马黑黑 发表于 2024-5-4 18:35

canvas拖尾效果演示

本帖最后由 马黑黑 于 2024-5-4 18:40 编辑 <br /><br /><style>
        #papa { margin: 20px auto; width: 760px; height: 560px; background: linear-gradient(lightblue, tan); }
        #canv { display: block; position: absolute; background: #000; mix-blend-mode: screen; }
</style>

<div id="papa"><canvas id="canv"></canvas></div>

<script>

var ctx = canv.getContext('2d');
let total = 50, r = 2, particles = [];
let ww = canv.width = papa.offsetWidth, hh = canv.height = papa.offsetHeight;

for (var i = 0; i < total; i ++) {
        let x = Math.random() * ww,
                y = Math.random() * hh,
                v = Math.random() * 0.5 + 0.5,
                color = 'rgba(255,255,255,.5)';
        particles.push({x: x, y: y, r: r, v: v, color: color});
}

var draw = (x,y,r,color) => {
                ctx.save();
                ctx.fillStyle = color;
                ctx.beginPath();
                ctx.arc(x, y, r, 0, 2 * Math.PI);
                ctx.fill();
                ctx.restore();
};

var move = () => {
        ctx.fillStyle = 'rgba(0, 0, 0, .1)';
        ctx.fillRect(0, 0, ww, hh);
        particles.forEach(p => {
                draw(p.x, p.y, p.r, p.color);
                p.x += p.v;
                p.y += p.v;
                if (p.x > ww) p.x = 0;
                if (p.y > ww) p.y = 0;
        });
        requestAnimationFrame(move);
};

move();

</script>

马黑黑 发表于 2024-5-4 18:35

本帖最后由 马黑黑 于 2024-5-4 18:39 编辑

代码
<style>
      #papa { margin: 20px auto; width: 760px; height: 560px; background: linear-gradient(lightblue, tan); }
      #canv { display: block; position: absolute; background: #000; mix-blend-mode: screen; }
</style>

<div id="papa"><canvas id="canv"></canvas></div>

<script>

var ctx = canv.getContext('2d');
let total = 50, r = 2, particles = [];
let ww = canv.width = papa.offsetWidth, hh = canv.height = papa.offsetHeight;

for (var i = 0; i < total; i ++) {
      let x = Math.random() * ww,
                y = Math.random() * hh,
                v = Math.random() * 0.5 + 0.5,
                color = 'rgba(255,255,255,.5)';
      particles.push({x: x, y: y, r: r, v: v, color: color});
}

var draw = (x,y,r,color) => {
                ctx.save();
                ctx.fillStyle = color;
                ctx.beginPath();
                ctx.arc(x, y, r, 0, 2 * Math.PI);
                ctx.fill();
                ctx.restore();
};

var move = () => {
      ctx.fillStyle = 'rgba(0, 0, 0, .1)';
      ctx.fillRect(0, 0, ww, hh);
      particles.forEach(p => {
                draw(p.x, p.y, p.r, p.color);
                p.x += p.v;
                p.y += p.v;
                if (p.x > ww) p.x = 0;
                if (p.y > ww) p.y = 0;
      });
      requestAnimationFrame(move);
};

move();

</script>

马黑黑 发表于 2024-5-4 18:39

本帖最后由 马黑黑 于 2024-5-4 20:45 编辑

代码说明:

第11行:三个全局变量,total 粒子总数,r 粒子半径,particles 存储粒子对象数组——

    let total = 50, r = 2, particles = [];

第14~20行:通过 for循环创建粒子对象数组,对象用 {} 表示,里面是四个键值对,xyr 记录圆形粒子的圆心坐标和半径,v 记录行进速度,color 记录填充颜色——

    for (var i = 0; i < total; i ++) {
      let x = Math.random() * ww,
            y = Math.random() * hh,
            v = Math.random() * 0.5 + 0.5,
            color = 'rgba(255,255,255,.5)';
      particles.push({x: x, y: y, r: r, v: v, color: color});
    }

其中,particles.push(...) 是数组追加数据的方法,这里把粒子各个键值对一一存储到前面声明的空数组 particles 中。

第22~29行:绘制原型图像函数,需要四个参数,圆心xy坐标、圆半径以及圆填充色。这是标准的canvas画圆方法的封装,不多说——

    var draw = (x,y,r,color) => {
      ctx.save();
      ctx.fillStyle = color;
      ctx.beginPath();
      ctx.arc(x, y, r, 0, 2 * Math.PI);
      ctx.fill();
      ctx.restore();
};

第31~42行:粒子行进函数,说明在下面代码的注释里——

    var move = () => {
         // 将画布绘制为0.1透明度的黑色矩形,这是粒子拖尾的关键
      ctx.fillStyle = 'rgba(0, 0, 0, .1)';
      ctx.fillRect(0, 0, ww, hh);
      // 遍历粒子,令粒子不断改变自己的圆心坐标
      particles.forEach(p => {
                draw(p.x, p.y, p.r, p.color);
                p.x += p.v;
                p.y += p.v;
                if (p.x > ww) p.x = 0;
                if (p.y > ww) p.y = 0;
      });
      requestAnimationFrame(move); // 通过显示刷新率递归调用函数自身以达到永动动画的目的
    };

此法实现了拖尾效果,也是canvas画布中最常使用的实现方式。运行性能极高,只是拖尾有残留。


CSS代码中,代码第03行,设计了纯黑背景色和融合滤镜属性。纯黑色背景配套 screen 融合滤镜,但即便不设置 background 纯黑背景,画布也会逐渐变黑,因为拖尾效果的实现就是将画布填充为一定透明度的黑色(可以是其他颜色,那就背景变成该颜色),反复渲染后呈现出来的背景会是黑色的。融合滤镜的使用是为了适应帖子容器的实际背景,虽然不完美。

另一种实现拖尾的方式和这个大同小异,它也会用到一定透明度的颜色填充画布,不同的是还用上全局透明属性+混合属性,效果会和本法一样。

还有第三种实现拖尾的方法,很复杂:记录粒子的前几个轨迹,将其渐次透明化。这回营造出更为逼真且没有残留的拖尾效果,但开销极大,运行性能会降低很多。

南无月 发表于 2024-5-4 19:03

画布画出来的流星雨效果。。
有得飞得快一些,有的飞的慢一些。。

红影 发表于 2024-5-4 20:05

canvas也能画流星雨,这流星雨好漂亮啊{:4_199:}

马黑黑 发表于 2024-5-4 20:06

红影 发表于 2024-5-4 20:05
canvas也能画流星雨,这流星雨好漂亮啊

之前我们不是做过烟花吗?拖尾的原理和这个虽然不尽一样,道理是相同的

马黑黑 发表于 2024-5-4 20:06

南无月 发表于 2024-5-4 19:03
画布画出来的流星雨效果。。
有得飞得快一些,有的飞的慢一些。。

这是机制问题,可以随意的

南无月 发表于 2024-5-4 20:23

马黑黑 发表于 2024-5-4 20:06
这是机制问题,可以随意的

随意飞的效果更好{:4_170:}

红影 发表于 2024-5-4 20:25

马黑黑 发表于 2024-5-4 20:06
之前我们不是做过烟花吗?拖尾的原理和这个虽然不尽一样,道理是相同的

是的,之前有过烟花,但是用画布做还是有点不一样呢{:4_187:}

马黑黑 发表于 2024-5-4 20:45

红影 发表于 2024-5-4 20:25
是的,之前有过烟花,但是用画布做还是有点不一样呢

道理差不多

马黑黑 发表于 2024-5-4 20:46

南无月 发表于 2024-5-4 20:23
随意飞的效果更好

那要看场景的,比如有些场景,敬酒的话,只能是下属对上司敬酒

南无月 发表于 2024-5-4 21:16

马黑黑 发表于 2024-5-4 20:46
那要看场景的,比如有些场景,敬酒的话,只能是下属对上司敬酒

这个也是实用的技巧。。老师好细心。{:4_170:}

{:4_191:}再给老师敬一杯

红影 发表于 2024-5-4 21:16

马黑黑 发表于 2024-5-4 20:45
道理差不多

这个很漂亮呢{:4_187:}

马黑黑 发表于 2024-5-4 21:48

红影 发表于 2024-5-4 21:16
这个很漂亮呢

不算特难看

马黑黑 发表于 2024-5-4 21:49

南无月 发表于 2024-5-4 21:16
这个也是实用的技巧。。老师好细心。

再给老师敬一杯

{:4_191:}

小辣椒 发表于 2024-5-4 21:56

流星雨粒子来了{:4_199:}

马黑黑 发表于 2024-5-4 21:57

小辣椒 发表于 2024-5-4 21:56
流星雨粒子来了

{:4_190:}

红影 发表于 2024-5-4 22:47

马黑黑 发表于 2024-5-4 21:48
不算特难看

其实在特定情况下,淡淡的拖尾效果也不错的{:4_173:}

马黑黑 发表于 2024-5-5 08:15

红影 发表于 2024-5-4 22:47
其实在特定情况下,淡淡的拖尾效果也不错的

只能如此

红影 发表于 2024-5-5 10:54

马黑黑 发表于 2024-5-5 08:15
只能如此

很多东西会有意想不到的效果呢。
页: [1] 2 3 4
查看完整版本: canvas拖尾效果演示