logo

原生JS抛物线动画与动态模糊:从原理到实战

作者:半吊子全栈工匠2025.09.19 15:54浏览量:0

简介:本文深入解析如何使用原生JavaScript实现抛物线轨迹动画与动态模糊效果,包含数学公式推导、Canvas渲染优化及性能提升方案,提供完整可运行的代码示例。

原生JS实现抛物线动画与动态模糊效果

在Web动画开发中,抛物线轨迹与动态模糊是两个极具视觉表现力的技术点。原生JavaScript凭借其轻量级和高度可控的特性,成为实现这类特效的理想选择。本文将通过数学建模、Canvas渲染和性能优化三个维度,系统讲解如何构建流畅的抛物线动画并叠加动态模糊效果。

一、抛物线动画的数学基础

1.1 抛物线运动模型构建

抛物线运动本质是二维平面上的匀加速运动,其轨迹方程可表示为:

  1. x = v0x * t
  2. y = v0y * t - 0.5 * g * t²

其中v0x、v0y为初始速度分量,g为重力加速度(通常取9.8m/s²的像素等效值)。在实际开发中,我们需要将物理单位转换为屏幕像素:

  1. const gravity = 0.5; // 像素/帧²
  2. let time = 0;
  3. function calculatePosition(v0x, v0y) {
  4. const x = v0x * time;
  5. const y = v0y * time - 0.5 * gravity * time * time;
  6. time++;
  7. return {x, y};
  8. }

1.2 贝塞尔曲线优化方案

对于需要更复杂轨迹的场景,可采用二次贝塞尔曲线模拟抛物线:

  1. function getBezierPoint(t, p0, p1, p2) {
  2. const mt = 1 - t;
  3. return {
  4. x: mt * mt * p0.x + 2 * mt * t * p1.x + t * t * p2.x,
  5. y: mt * mt * p0.y + 2 * mt * t * p1.y + t * t * p2.y
  6. };
  7. }
  8. // 控制点设置技巧:将中点y坐标下移模拟重力效果

二、Canvas渲染体系构建

2.1 双缓冲技术实现

为避免动画闪烁,必须实现离屏渲染:

  1. const canvas = document.getElementById('mainCanvas');
  2. const ctx = canvas.getContext('2d');
  3. const bufferCanvas = document.createElement('canvas');
  4. bufferCanvas.width = canvas.width;
  5. bufferCanvas.height = canvas.height;
  6. const bufferCtx = bufferCanvas.getContext('2d');
  7. function render() {
  8. // 在bufferCanvas上绘制
  9. bufferCtx.clearRect(0, 0, bufferCanvas.width, bufferCanvas.height);
  10. drawParticle(bufferCtx, currentPos);
  11. // 一次性绘制到主canvas
  12. ctx.clearRect(0, 0, canvas.width, canvas.height);
  13. ctx.drawImage(bufferCanvas, 0, 0);
  14. }

2.2 粒子系统设计

构建可扩展的粒子类:

  1. class Particle {
  2. constructor(x, y, vx, vy) {
  3. this.x = x;
  4. this.y = y;
  5. this.vx = vx;
  6. this.vy = vy;
  7. this.life = 100;
  8. this.size = Math.random() * 5 + 2;
  9. }
  10. update() {
  11. this.x += this.vx;
  12. this.y += this.vy - 0.25; // 重力分量
  13. this.vy *= 0.99; // 空气阻力
  14. this.life--;
  15. return this.life > 0;
  16. }
  17. draw(ctx) {
  18. ctx.beginPath();
  19. ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
  20. ctx.fillStyle = `rgba(255, 100, 100, ${this.life/100})`;
  21. ctx.fill();
  22. }
  23. }

三、动态模糊实现方案

3.1 运动模糊算法

基于速度向量的模糊实现:

  1. function applyMotionBlur(ctx, particle, prevPos) {
  2. const blurRadius = Math.max(2, Math.abs(particle.vx) * 0.5);
  3. const gradient = ctx.createRadialGradient(
  4. particle.x, particle.y, 0,
  5. particle.x, particle.y, blurRadius
  6. );
  7. gradient.addColorStop(0, 'rgba(255,100,100,0.8)');
  8. gradient.addColorStop(1, 'rgba(255,100,100,0)');
  9. ctx.save();
  10. ctx.beginPath();
  11. ctx.arc(particle.x, particle.y, blurRadius, 0, Math.PI * 2);
  12. ctx.fillStyle = gradient;
  13. ctx.fill();
  14. ctx.restore();
  15. }

3.2 高斯模糊优化

对于静态元素的模糊效果,可采用分离滤波的高斯模糊:

  1. function gaussianBlur(ctx, radius = 5) {
  2. // 水平模糊
  3. const tempCanvas = document.createElement('canvas');
  4. const tempCtx = tempCanvas.getContext('2d');
  5. // ...实现水平模糊代码
  6. // 垂直模糊
  7. // ...实现垂直模糊代码
  8. ctx.drawImage(tempCanvas, 0, 0);
  9. }

四、性能优化策略

4.1 分层渲染技术

将静态背景与动态元素分离:

  1. // 背景层(低频更新)
  2. function renderBackground() {
  3. staticCtx.fillStyle = '#000';
  4. staticCtx.fillRect(0, 0, canvas.width, canvas.height);
  5. }
  6. // 动态层(高频更新)
  7. function renderDynamic() {
  8. dynamicCtx.clearRect(0, 0, canvas.width, canvas.height);
  9. particles.forEach(p => p.draw(dynamicCtx));
  10. }

4.2 请求动画帧优化

使用rAF的精确时序控制:

  1. let lastTime = 0;
  2. function animate(timestamp) {
  3. const deltaTime = timestamp - lastTime;
  4. if (deltaTime > 16) { // 限制帧率
  5. updateParticles();
  6. render();
  7. lastTime = timestamp;
  8. }
  9. requestAnimationFrame(animate);
  10. }

五、完整实现示例

  1. <canvas id="canvas" width="800" height="600"></canvas>
  2. <script>
  3. const canvas = document.getElementById('canvas');
  4. const ctx = canvas.getContext('2d');
  5. class Particle {
  6. constructor(x, y) {
  7. this.x = x;
  8. this.y = y;
  9. this.vx = (Math.random() - 0.5) * 8;
  10. this.vy = Math.random() * -15;
  11. this.life = 100;
  12. this.trail = [];
  13. }
  14. update() {
  15. this.trail.push({x: this.x, y: this.y});
  16. if (this.trail.length > 10) this.trail.shift();
  17. this.x += this.vx;
  18. this.y += this.vy + 0.25;
  19. this.vy += 0.25; // 重力
  20. this.life--;
  21. return this.life > 0;
  22. }
  23. draw(ctx) {
  24. // 绘制轨迹
  25. ctx.beginPath();
  26. ctx.moveTo(this.trail[0].x, this.trail[0].y);
  27. for (let i = 1; i < this.trail.length; i++) {
  28. ctx.lineTo(this.trail[i].x, this.trail[i].y);
  29. }
  30. ctx.strokeStyle = `rgba(255,100,100,${this.life/200})`;
  31. ctx.lineWidth = 2;
  32. ctx.stroke();
  33. // 绘制主体
  34. ctx.beginPath();
  35. ctx.arc(this.x, this.y, 5, 0, Math.PI * 2);
  36. ctx.fillStyle = `rgba(255,200,100,${this.life/100})`;
  37. ctx.fill();
  38. }
  39. }
  40. let particles = [];
  41. function createParticle(x, y) {
  42. particles.push(new Particle(x, y));
  43. }
  44. function update() {
  45. particles = particles.filter(p => p.update());
  46. }
  47. function render() {
  48. ctx.clearRect(0, 0, canvas.width, canvas.height);
  49. particles.forEach(p => p.draw(ctx));
  50. }
  51. function animate() {
  52. update();
  53. render();
  54. requestAnimationFrame(animate);
  55. }
  56. canvas.addEventListener('click', (e) => {
  57. const rect = canvas.getBoundingClientRect();
  58. createParticle(e.clientX - rect.left, e.clientY - rect.top);
  59. });
  60. animate();
  61. </script>

六、进阶优化方向

  1. Web Workers:将粒子物理计算移至工作线程
  2. WebGL加速:使用着色器实现批量粒子渲染
  3. 空间分区:对大规模粒子系统进行四叉树管理
  4. 预计算表存储常用物理计算结果

通过上述技术组合,开发者可以在不依赖任何第三方库的情况下,实现高性能的抛物线动画与动态模糊效果。这种原生实现方式不仅具有最佳的控制灵活性,还能显著减少资源占用,特别适合对性能要求严苛的移动端Web应用。

相关文章推荐

发表评论