发布日期:2023-11-20
阅读时间:15分钟
一、Canvas动画技术概述
HTML5 Canvas为现代Web应用提供了强大的图形绘制能力,特别适合实现复杂的动画效果。与CSS动画不同,Canvas动画通过JavaScript直接控制像素,可以实现更精细的物理效果和交互体验。
Canvas基础设置
<!DOCTYPE html>
<html>
<head>
<title>Canvas基础模板</title>
</head>
<body>
<canvas id="particleCanvas" width="800" height="600">
您的浏览器不支持Canvas
</canvas>
<script>
// 获取Canvas上下文
const canvas = document.getElementById('particleCanvas');
const ctx = canvas.getContext('2d');
// 基础绘制示例
function drawBasicShapes() {
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制圆形粒子
ctx.beginPath();
ctx.arc(100, 100, 10, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255, 0, 0, 0.8)';
ctx.fill();
// 绘制渐变效果
const gradient = ctx.createRadialGradient(200, 100, 5, 200, 100, 20);
gradient.addColorStop(0, 'rgba(0, 255, 255, 1)');
gradient.addColorStop(1, 'rgba(0, 255, 255, 0)');
ctx.beginPath();
ctx.arc(200, 100, 20, 0, Math.PI * 2);
ctx.fillStyle = gradient;
ctx.fill();
}
drawBasicShapes();
</script>
</body>
</html>
二、粒子系统架构设计
粒子系统是复杂动画的基础,通过管理大量简单的图形单元来创建逼真的自然现象效果。
1. 粒子类设计
class Particle {
constructor(x, y, radius = 2) {
this.x = x;
this.y = y;
this.radius = radius;
this.vx = (Math.random() - 0.5) * 4; // 水平速度
this.vy = (Math.random() - 0.5) * 4; // 垂直速度
this.alpha = 1; // 透明度
this.decay = 0.01 + Math.random() * 0.02; // 衰减速率
this.color = this.generateColor();
}
generateColor() {
const colors = [
'#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7',
'#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E9'
];
return colors[Math.floor(Math.random() * colors.length)];
}
update() {
// 物理更新
this.x += this.vx;
this.y += this.vy;
this.alpha -= this.decay;
// 边界检测
if (this.x canvas.width) this.vx *= -0.8;
if (this.y canvas.height) this.vy *= -0.8;
// 限制在画布内
this.x = Math.max(0, Math.min(canvas.width, this.x));
this.y = Math.max(0, Math.min(canvas.height, this.y));
}
draw(ctx) {
ctx.save();
ctx.globalAlpha = this.alpha;
// 创建发光效果
const gradient = ctx.createRadialGradient(
this.x, this.y, 0,
this.x, this.y, this.radius * 2
);
gradient.addColorStop(0, this.color);
gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius * 2, 0, Math.PI * 2);
ctx.fillStyle = gradient;
ctx.fill();
ctx.restore();
}
isDead() {
return this.alpha <= 0;
}
}
2. 粒子系统管理器
class ParticleSystem {
constructor() {
this.particles = [];
this.maxParticles = 1000;
}
addParticle(x, y, count = 1) {
for (let i = 0; i < count; i++) {
if (this.particles.length = 0; i--) {
this.particles[i].update();
// 移除死亡的粒子
if (this.particles[i].isDead()) {
this.particles.splice(i, 1);
}
}
}
draw(ctx) {
this.particles.forEach(particle => {
particle.draw(ctx);
});
}
clear() {
this.particles = [];
}
getParticleCount() {
return this.particles.length;
}
}
三、物理引擎核心实现
为粒子系统添加真实的物理效果,包括重力、碰撞、吸引力等。
1. 物理常量与工具函数
class PhysicsEngine {
constructor() {
this.gravity = 0.1;
this.friction = 0.99;
this.springs = [];
this.attractors = [];
}
// 重力应用
applyGravity(particle) {
particle.vy += this.gravity;
}
// 摩擦力应用
applyFriction(particle) {
particle.vx *= this.friction;
particle.vy *= this.friction;
}
// 粒子间碰撞检测
checkCollisions(particles) {
for (let i = 0; i < particles.length; i++) {
for (let j = i + 1; j < particles.length; j++) {
const p1 = particles[i];
const p2 = particles[j];
const dx = p2.x - p1.x;
const dy = p2.y - p1.y;
const distance = Math.sqrt(dx * dx + dy * dy);
// 碰撞距离阈值
const minDistance = p1.radius + p2.radius;
if (distance {
const dx = attractor.x - particle.x;
const dy = attractor.y - particle.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
const force = attractor.strength / (distance * distance);
particle.vx += (dx / distance) * force;
particle.vy += (dy / distance) * force;
}
});
}
}
2. 高级物理效果:弹簧系统
class SpringSystem {
constructor() {
this.springs = [];
this.springStrength = 0.1;
this.damping = 0.9;
}
// 创建弹簧连接
createSpring(particle1, particle2, length = 50) {
this.springs.push({
p1: particle1,
p2: particle2,
length: length,
strength: this.springStrength
});
}
// 更新弹簧物理
updateSprings() {
this.springs.forEach(spring => {
const dx = spring.p2.x - spring.p1.x;
const dy = spring.p2.y - spring.p1.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
const displacement = distance - spring.length;
const force = displacement * spring.strength;
// 应用弹簧力
const fx = (dx / distance) * force;
const fy = (dy / distance) * force;
spring.p1.vx += fx;
spring.p1.vy += fy;
spring.p2.vx -= fx;
spring.p2.vy -= fy;
// 阻尼效果
spring.p1.vx *= this.damping;
spring.p1.vy *= this.damping;
spring.p2.vx *= this.damping;
spring.p2.vy *= this.damping;
}
});
}
}
四、性能优化策略
处理大量粒子时的性能优化技巧。
1. 空间分区优化
class SpatialHash {
constructor(cellSize = 50) {
this.cellSize = cellSize;
this.grid = new Map();
}
getCellKey(x, y) {
return `${Math.floor(x / this.cellSize)},${Math.floor(y / this.cellSize)}`;
}
update(particles) {
this.grid.clear();
particles.forEach((particle, index) => {
const key = this.getCellKey(particle.x, particle.y);
if (!this.grid.has(key)) {
this.grid.set(key, []);
}
this.grid.get(key).push(particle);
});
}
getNeighbors(particle, radius = 100) {
const neighbors = [];
const centerX = Math.floor(particle.x / this.cellSize);
const centerY = Math.floor(particle.y / this.cellSize);
// 检查周围9个网格
for (let x = centerX - 1; x <= centerX + 1; x++) {
for (let y = centerY - 1; y {
if (p !== particle) {
const dx = p.x - particle.x;
const dy = p.y - particle.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < radius) {
neighbors.push(p);
}
}
});
}
}
}
return neighbors;
}
}
2. 渲染优化技巧
class OptimizedRenderer {
constructor(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.useOffscreenCanvas = this.checkOffscreenSupport();
if (this.useOffscreenCanvas) {
this.offscreenCanvas = document.createElement('canvas');
this.offscreenCanvas.width = canvas.width;
this.offscreenCanvas.height = canvas.height;
this.offscreenCtx = this.offscreenCanvas.getContext('2d');
}
}
checkOffscreenSupport() {
return typeof OffscreenCanvas !== 'undefined';
}
// 批量渲染优化
renderParticles(particles) {
if (this.useOffscreenCanvas) {
this.offscreenCtx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// 批量绘制粒子
particles.forEach(particle => {
this.drawParticleToOffscreen(particle);
});
// 一次性绘制到主画布
this.ctx.drawImage(this.offscreenCanvas, 0, 0);
} else {
// 传统渲染方式
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
particles.forEach(particle => {
particle.draw(this.ctx);
});
}
}
drawParticleToOffscreen(particle) {
const ctx = this.offscreenCtx;
ctx.save();
ctx.globalAlpha = particle.alpha;
const gradient = ctx.createRadialGradient(
particle.x, particle.y, 0,
particle.x, particle.y, particle.radius * 2
);
gradient.addColorStop(0, particle.color);
gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
ctx.beginPath();
ctx.arc(particle.x, particle.y, particle.radius * 2, 0, Math.PI * 2);
ctx.fillStyle = gradient;
ctx.fill();
ctx.restore();
}
}
五、实战案例:烟花模拟系统
综合运用以上技术构建完整的烟花模拟效果。
完整实现代码
<!DOCTYPE html>
<html>
<head>
<title>Canvas烟花模拟器</title>
</head>
<body>
<canvas id="fireworksCanvas" width="1200" height="800"></canvas>
<script>
class FireworksSimulator {
constructor(canvasId) {
this.canvas = document.getElementById(canvasId);
this.ctx = this.canvas.getContext('2d');
this.particleSystem = new ParticleSystem();
this.physicsEngine = new PhysicsEngine();
this.renderer = new OptimizedRenderer(this.canvas);
this.isRunning = false;
this.lastTime = 0;
this.setupEventListeners();
this.setupInitialAttractors();
}
setupEventListeners() {
this.canvas.addEventListener('click', (e) => {
this.launchFirework(e.offsetX, e.offsetY);
});
this.canvas.addEventListener('mousemove', (e) => {
this.updateMouseAttractor(e.offsetX, e.offsetY);
});
}
setupInitialAttractors() {
// 添加屏幕边缘引力
this.physicsEngine.addAttractor(0, 0, 1000);
this.physicsEngine.addAttractor(this.canvas.width, 0, 1000);
this.physicsEngine.addAttractor(0, this.canvas.height, 1000);
this.physicsEngine.addAttractor(this.canvas.width, this.canvas.height, 1000);
}
updateMouseAttractor(x, y) {
// 更新鼠标引力点
if (this.physicsEngine.attractors.length > 4) {
this.physicsEngine.attractors[4] = { x, y, strength: 500 };
} else {
this.physicsEngine.addAttractor(x, y, 500);
}
}
launchFirework(x, y) {
// 创建烟花爆炸效果
const particleCount = 100 + Math.random() * 100;
for (let i = 0; i {
this.physicsEngine.applyGravity(particle);
this.physicsEngine.applyFriction(particle);
this.physicsEngine.applyAttraction(particle);
});
this.physicsEngine.checkCollisions(this.particleSystem.particles);
this.particleSystem.update();
// 渲染
this.renderer.renderParticles(this.particleSystem.particles);
// 显示粒子数量
this.drawParticleCount();
if (this.isRunning) {
requestAnimationFrame((ts) => this.update(ts));
}
}
drawParticleCount() {
this.ctx.fillStyle = 'white';
this.ctx.font = '16px Arial';
this.ctx.fillText(`粒子数量: ${this.particleSystem.getParticleCount()}`, 10, 20);
}
start() {
this.isRunning = true;
requestAnimationFrame((ts) => this.update(ts));
}
stop() {
this.isRunning = false;
}
}
// 初始化并启动模拟器
const simulator = new FireworksSimulator('fireworksCanvas');
simulator.start();
// 自动发射烟花
setInterval(() => {
const x = 100 + Math.random() * (simulator.canvas.width - 200);
const y = 100 + Math.random() * (simulator.canvas.height - 200);
simulator.launchFirework(x, y);
}, 2000);
</script>
</body>
</html>
效果特性
- 鼠标交互:点击发射烟花,鼠标移动产生引力
- 物理效果:重力、摩擦力、粒子碰撞、引力场
- 视觉特效:粒子发光、透明度衰减、颜色渐变
- 性能优化:支持数千粒子流畅运行