欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

canvas实现烟花的示例代码

程序员文章站 2022-09-25 14:28:19
这篇文章主要介绍了canvas实现烟花的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 20-01-16...

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

目录结构

目录结构大致如下

canvas实现烟花的示例代码

我们将烟花分为两个阶段,一个是未炸开持续上升时期,另一个是炸开后分散的时期。
其中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;

大致效果

canvas实现烟花的示例代码

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