美文网首页页面特效
Canvas<粒子文字效果>

Canvas<粒子文字效果>

作者: 誰在花里胡哨 | 来源:发表于2020-03-18 17:24 被阅读0次
    效果图:
    20200318.gif
    参考效果地址:http://www.jq22.com/code1148
    (孰能生巧,现在只能先 “比葫芦画瓢”,慢慢来吧,注释很全,不过还是需要自己加以理解 👍)

    🎈背景色修改:

    background: rgb(0, 0, 0);
    

    🎈粒子相关配置:

    var max_radius = 3; //粒子的最大半径
    var min_radius = 1;  //粒子的最小半径
    var drag = 20; //切换文字,粒子组成速度 值越小越快
    var colors = ["rgba(255, 255, 255, 0.7)","rgba(255, 255, 255, 0.1)"];//粒子颜色
    var bool = true; //默认粒子文字是直接显示,反之则是粒子打乱后显示
    var textFont = 80; //粒子文字大小
    

    🎈方法调用:

    changeText("❤");
    
    202003181718.gif

    获得在画布上绘制的像素坐标位置

        let temp;
            for (let i = 0; i < data.length; i += 4) {
                temp = {
                    x: (i / 4) % w,
                    y: ~~((i / 4) / w)
                };
                if (data[i] !== 0 && ~~(Math.random() * 5) == 1) {
                    ctx.beginPath();
                    ctx.fillStyle = 'red';
                    ctx.arc(temp.x, temp.y, 2, 0, Math.PI * 2);
                    ctx.fill();
                }
            }
    

    若你不在画布上做任何操作,那么当你输出data时为。所以加上 data[i] !== 0 (rgb(0, 0, 0)为黑色,只要你绘制的东西不是这个色值,肯定会出现不等于0的像素模块,因此就能确定出你作画的位置坐标)

    image.png
    代码如下:
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <style>
            body {
                width: 100%;
                height: 100vh;
                overflow: hidden;
                /* background-color: black; */
                margin: 0;
            }
    
            canvas {
                width: 100%;
                height: 100%;
                background: rgb(0, 0, 0);
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
            }
    
            #input {
                font-size: 30px;
                color: white;
                outline: none;
                display: block;
                width: 300px;
                position: absolute;
                left: calc(50% - 150px);
                top: 30%;
                border: none;
                background: transparent;
                border-bottom: 2px solid rgba(255, 255, 255, 0.822);
                text-align: center;
            }
        </style>
    </head>
    
    <body>
        <input id="input" type="text" maxlength="8" oninput="changeText(input.value)">
    </body>
    <script>
        /*jshint esversion:6*/
        var canvas = document.createElement("canvas");
        var w = window.innerWidth;
        var h = window.innerHeight;
        document.body.appendChild(canvas);
        canvas.width = w;
        canvas.height = h;
        var ctx = canvas.getContext('2d');
    
        //储存粒子数组
        var particles = [];
        var max_radius = 3; //粒子的最大半径
        var min_radius = 1;  //粒子的最小半径
        var drag = 20; //切换文字,粒子组成速度 值越小越快
        var colors = ["rgba(255, 255, 255, 0.7)", "rgba(255, 255, 255, 0.1)"];//粒子颜色
        var bool = true; //默认粒子文字是直接显示,反之则是粒子打乱后显示
        var textFont = 80; //粒子文字大小
        //初始鼠标坐标
        var mouse = {
            x: -1000,
            y: -1000
        };
        //pc 鼠标移动
        canvas.onmousemove = function (e) {
            mouse.x = e.clientX;
            mouse.y = e.clientY;
        };
        //mobile 手势移动
        canvas.ontouchmove = function (e) {
            mouse.x = e.touches[0].clientX;
            mouse.y = e.touches[0].clientY;
        };
    
        // 返回一个数的平方根
        function distance(x, y, x1, y1) {
            // sqrt() 方法可返回一个数的平方根。
            // a² + b² = c²
            return Math.sqrt((x1 - x) * (x1 - x) + (y1 - y) * (y1 - y));
        }
    
        //创建粒子文字或改变粒子文字
        //根据文字生成相对应的 粒子 ,并储存到 particles 数组内
        function changeText(text) {
            ctx.clearRect(0, 0, w, h);
            var current = 0; //初始化 粒子个数 默认为0个
            var temp; //初始化 粒子坐标
            var radius; //初始化 粒子半径
            var color; //初始化 粒子颜色
            //文字部分
            ctx.fillStyle = "rgb(255, 255, 255)";
            ctx.font = textFont + "px -apple-system";
            //https://www.w3school.com.cn/tags/canvas_filltext.asp
            //https://www.w3school.com.cn/tags/canvas_measuretext.asp
            //measureText 在画布上输出文本之前,检查字体的宽度
            // 实现文字居中
            ctx.fillText(text, w / 2 - ctx.measureText(text).width / 2, h / 2 + textFont / 2);
            //https://www.w3school.com.cn/tags/canvas_getimagedata.asp
            //通过 getImageData() 复制画布上指定矩形的像素数据
            var data = ctx.getImageData(0, 0, w, h).data;
            // console.log(data)
            // +=4 对于 ImageData 对象中的每个像素,都存在着4方面的信息,即 RGBA 值
            //此处 +=8 是为了避免资源浪费,减少粒子个数
            // += 越多,避免筛选的粒子越少,符合条件的,显示的粒子越少
            //data 会有特别 特别多
            for (let i = 0; i < data.length; i += 4) {
                temp = {
                    x: (i / 4) % w,
                    y: ~~((i / 4) / w)
                };
    
                //比较结果上的区别
                // != 返回同类型值比较结果。
                // !== 不同类型不比较,且无结果,同类型才比较。
                //比较过程上的区别
                //!= 比较时,若类型不同,会偿试转换类型。
                //!== 只有相同类型才会比较。
                //此处使用 !==,只能说是为了节约资源
    
                //http://www.fly63.com/article/detial/2802
                //~~ 同Math.floor() 向下取整
                //判断不为0 && 随机一个数值取值为 1 时,才生成一个粒子
                if (data[i] !== 0 && ~~(Math.random() * 5) == 1) {
                    //因为之前定义文字颜色为 rgb(255, 255, 255)w
                    //所以此处判断当遇到像素色值为 255 时,不显示粒子(若加上此处判断,则会实现描边效果,反之为填充效果)
                    // if (data[i + 4] !== 255 || data[i - 4] !== 255 || data[i + w * 4] !== 255 || data[i - w * 4] !== 255) {
                    //判断当再次调用 changeText 改变文字时,重新打乱并组成新的粒子文字
                    if (current < particles.length) {
                        particles[current].target = temp
                        // console.log(current)
                    } else {
                        //随机生成一个 min max 之间的粒子半径
                        radius = max_radius - Math.random() * min_radius;
                        //粒子从随机位置生成
                        if (!bool) {
                            temp = {
                                x: Math.random() * w,
                                y: Math.random() * h
                            };
                        }
    
                        //取粒子随机色
                        color = colors[~~(Math.random() * colors.length)];
    
                        //创建一个粒子,并添加到粒子数组中
                        var p = new Particle(
                            temp,
                            { x: (i / 4) % w, y: ~~((i / 4) / w) },
                            { x: 0, y: 0 },
                            color,
                            radius);
    
                        particles.push(p);
                    }
                    // ++current先自己加1,再做别的事情
                    //存储符合条件的粒子数量
                    ++current;
                }
                // }
            }
            bool = false;
            //当粒子文字变化时,删除多余的粒子
            particles.splice(current, particles.length - current);
        }
    
        //创建一个粒子的类,制定相关属性
        class Particle {
            constructor(pos, target, vel, color, radius) {
                this.pos = pos; //粒子坐标(随机位置或粒子坐标,方便实现文字切换时粒子打乱效果)
                this.target = target; //粒子坐标
                this.vel = vel; //记录鼠标移动打散的粒子坐标
                this.color = color; //粒子颜色
                this.radius = radius; //粒子半径
                var arr = [-1, 1];
                //粒子大小的变化速率
                this.direction = arr[~~(Math.random() * 2)] * Math.random() / 10;
            }
            //实时更新数据
            update() {
                //粒子半径的变化
                this.radius += this.direction;
                this.vel.x = (this.pos.x - this.target.x) / drag;
                this.vel.y = (this.pos.y - this.target.y) / drag;
    
                //鼠标移动到文字上时的效果
                //根据勾股定理 a² + b² = c²,鼠标坐标位置 - 粒子坐标位置 得到 a b 的长度,以此求得c的长度(及打散半径)
                //50 鼠标打散范围,当 c 的长度小于 50 时执行
                if (distance(this.pos.x, this.pos.y, mouse.x, mouse.y) < 50) {
                    this.vel.x += this.vel.x - (this.pos.x - mouse.x) / 15;
                    this.vel.y += this.vel.y - (this.pos.y - mouse.y) / 15;
                }
    
                //实现粒子变大缩小一直循环的效果
                if (this.radius >= max_radius) {
                    this.direction *= -1;
                }
                if (this.radius <= 1) {
                    this.direction *= -1;
                }
    
                this.pos.x -= this.vel.x;
                this.pos.y -= this.vel.y;
                this.draw()
            }
            //绘制粒子
            draw() {
                ctx.beginPath();
                ctx.fillStyle = this.color;
                ctx.arc(this.pos.x, this.pos.y, this.radius, 0, Math.PI * 2);
                ctx.fill();
            }
        }
    
        //绘制粒子文字
        function draw() {
            ctx.clearRect(0, 0, w, h);
            for (let n of particles) {
                n.update()
            }
        }
    
        //每帧执行的粒子变化动画
        function animation() {
            // 相当于每一帧都会执行一次方法
            window.requestAnimationFrame(animation)
            draw()
        }
        animation()
    
        //默认显示粒子文字为 input
        changeText("❤");
    </script>
    
    </html>
    

    相关文章

      网友评论

        本文标题:Canvas<粒子文字效果>

        本文链接:https://www.haomeiwen.com/subject/sajcyhtx.html