当前位置: 移动技术网 > IT编程>网页制作>Html5 > canvas实现烟花的示例代码

canvas实现烟花的示例代码

2020年06月22日  | 移动技术网IT编程  | 我要评论

前言:马上过年了,我打算在后台里面偷偷地埋个新春祝福+放烟花的彩蛋。项目是基于react+typescript的,因此最后封装成了一个组件,设置好开启时间就可以显示了。

目录结构

目录结构大致如下

我们将烟花分为两个阶段,一个是未炸开持续上升时期,另一个是炸开后分散的时期。
其中vector表示一个坐标,particle表示一个烟花的亮点,firewor表示烟花未炸开时持续上升的亮点。index.tsx就是组件了,绘制了canvas,并执行了动画。

vector

这个坐标就很简单,后面涉及到位置的变更都可以使用它的add方法进行偏移操作

export default class vector {
    constructor(public x: number, public y: number) {}
    add(vec2: {x: number; y: number}) {
        this.x = this.x + vec2.x;
        this.y = this.y + vec2.y;
    }
}

particle

初始创建的时候给个坐标,后续每次更新的时候控制y坐标下落,gravity变量就是下落的值。timespan用于控制烟花展示的时长

import vector from './vector';
export default class particle {
    pos: vector = null;
    vel: {x: number; y: number} = null;
    dead: boolean = false;
    start: number = 0;
    ctx: canvasrenderingcontext2d = null;
    constructor(pos: {x: number; y: number}, vel: {x: number; y: number}, ctx: canvasrenderingcontext2d) {
        this.pos = new vector(pos.x, pos.y);
        this.vel = vel;
        this.dead = false;
        this.start = 0;
        this.ctx = ctx;
    }
    update(time: number, gravity: number) {
        let timespan = time - this.start;

        if (timespan > 500) {
            this.dead = true;
        }

        if (!this.dead) {
            this.pos.add(this.vel);
            this.vel.y = this.vel.y + gravity;
        }
    }

    draw() {
        if (!this.dead) {
            this.drawdot(this.pos.x, this.pos.y, math.random() > 0.5 ? 1 : 2);
        }
    }
    drawdot(x: number, y: number, size: number) {
        this.ctx.beginpath();
        this.ctx.arc(x, y, size, 0, math.pi * 2);
        this.ctx.fill();
        this.ctx.closepath();
    }
}

firework

生成随机的hsl颜色,hsl(' + rndnum(360) + ', 100%, 60%);firework每次上升的距离是一个递减的过程,我们初始设置一个上升的距离,之后每次绘制的时候,这个距离减gravity,当距离小于零的时候,说明该出现烟花绽放的动画了。

import vector from './vector';
import particle from './particle';
let rnd = math.random;
function rndnum(num: number) {
    return rnd() * num + 1;
}
export default class firework {
    pos: vector = null;
    vel: vector = null;
    color: string = null;
    size: number = 0;
    dead: boolean = false;
    start: number = 0;
    ctx: canvasrenderingcontext2d = null;
    gravity: number = null;
    exparticles: particle[] = [];
    explen: number = 100;
    rootshow: boolean = true;
    constructor(x: number, y: number, gravity: number, ctx: canvasrenderingcontext2d) {
        this.pos = new vector(x, y);
        this.vel = new vector(0, -rndnum(10) - 3);
        this.color = 'hsl(' + rndnum(360) + ', 100%, 60%)';
        this.size = 4;
        this.dead = false;
        this.start = 0;
        this.ctx = ctx;
        this.gravity = gravity;
    }
    update(time: number, gravity: number) {
        if (this.dead) {
            return;
        }

        this.rootshow = this.vel.y < 0;

        if (this.rootshow) {
            this.pos.add(this.vel);
            this.vel.y = this.vel.y + gravity;
        } else {
            if (this.exparticles.length === 0) {
                for (let i = 0; i < this.explen; i++) {
                    let randomr = rndnum(5);
                    let randomx = -rndnum(math.abs(randomr) * 2) + math.abs(randomr);
                    let randomy =
                        math.sqrt(math.abs(math.pow(randomr, 2) - math.pow(randomx, 2))) *
                        (math.random() > 0.5 ? 1 : -1);
                    this.exparticles.push(new particle(this.pos, new vector(randomx, randomy), this.ctx));
                    this.exparticles[this.exparticles.length - 1].start = time;
                }
            }
            let numofdead = 0;
            for (let i = 0; i < this.explen; i++) {
                let p = this.exparticles[i];
                p.update(time, this.gravity);
                if (p.dead) {
                    numofdead++;
                }
            }

            if (numofdead === this.explen) {
                this.dead = true;
            }
        }
    }

    draw() {
        if (this.dead) {
            return;
        }

        this.ctx.fillstyle = this.color;
        if (this.rootshow) {
            this.drawdot(this.pos.x, this.pos.y, this.size);
        } else {
            for (let i = 0; i < this.explen; i++) {
                let p = this.exparticles[i];
                p.draw();
            }
        }
    }
    drawdot(x: number, y: number, size: number) {
        this.ctx.beginpath();

        this.ctx.arc(x, y, size, 0, math.pi * 2);
        this.ctx.fill();

        this.ctx.closepath();
    }
}

fireworkcomponent

组件本身就很简单了,生成和绘制firework。我们在这里面可以额外加一些文字

import react from 'react';
import firework from './firework';
import {autobind} from 'core-decorators';
let rnd = math.random;
function rndnum(num: number) {
    return rnd() * num + 1;
}
interface proptypes {
    onclick?: () => void;
}
@autobind
class fireworkcomponent extends react.component<proptypes> {
    canvas: htmlcanvaselement = null;
    ctx: canvasrenderingcontext2d = null;
    snaptime: number = 0;
    fireworks: firework[] = [];
    gravity: number = 0.1;
    componentdidmount() {
        this.canvas = document.queryselector('#fireworks');
        this.canvas.width = window.innerwidth;
        this.canvas.height = window.innerheight;
        this.ctx = this.canvas.getcontext('2d');
        this.init();
        this.draw();
    }

    init() {
        let numoffireworks = 20;
        for (let i = 0; i < numoffireworks; i++) {
            this.fireworks.push(new firework(rndnum(this.canvas.width), this.canvas.height, this.gravity, this.ctx));
        }
    }

    update(time: number) {
        for (let i = 0, len = this.fireworks.length; i < len; i++) {
            let p = this.fireworks[i];
            p.update(time, this.gravity);
        }
    }
    draw(time?: number) {
        this.update(time);

        this.ctx.fillstyle = 'rgba(0,0,0,0.3)';

        this.ctx.fillstyle = 'rgba(0,0,0,0)';
        this.ctx.clearrect(0, 0, this.canvas.width, this.canvas.height);

        this.ctx.font = 'bold 30px cursive';
        this.ctx.fillstyle = '#e91818';
        let text = 'xx项目组给您拜个早年!';
        let textwidth = this.ctx.measuretext(text);
        this.ctx.filltext(text, this.canvas.width / 2 - textwidth.width / 2, 200);
        text = '在新年来临之际,祝您:';
        textwidth = this.ctx.measuretext(text);
        this.ctx.filltext(text, this.canvas.width / 2 - textwidth.width / 2, 260);
        text = '工作顺利,新春快乐!';
        this.ctx.font = 'bold 48px stcaiyun';
        this.ctx.fillstyle = 'orangered';
        textwidth = this.ctx.measuretext(text);
        this.ctx.filltext(text, this.canvas.width / 2 - textwidth.width / 2, 340);
        this.ctx.fillstyle = 'gray';
        this.ctx.font = '18px arial';
        text = '点击任意处关闭';
        textwidth = this.ctx.measuretext(text);
        this.ctx.filltext(text, this.canvas.width - 20 - textwidth.width, 60);
        this.snaptime = time;

        this.ctx.fillstyle = 'blue';
        for (let i = 0, len = this.fireworks.length; i < len; i++) {
            let p = this.fireworks[i];
            if (p.dead) {
                p = this.fireworks[i] = new firework(
                    rndnum(this.canvas.width),
                    this.canvas.height,
                    this.gravity,
                    this.ctx
                );
                p.start = time;
            }
            p.draw();
        }

        window.requestanimationframe(this.draw);
    }

    render() {
        return (
            <canvas
                id="fireworks"
                onclick={this.props.onclick}
                style={{position: 'fixed', zindex: 99, background: 'rgba(0,0,0, 0.8)'}}
                width="400"
                height="400"></canvas>
        );
    }
}
export default fireworkcomponent;

大致效果

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网