vue-admin项目登录Canvas放射星空粒子特效

156 阅读2分钟

先上图

image.png

实际是动态图,这里不能发视频

话不多说直接上代码

首先找你的login页面 定义一个空盒子,用来做canvas的容器

image.png

这里用id选择器,方便后续的js操作

然后,找到你项目的utils目录,这里我们一般是用来放项目中用到的辅助工具函数或文件的地方,

定义一个StarryParticles.ts文件,

因为是vue项目,所以我们要在这里默认导出一个函数,然后再login页面导入执行,就可以了

具体操作如下:

在utils导出

image.png

在login导入

image.png

这里会遇到一个问题,就是你要在导出的函数中使用id选择器获取dom,但是,在login页面执行函数的时候,页面还未加载完成,就获取dom,是获取不到的,这和在vue中画echarts遇到的情况很像,所以,我这里的解决办法是,在页面加载完成之后,在延时器中调用导出的函数

onMounted(() => {
    setTimeout(() => {
        StarryParticles();
    }, 200);
});

然后,就可以正常执行这个函数啦

最后,在StarryParticles.ts导出的函数具体如下:

// @ts-nocheck  开头写这个注释是为了关闭ts类型检查对当前页面的检查,有时候这个类型检查很sb
export const StarryParticles = () => {
    window.requestAnimFrame = (function () {
        return window.requestAnimationFrame;
    })();
    var t,
        e,
        n,
        o = document.getElementById('canvas'),
        r = o.getContext('2d'),
        i = 1900,
        s = '0.' + Math.floor(9 * Math.random()) + 1,
        a = 2 * o.width,
        c = 0,
        l = [];
    function u() {
        window.requestAnimFrame(u), p(), h();
    }
    function d() {
        (t = o.width / 2), (e = o.height / 2), (l = []);
        for (var r = 0; r < i; r++)
            (n = {
                x: Math.random() * o.width,
                y: Math.random() * o.height,
                z: Math.random() * o.width,
                o: '0.' + Math.floor(99 * Math.random()) + 1,
            }),
                l.push(n);
    }
    function p() {
        for (var t = 0; t < i; t++) (n = l[t]), n.z--, n.z <= 0 && (n.z = o.width);
    }
    function h() {
        var u, p, h;
        if (((o.width === window.innerWidth && o.width === window.innerWidth) || ((o.width = window.innerWidth), (o.height = window.innerHeight), d()), 0 === c)) {
            (r.fillStyle = 'rgba(0,10,20,1)'), r.fillRect(0, 0, o.width, o.height), (r.fillStyle = 'rgba(209, 255, 255, ' + s + ')');
            for (var f = 0; f < i; f++) (n = l[f]), (u = (n.x - t) * (a / n.z)), (u += t), (p = (n.y - e) * (a / n.z)), (p += e), (h = (a / n.z) * 1), r.fillRect(u, p, h, h), (r.fillStyle = 'rgba(209, 255, 255, ' + n.o + ')');
        }
    }
    d(), u();
 }

当然还有别的画法,但是如果是在vue3的话需要引入一个库

pnpm add particles.vue3

官方地址我也贴在下面

github

可视化地址

我用到的另外两张图如下:

同样是在StarryParticles.ts导出的函数

first

let canvas = document.querySelector('#canvas');
    let context = canvas.getContext('2d');
    let cw = (canvas.width = window.innerWidth);
    let ch = (canvas.height = window.innerHeight);

    class Boom {
        // 爆炸物是没有确定的结束点坐标, 这个可以通过设定一定的阀值来限定
        constructor(startX, startY) {
            this.startLocation = { x: startX, y: startY };
            this.nowLocation = { x: startX, y: startY };
            // 速度
            this.speed = Math.random() * 3;
            // 加速度
            this.acceleration = 1;
            // 没有确定的结束点,所以没有固定的角度,可以随机角度扩散
            this.angle = Math.random() * Math.PI * 2;
            // 这里设置阀值为100
            this.targetCount = 100;
            // 当前计算为1,用于判断是否会超出阀值
            this.nowNum = 1;
            // 透明度
            this.alpha = 1;
            // 重力系数
            this.gravity = 0;
            this.decay = 0.015;

            // 线段集合
            this.collection = new Array(2);

            // 是否到达目标点
            this.arrived = false;
        }

        draw() {
            context.beginPath();
            try {
                context.moveTo(this.collection[0][0], this.collection[0][1]);
            } catch (e) {
                context.moveTo(this.nowLocation.x, this.nowLocation.y);
            }

            context.lineWidth = 1;
            context.lineCap = 'round';
            context.lineTo(this.nowLocation.x, this.nowLocation.y);
            // 设置由透明度减小产生的渐隐效果,看起来没这么突兀
            context.strokeStyle = `rgba(255, 255, 255, ${this.alpha})`;
            context.stroke();
        }

        update() {
            this.collection.shift();
            this.collection.push([this.nowLocation.x, this.nowLocation.y]);
            this.speed *= this.acceleration;

            let vx = Math.cos(this.angle) * this.speed;
            // 加上重力系数,运动轨迹会趋向下
            let vy = Math.sin(this.angle) * this.speed + this.gravity;

            // 当前计算大于阀值的时候的时候,开始进行渐隐处理
            if (this.nowNum >= this.targetCount) {
                this.alpha -= this.decay;
            } else {
                this.nowLocation.x += vx;
                this.nowLocation.y += vy;
                this.nowNum++;
            }

            // 透明度为0的话,可以进行移除处理,释放空间
            if (this.alpha <= 0) {
                this.arrived = true;
            }
        }

        init() {
            this.draw();
            this.update();
        }
    }

    class Animate {
        constructor() {
            // 定义一个数组做为爆炸点的集合
            this.booms = [];
        }

        pushBoom() {
            // 实例化爆炸效果,随机条数的射线扩散
            for (let bi = Math.random() * 10 + 20; bi > 0; bi--) {
                this.booms.push(new Boom(cw / 2, ch / 2));
            }
        }

        run() {
            window.requestAnimationFrame(this.run.bind(this));
            context.clearRect(0, 0, cw, ch);

            let bnum = this.booms.length;
            while (bnum--) {
                // 触发动画
                this.booms[bnum].init();
                if (this.booms[bnum].arrived) {
                    // 到达目标透明度后,把炸点给移除,释放空间
                    this.booms.splice(bnum, 1);
                }
            }

            this.pushBoom();
        }
    }

    let a = new Animate();
    a.run();

second

    //整个星空的画布 canvas
        var canvas: HTMLCanvasElement = document.getElementById('canvas')!,
            ctx = canvas.getContext('2d')!,
            w = (canvas.width = window.innerWidth),
            h = (canvas.height = window.innerHeight),
            hue = 217,
            stars: string | any[] = [],
            count = 0,
            maxStars = 1600;
        //canvas2是每一颗星星的画法
        var canvas2 = document.createElement('canvas')!,
            ctx2 = canvas2.getContext('2d')!;
        canvas2.width = 100;
        canvas2.height = 100;
        var half = canvas2.width / 2,
            gradient2 = ctx2.createRadialGradient(half, half, 0, half, half, half); //中心渐变
        gradient2.addColorStop(0.025, '#fff');
        /*gradient2.addColorStop(0.1, 'hsl(' + hue + ', 61%, 33%)');
    gradient2.addColorStop(0.25, 'hsl(' + hue + ', 64%, 6%)');*/
        gradient2.addColorStop(0.1, 'rgba(33, 73, 136,0.9)');
        gradient2.addColorStop(0.25, 'rgba(33, 73, 136,0.05)');
        gradient2.addColorStop(1, 'transparent');

        ctx2.fillStyle = gradient2;
        ctx2.beginPath();
        ctx2.arc(half, half, half, 0, Math.PI * 2);
        ctx2.fill();

        // End cache

        function random(min: number, max?: number) {
            if (arguments.length < 2) {
                max = min;
                min = 0;
            }

            if (min > max) {
                var hold = max;
                max = min;
                min = hold;
            }

            return Math.floor(Math.random() * (max - min + 1)) + min;
        }

        function maxOrbit(x: number, y: number) {
            var max = Math.max(x, y),
                diameter = Math.round(Math.sqrt(max * max + max * max));
            return diameter / 2;
        }

        var Star = function (this: any) {
            this.orbitRadius = random(maxOrbit(w, h)); //星星运动轨迹旋转半径
            this.radius = random(60, this.orbitRadius) / 24; //星星半径
            this.orbitX = w / 2;
            this.orbitY = h / 2;
            this.timePassed = random(0, maxStars);
            this.speed = random(this.orbitRadius) / 900000; //900000初始值
            this.alpha = random(2, 10) / 10;

            count++;
            stars[count] = this;
        };

        Star.prototype.draw = function () {
            var x = Math.sin(this.timePassed) * this.orbitRadius + this.orbitX,
                y = Math.cos(this.timePassed) * this.orbitRadius + this.orbitY,
                twinkle = random(10);
            //实现闪烁的效果
            if (twinkle === 1 && this.alpha > 0) {
                this.alpha -= 0.05;
            } else if (twinkle === 2 && this.alpha < 1) {
                this.alpha += 0.05;
            }

            ctx.globalAlpha = this.alpha;
            ctx.drawImage(canvas2, x - this.radius / 2, y - this.radius / 2, this.radius, this.radius);
            this.timePassed += this.speed;
        };

        for (var i = 0; i < maxStars; i++) {
            new Star();
        }

        function animation() {
            //ctx.globalCompositeOperation = 'source-over';
            //ctx.globalAlpha = 1;
            //ctx.fillStyle = 'hsla(' + 217 + ', 64%, 6%, 1)';
            //ctx.fillRect(0, 0, w, h);
            ctx.clearRect(0, 0, w, h);

            ctx.globalCompositeOperation = 'lighter';
            for (var i = 1, l = stars.length; i < l; i++) {
                stars[i].draw();
            }
            //drawText();
            window.requestAnimationFrame(animation);
        }

        /*function drawChat(WhichCanvas,chat){}*/

        animation();
        //输入值 color[ r: g: b]
        function changeColorOfCanvas2(colorR: string | number, colorG: string | number, colorB: string | number) {
            //hue = color || 0;
            ctx2 = canvas2.getContext('2d');
            canvas2.width = 100;
            canvas2.height = 100;
            var half = canvas2.width / 2,
                gradient2 = ctx2.createRadialGradient(half, half, 0, half, half, half); //中心渐变
            gradient2.addColorStop(0.025, '#fff');
            gradient2.addColorStop(0.1, 'rgba(' + colorR + ',' + colorG + ', ' + colorB + ',0.9)');
            gradient2.addColorStop(0.25, 'rgba(' + colorR + ', ' + colorG + ',' + colorB + ',0.05)');
            gradient2.addColorStop(1, 'transparent');

            ctx2.fillStyle = gradient2;
            ctx2.beginPath();
            ctx2.arc(half, half, half, 0, Math.PI * 2);
            ctx2.fill();
        }
        var colors = [
            { r: 255, g: 255, b: 255 },
            // { r: 0, g: 200, b: 0 },
            { r: 0, g: 200, b: 200 },
            // { r: 200, g: 0, b: 200 },
        ];
        function randomNum(min: number, max: number) {
            var num = Math.floor(Math.random() * (max + 1 - min) + min);
            return num;
        }

        var currentColor = {
            r: 33,
            g: 73,
            b: 136,
        };
        /*changeColorOfCanvas2(colors[0]);*/
        function animateColor(targetColor: { r: any; g: any; b: any }) {
            var colorR = (targetColor.r - currentColor.r) / 30;
            var colorG = (targetColor.g - currentColor.g) / 30;
            var colorB = (targetColor.b - currentColor.b) / 30;
            console.log(colorR);
            for (var i = 1; i <= 30; i++) {
                var colorstep = {
                    r: Math.floor(currentColor.r + colorR * i),
                    g: Math.floor(currentColor.g + colorG * i),
                    b: Math.floor(currentColor.b + colorB * i),
                };
                setTimeout(() => {
                    changeColorOfCanvas2(colorstep.r, colorstep.g, colorstep.b);
                }, i * (1000 / 30));
            }
            currentColor = targetColor;
        }

        function animateColors(colors: string | any[]) {
            var randomindex = randomNum(0, colors.length - 1);
            var randomColor = colors[randomindex];
            animateColor(randomColor);
            return randomColor;
        }
        animateColors(colors);