浮动粒子制作404动画页面

前言:之前在网上看到了这个效果,之后我在做项目的时候,正好将他放进了项目里面,这篇博客就介绍一下该效果的原理;

1.首先是基础的设置

const canvas = <HTMLCanvasElement>document.getElementById('canvas');
let ctx: any = canvas.getContext('2d');
const W: number = canvas.width = window.innerWidth;
const H: number = canvas.height = window.innerHeight;

const mFound: number = H * 2.5; //found 的粒子数量
const mStr: number = H * 1.5;// 404的例子数量
ctx.textAlign = "center";//居中

canvas 的大小是占据整个屏幕;

2.绘出字

const str: string = '404';
ctx.textBaseline = 'bottom';
ctx.font = `${H / 2}px  'Arial', sans-serif`;
ctx.fillText(str, W / 2, (H + H / 4) / 2, W);

const found: string = 'Not Found';
ctx.textBaseline = 'top';
ctx.font = `${H / 6}px  'Arial', sans-serif`;
ctx.fillText(found, W / 2, (H + H / 4) / 2, W);

通过 fillText 的最后一个值,可以让字的大小不超过该值,我选择的是,浏览器的宽度,经我手机上检验,字的大小正好到达手机的宽度;
再说一下我这边要写2次,是因为字的尺寸是不一样的;

3.获取粒子

let imageDataStr = ctx.getImageData(0, 0, W, (H + H / 4) / 2);
let imageDataFound = ctx.getImageData(0, (H + H / 4) / 2, W, (H - H / 4) / 2);

interface dotsType {
    x: number
    y: number
    r: number
    a: number
    lx: number
    rx: number
    v: number
}

function getDots(imageData, isStr: boolean): dotsType[] {
    let dots = [];
    let index: number = 0;
    for (let i = 0; i < W; i += 2) {
        for (let j = 0; j < H; j += 2) {
            let k = 4 * (i + j * W);
            if (imageData.data[k + 3] > 0) {
                dots[index++] = {
                    x: i,
                    y: isStr ? j : j + (H + H / 4) / 2,
                    r: Math.random() * (isStr ? 8 : 3),
                    a: Math.random(),
                    lx: isStr ? i - 4 : i - 2,
                    rx: isStr ? i + 4 : i + 2,
                    v: (Math.random() - .5) * (isStr ? .8 : .4)
                }
            }
        }
    }
    let newDots = [];
    const m = isStr ? mStr : mFound;
    if (m && (dots.length > m)) {
        for (let i = 0; i < m; i++) {
            newDots.push(dots[Math.floor(Math.random() * dots.length)]);
        }
    } else {
        newDots = dots;
    }
    return newDots;
}

使用 canvas 的 getImageData函数,将 canvas 读取为 imageData 格式,再根据图片的像素的值来获取写有字的例子的位置,将其添加入一个粒子数组,顺便加入粒子的半径,透明度,运动半径,和运动速度,而他接受的isStr参数,是为了区分是 str 还是 found 而区别的;
为什么我要这样用一个参数来区分呢? 因为原来我看到的效果他是拿2个 canvas 叠在一起的,十分麻烦,而我根据定位和对应高度,加上 getImageData 的位置参数可以解决这个问题;
这新建了 newDots 数组是为了根据开头所设置的粒子数来打乱原数组的顺序;

4.渲染

let dataStr: dotsType[] = getDots(imageDataStr, true);
let DataFound: dotsType[] = getDots(imageDataFound, false);

const data: dotsType[] = [...dataStr, ...DataFound];

function render(): void {
    ctx.fillStyle = "#4db9ea";
    ctx.fillRect(0, 0, W, H);
    for (let i = 0; i < data.length; i++) {
        let temp = data[i];
        ctx.beginPath();
        ctx.fillStyle = `rgba(255,255,255,${temp.a})`;

        temp.x = temp.x + temp.v;
        temp.y = temp.y + temp.v;
        if (temp.x < temp.lx || temp.x > temp.rx) {
            temp.v = -temp.v;
        }
        ctx.arc(temp.x, temp.y, temp.r, 0, 2 * Math.PI);
        ctx.fill()
    }

}

render();

渲染出对于半径和透明度的圆;
因为分成了2个 imageData, 所以需要后面将2个粒子数组合并;并且根据每个数组的速度,来进行一个来回的直线运动;

5.计时

const requestAnimFrame = window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame;

let startTime = 0;

function animation(time: number = 0): void {
    if (time - startTime > 25) {
        render();
        startTime = time;
    }
    requestAnimFrame(animation)
};

animation();

本来的话直接使用requestAnimFrame就行了,但是我调低了帧率,影响不大;

最后:
demo:点击查看
github:https://github.com/Grewer/JsDemo/tree/master/floatParticles

原文地址:https://www.cnblogs.com/Grewer/p/8385117.html