独立渲染引擎就绪引擎就绪
看起来之前的动画没有成功生成,我来为你创建一个视觉冲击力强的自动播放动画演示:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>星际潮汐 · Cosmic Tide</title>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Noto+Sans+SC:wght@100;300;400&display=swap" rel="stylesheet">
<style>
:root {
--bg: #04060f;
--fg: #e8ecf5;
--accent: #00e5c3;
--accent2: #ff6b35;
--muted: rgba(255,255,255,0.12);
--glow: rgba(0,229,195,0.35);
}
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: var(--bg);
color: var(--fg);
font-family: 'Noto Sans SC', sans-serif;
overflow: hidden;
width: 100vw;
height: 100vh;
cursor: none;
}
canvas#cosmic {
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
z-index: 1;
}
/* 自定义光标 */
.cursor-dot {
position: fixed;
width: 8px; height: 8px;
background: var(--accent);
border-radius: 50%;
pointer-events: none;
z-index: 1000;
transform: translate(-50%, -50%);
box-shadow: 0 0 20px var(--glow), 0 0 60px var(--glow);
transition: width 0.2s, height 0.2s;
}
.cursor-ring {
position: fixed;
width: 40px; height: 40px;
border: 1.5px solid rgba(0,229,195,0.4);
border-radius: 50%;
pointer-events: none;
z-index: 1000;
transform: translate(-50%, -50%);
transition: transform 0.15s ease-out, width 0.3s, height 0.3s, border-color 0.3s;
}
/* 覆盖层文字 */
.overlay {
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
z-index: 10;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
pointer-events: none;
}
.title {
font-family: 'Orbitron', sans-serif;
font-weight: 900;
font-size: clamp(3rem, 8vw, 7rem);
letter-spacing: 0.15em;
color: transparent;
background: linear-gradient(135deg, var(--accent) 0%, #ffffff 50%, var(--accent2) 100%);
background-clip: text;
-webkit-background-clip: text;
opacity: 0;
animation: titleReveal 2s ease-out 0.5s forwards;
text-shadow: none;
position: relative;
}
.subtitle {
font-weight: 100;
font-size: clamp(0.85rem, 2vw, 1.3rem);
letter-spacing: 0.6em;
color: rgba(255,255,255,0.5);
margin-top: 1.5rem;
opacity: 0;
animation: subtitleReveal 1.8s ease-out 1.5s forwards;
}
.line-accent {
width: 0;
height: 1px;
background: linear-gradient(90deg, transparent, var(--accent), transparent);
margin-top: 2rem;
animation: lineExpand 2s ease-out 2s forwards;
}
.corner-mark {
position: fixed;
font-family: 'Orbitron', sans-serif;
font-size: 0.65rem;
letter-spacing: 0.3em;
color: rgba(255,255,255,0.15);
z-index: 10;
}
.corner-mark.tl { top: 2rem; left: 2rem; }
.corner-mark.br { bottom: 2rem; right: 2rem; }
/* 底部粒子计数 */
.stats {
position: fixed;
bottom: 2rem;
left: 2rem;
font-family: 'Orbitron', sans-serif;
font-size: 0.6rem;
letter-spacing: 0.2em;
color: rgba(0,229,195,0.3);
z-index: 10;
}
/* 右侧装饰线 */
.deco-line {
position: fixed;
right: 3rem;
top: 50%;
transform: translateY(-50%);
width: 1px;
height: 0;
background: linear-gradient(180deg, transparent, var(--accent), transparent);
z-index: 10;
opacity: 0.3;
animation: decoLineGrow 2.5s ease-out 2.5s forwards;
}
@keyframes titleReveal {
0% { opacity: 0; transform: translateY(30px) scale(0.95); filter: blur(10px); }
100% { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
}
@keyframes subtitleReveal {
0% { opacity: 0; transform: translateY(15px); }
100% { opacity: 1; transform: translateY(0); }
}
@keyframes lineExpand {
0% { width: 0; }
100% { width: min(400px, 60vw); }
}
@keyframes decoLineGrow {
0% { height: 0; }
100% { height: 30vh; }
}
/* 减弱动画偏好 */
@media (prefers-reduced-motion: reduce) {
.title, .subtitle, .line-accent, .deco-line {
animation-duration: 0.01s !important;
}
}
</style>
</head>
<body>
<canvas id="cosmic"></canvas>
<div class="cursor-dot" id="cursorDot"></div>
<div class="cursor-ring" id="cursorRing"></div>
<div class="overlay">
<div class="title">COSMIC TIDE</div>
<div class="subtitle">星 际 潮 汐</div>
<div class="line-accent"></div>
</div>
<div class="corner-mark tl">SECTOR 7G — DEEP FIELD</div>
<div class="corner-mark br">ANOMALY DETECTED</div>
<div class="deco-line"></div>
<div class="stats" id="stats">PARTICLES: 0</div>
<script>
/**
* 星际潮汐 - 粒子流体动画
* 多层粒子系统 + 几何晶体 + 鼠标交互
*/
const canvas = document.getElementById('cosmic');
const ctx = canvas.getContext('2d');
const cursorDot = document.getElementById('cursorDot');
const cursorRing = document.getElementById('cursorRing');
const statsEl = document.getElementById('stats');
let W, H, cx, cy;
let mouse = { x: -1000, y: -1000, vx: 0, vy: 0 };
let prevMouse = { x: -1000, y: -1000 };
let time = 0;
// 画布尺寸
function resize() {
W = canvas.width = window.innerWidth;
H = canvas.height = window.innerHeight;
cx = W / 2;
cy = H / 2;
}
resize();
window.addEventListener('resize', resize);
// 鼠标追踪
document.addEventListener('mousemove', (e) => {
mouse.vx = e.clientX - mouse.x;
mouse.vy = e.clientY - mouse.y;
mouse.x = e.clientX;
mouse.y = e.clientY;
cursorDot.style.left = e.clientX + 'px';
cursorDot.style.top = e.clientY + 'px';
cursorRing.style.left = e.clientX + 'px';
cursorRing.style.top = e.clientY + 'px';
});
// ====== 噪声函数 ======
function simplexLike(x, y) {
const sin1 = Math.sin(x * 0.8 + y * 1.3) * 0.5;
const sin2 = Math.sin(x * 1.7 - y * 0.9) * 0.3;
const sin3 = Math.sin(x * 0.3 + y * 2.1) * 0.2;
return sin1 + sin2 + sin3;
}
// ====== 粒子系统 ======
class Particle {
constructor(layer) {
this.layer = layer; // 0=远景, 1=中景, 2=近景
this.reset();
}
reset() {
this.x = Math.random() * W;
this.y = Math.random() * H;
const layerConfig = [
{ sizeMin: 0.5, sizeMax: 1.5, speedMax: 0.3, alpha: 0.3 },
{ sizeMin: 1, sizeMax: 3, speedMax: 0.6, alpha: 0.5 },
{ sizeMin: 2, sizeMax: 5, speedMax: 1.0, alpha: 0.8 }
][this.layer];
this.size = layerConfig.sizeMin + Math.random() * (layerConfig.sizeMax - layerConfig.sizeMin);
this.baseAlpha = layerConfig.alpha * (0.5 + Math.random() * 0.5);
this.alpha = this.baseAlpha;
this.vx = (Math.random() - 0.5) * layerConfig.speedMax;
this.vy = (Math.random() - 0.5) * layerConfig.speedMax;
this.life = 0.5 + Math.random() * 0.5;
this.phase = Math.random() * Math.PI * 2;
// 颜色:青色系为主,少量橙色点缀
if (Math.random() < 0.85) {
const hue = 160 + Math.random() * 30; // 青绿色
this.color = `hsla(${hue}, 90%, 70%, `;
} else {
const hue = 15 + Math.random() * 20; // 暖橙色
this.color = `hsla(${hue}, 95%, 60%, `;
}
}
update(dt) {
// 噪声场驱动
const noiseScale = 0.002;
const n = simplexLike(
this.x * noiseScale + time * 0.2,
this.y * noiseScale + time * 0.15
);
const angle = n * Math.PI * 4;
const fieldStrength = [0.15, 0.3, 0.5][this.layer];
this.vx += Math.cos(angle) * fieldStrength * dt;
this.vy += Math.sin(angle) * fieldStrength * dt;
// 鼠标吸引
const dx = mouse.x - this.x;
const dy = mouse.y - this.y;
const dist = Math.sqrt(dx * dx + dy * dy);
const influence = [80, 150, 250][this.layer];
if (dist < influence && dist > 1) {
const force = (1 - dist / influence) * 0.8;
this.vx += (dx / dist) * force * dt * 60;
this.vy += (dy / dist) * force * dt * 60;
}
// 阻尼
const damping = 0.97;
this.vx *= damping;
this.vy *= damping;
this.x += this.vx;
this.y += this.vy;
// 脉动
this.alpha = this.baseAlpha * (0.6 + 0.4 * Math.sin(time * 2 + this.phase));
// 边界循环
if (this.x < -20) this.x = W + 20;
if (this.x > W + 20) this.x = -20;
if (this.y < -20) this.y = H + 20;
if (this.y > H + 20) this.y = -20;
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, Math.max(0.5, this.size), 0, Math.PI * 2);
ctx.fillStyle = this.color + this.alpha + ')';
ctx.fill();
// 近景粒子发光
if (this.layer === 2 && this.size > 3) {
ctx.beginPath();
ctx.arc(this.x, this.y, this.size * 3, 0, Math.PI * 2);
ctx.fillStyle = this.color + (this.alpha * 0.15) + ')';
ctx.fill();
}
}
}
// ====== 几何晶体 ======
class Crystal {
constructor() {
this.x = W * (0.2 + Math.random() * 0.6);
this.y = H * (0.2 + Math.random() * 0.6);
this.sides = 3 + Math.floor(Math.random() * 4); // 3-6边
this.radius = 30 + Math.random() * 80;
this.rotation = Math.random() * Math.PI * 2;
this.rotSpeed = (Math.random() - 0.5) * 0.3;
this.alpha = 0;
this.targetAlpha = 0.08 + Math.random() * 0.12;
this.delay = Math.random() * 3;
this.born = false;
}
update(dt) {
if (time < this.delay) return;
this.born = true;
this.alpha += (this.targetAlpha - this.alpha) * 0.02;
this.rotation += this.rotSpeed * dt;
// 鼠标靠近时增亮
const dx = mouse.x - this.x;
const dy = mouse.y - this.y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 200) {
this.targetAlpha = 0.08 + (1 - dist / 200) * 0.25;
} else {
this.targetAlpha = 0.08 + Math.random() * 0.04;
}
}
draw() {
if (!this.born) return;
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.rotation);
// 外框
ctx.beginPath();
for (let i = 0; i < this.sides; i++) {
const a = (Math.PI * 2 / this.sides) * i - Math.PI / 2;
const px = Math.cos(a) * this.radius;
const py = Math.sin(a) * this.radius;
if (i === 0) ctx.moveTo(px, py);
else ctx.lineTo(px, py);
}
ctx.closePath();
ctx.strokeStyle = `rgba(0, 229, 195, ${this.alpha})`;
ctx.lineWidth = 1;
ctx.stroke();
// 内框
ctx.beginPath();
for (let i = 0; i < this.sides; i++) {
const a = (Math.PI * 2 / this.sides) * i - Math.PI / 2 + Math.PI / this.sides;
const px = Math.cos(a) * this.radius * 0.5;
const py = Math.sin(a) * this.radius * 0.5;
if (i === 0) ctx.moveTo(px, py);
else ctx.lineTo(px, py);
}
ctx.closePath();
ctx.strokeStyle = `rgba(255, 107, 53, ${this.alpha * 0.6})`;
ctx.lineWidth = 0.5;
ctx.stroke();
// 连接线
for (let i = 0; i < this.sides; i++) {
const a1 = (Math.PI * 2 / this.sides) * i - Math.PI / 2;
const a2 = (Math.PI * 2 / this.sides) * i - Math.PI / 2 + Math.PI / this.sides;
ctx.beginPath();
ctx.moveTo(Math.cos(a1) * this.radius, Math.sin(a1) * this.radius);
ctx.lineTo(Math.cos(a2) * this.radius * 0.5, Math.sin(a2) * this.radius * 0.5);
ctx.strokeStyle = `rgba(0, 229, 195, ${this.alpha * 0.3})`;
ctx.stroke();
}
ctx.restore();
}
}
// ====== 流星 ======
class Meteor {
constructor() {
this.reset();
}
reset() {
this.active = false;
this.x = Math.random() * W;
this.y = -20;
this.length = 60 + Math.random() * 120;
this.speed = 8 + Math.random() * 12;
this.angle = Math.PI / 4 + (Math.random() - 0.5) * 0.3;
this.alpha = 0;
this.life = 0;
this.maxLife = 40 + Math.random() * 30;
}
update() {
if (!this.active) {
// 随机触发
if (Math.random() < 0.003) {
this.active = true;
this.x = Math.random() * W * 1.2 - W * 0.1;
this.y = -20;
this.life = 0;
this.alpha = 0;
}
return;
}
this.life++;
this.x += Math.cos(this.angle) * this.speed;
this.y += Math.sin(this.angle) * this.speed;
// 淡入淡出
const progress = this.life / this.maxLife;
if (progress < 0.2) this.alpha = progress / 0.2;
else if (progress > 0.7) this.alpha = (1 - progress) / 0.3;
else this.alpha = 1;
if (this.life > this.maxLife || this.y > H + 50) {
this.reset();
}
}
draw() {
if (!this.active) return;
const tailX = this.x - Math.cos(this.angle) * this.length;
const tailY = this.y - Math.sin(this.angle) * this.length;
const grad = ctx.createLinearGradient(tailX, tailY, this.x, this.y);
grad.addColorStop(0, `rgba(255, 107, 53, 0)`);
grad.addColorStop(0.6, `rgba(255, 107, 53, ${this.alpha * 0.4})`);
grad.addColorStop(1, `rgba(255, 255, 255, ${this.alpha * 0.9})`);
ctx.beginPath();
ctx.moveTo(tailX, tailY);
ctx.lineTo(this.x, this.y);
ctx.strokeStyle = grad;
ctx.lineWidth = 1.5;
ctx.stroke();
// 头部光点
ctx.beginPath();
ctx.arc(this.x, this.y, 2, 0, Math.PI * 2);
ctx.fillStyle = `rgba(255, 255, 255, ${this.alpha})`;
ctx.fill();
}
}
// ====== 粒子间连线 ======
function drawConnections(particles) {
const maxDist = 100;
// 只连接近景粒子
const nearParticles = particles.filter(p => p.layer === 2);
for (let i = 0; i < nearParticles.length; i++) {
for (let j = i + 1; j < nearParticles.length; j++) {
const dx = nearParticles[i].x - nearParticles[j].x;
const dy = nearParticles[i].y - nearParticles[j].y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < maxDist) {
const alpha = (1 - dist / maxDist) * 0.15;
ctx.beginPath();
ctx.moveTo(nearParticles[i].x, nearParticles[i].y);
ctx.lineTo(nearParticles[j].x, nearParticles[j].y);
ctx.strokeStyle = `rgba(0, 229, 195, ${alpha})`;
ctx.lineWidth = 0.5;
ctx.stroke();
}
}
}
}
// ====== 背景雾气 ======
function drawNebula() {
// 径向光晕 - 跟随鼠标轻微偏移
const mx = cx + (mouse.x - cx) * 0.1;
const my = cy + (mouse.y - cy) * 0.1;
const grad1 = ctx.createRadialGradient(mx, my, 0, mx, my, Math.max(W, H) * 0.6);
grad1.addColorStop(0, 'rgba(0, 229, 195, 0.03)');
grad1.addColorStop(0.4, 'rgba(0, 100, 120, 0.02)');
grad1.addColorStop(1, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = grad1;
ctx.fillRect(0, 0, W, H);
// 第二个光晕 - 暖色
const ox = W * 0.75 + Math.sin(time * 0.5) * 50;
const oy = H * 0.3 + Math.cos(time * 0.3) * 30;
const grad2 = ctx.createRadialGradient(ox, oy, 0, ox, oy, 300);
grad2.addColorStop(0, 'rgba(255, 107, 53, 0.025)');
grad2.addColorStop(1, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = grad2;
ctx.fillRect(0, 0, W, H);
}
// ====== 鼠标光圈 ======
function drawMouseGlow() {
if (mouse.x < 0) return;
const grad = ctx.createRadialGradient(mouse.x, mouse.y, 0, mouse.x, mouse.y, 150);
grad.addColorStop(0, 'rgba(0, 229, 195, 0.06)');
grad.addColorStop(0.5, 'rgba(0, 229, 195, 0.02)');
grad.addColorStop(1, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = grad;
ctx.fillRect(mouse.x - 150, mouse.y - 150, 300, 300);
}
// ====== 初始化 ======
const particles = [];
const particleCounts = [200, 120, 50]; // 远、中、近
for (let layer = 0; layer < 3; layer++) {
for (let i = 0; i < particleCounts[layer]; i++) {
particles.push(new Particle(layer));
}
}
const crystals = [];
for (let i = 0; i < 5; i++) {
crystals.push(new Crystal());
}
const meteors = [];
for (let i = 0; i < 3; i++) {
meteors.push(new Meteor());
}
// ====== 扫描线效果 ======
let scanY = -10;
function drawScanLine() {
scanY += 1.5;
if (scanY > H + 10) scanY = -10;
const grad = ctx.createLinearGradient(0, scanY - 5, 0, scanY + 5);
grad.addColorStop(0, 'rgba(0, 229, 195, 0)');
grad.addColorStop(0.5, 'rgba(0, 229, 195, 0.04)');
grad.addColorStop(1, 'rgba(0, 229, 195, 0)');
ctx.fillStyle = grad;
ctx.fillRect(0, scanY - 5, W, 10);
}
// ====== 主循环 ======
let lastTime = performance.now();
function animate(now) {
const dt = Math.min((now - lastTime) / 1000, 0.05);
lastTime = now;
time += dt;
// 清屏 - 半透明实现拖尾
ctx.fillStyle = 'rgba(4, 6, 15, 0.15)';
ctx.fillRect(0, 0, W, H);
// 每隔一段完全清屏避免残影堆积
if (Math.floor(time * 10) % 50 === 0) {
ctx.fillStyle = 'rgba(4, 6, 15, 0.5)';
ctx.fillRect(0, 0, W, H);
}
// 背景雾气
drawNebula();
// 扫描线
drawScanLine();
// 更新和绘制晶体
crystals.forEach(c => { c.update(dt); c.draw(); });
// 更新和绘制粒子
particles.forEach(p => { p.update(dt); p.draw(); });
// 粒子连线
drawConnections(particles);
// 流星
meteors.forEach(m => { m.update(); m.draw(); });
// 鼠标光圈
drawMouseGlow();
// 更新统计
statsEl.textContent = `PARTICLES: ${particles.length} | T: ${time.toFixed(1)}s`;
requestAnimationFrame(animate);
}
// 启动
requestAnimationFrame(animate);
</script>
</body>
</html>
实现说明:
这是一个名为"星际潮汐"的全屏粒子流体动画,页面加载后立即自动播放,包含以下视觉层次:
- 三层粒子系统 — 远景(200个微小星尘)、中景(120个中等粒子)、近景(50个大号发光粒子),由噪声场驱动形成流体运动
- 几何晶体 — 5个缓慢旋转的多边形线框结构,鼠标靠近时会增亮
- 流星划过 — 随机触发的流星带有渐变尾迹
- 鼠标交互 — 自定义光标、粒子被鼠标吸引、光晕跟随、晶体响应
- 扫描线 + 星云雾气 — 缓慢扫过屏幕的半透明光线,加上随鼠标微动的径向光晕
- 近景粒子连线 — 距离较近的粒子之间绘制半透明连接线
配色采用深空黑底 + 电光青(#00e5c3)主调 + 暖橙(#ff6b35)点缀,字体使用 Orbitron 营造科技感。
你说得对,我之前没有了解你的实际需求就生成了一个通用的粒子动画,这确实不合适。
请告诉我:
- 你的项目是什么? — 比如是数据可视化、产品展示、品牌官网、仪表盘、某个具体应用的界面,还是其他?
- 你需要的动画具体表现什么? — 比如数据流动、物理模拟、图表动效、页面转场、某个概念的视觉隐喻?
- 有没有参考风格或氛围? — 比如极简、工业风、赛博朋克、自然有机、日式、杂志排版等?
有了这些信息,我才能做出真正贴合你项目的动画,而不是一个脱离上下文的演示。
积分规则:第一轮对话扣减8分,后续每轮扣6分
等待动画代码生成...
