亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > HTML5 > 正文

canvas實現煙花的示例代碼

2024-09-05 07:23:14
字體:
來源:轉載
供稿:網友

前言:馬上過年了,我打算在后臺里面偷偷地埋個新春祝福+放煙花的彩蛋。項目是基于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;}@autobindclass 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;

大致效果

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
一区二区福利视频| 国产精品久久久| 欧美美女操人视频| 欧美体内谢she精2性欧美| 国产精品国产福利国产秒拍| 精品性高朝久久久久久久| 亚洲成年网站在线观看| 欧美激情精品久久久久久大尺度| 国产精品视频精品| 欧美大胆a视频| 欧美日韩一区二区三区| 这里只有视频精品| 欧美成人在线网站| 国产亚洲成精品久久| 日韩欧美一区二区三区久久| 国产精品18久久久久久首页狼| 亚洲精品国产品国语在线| 亚洲欧洲成视频免费观看| 91产国在线观看动作片喷水| 久久久999精品| 久久国产精品久久久久久| 亚洲欧美日韩一区二区三区在线| 黑人巨大精品欧美一区二区一视频| 国产精品高清免费在线观看| 欧美激情在线狂野欧美精品| 亚洲第一区第一页| www.亚洲一二| 日韩av最新在线| 国产一区二区三区在线免费观看| 狠狠色香婷婷久久亚洲精品| 伊人久久久久久久久久久久久| 亚洲精品动漫久久久久| 国内伊人久久久久久网站视频| 国产欧美日韩专区发布| 91中文字幕一区| 久久免费少妇高潮久久精品99| 亚洲欧美在线第一页| 2019中文字幕全在线观看| 国产精品美女久久久久久免费| 中文字幕日韩欧美在线视频| 69**夜色精品国产69乱| 成人午夜一级二级三级| 伊人久久男人天堂| 欧美大片第1页| 久久男人av资源网站| 国产在线拍揄自揄视频不卡99| 亚洲天堂成人在线| 国产欧美日韩亚洲精品| 欧美韩日一区二区| 国产精品视频精品视频| 91大神在线播放精品| 亚洲香蕉在线观看| 国产精品自在线| 免费91麻豆精品国产自产在线观看| 在线亚洲午夜片av大片| 国产成人在线一区| 亚洲自拍偷拍区| 中文字幕日本精品| 久久视频精品在线| 永久免费毛片在线播放不卡| 日本视频久久久| 欧美人与性动交| 国产午夜精品全部视频播放| 中文字幕亚洲欧美日韩在线不卡| 日韩av片永久免费网站| 少妇激情综合网| 操人视频在线观看欧美| 国内久久久精品| 欧美一级在线播放| 日韩精品在线私人| 国产欧美久久久久久| 国产欧美在线视频| 欧美日韩在线免费| 日韩欧美在线观看视频| 亚洲综合色激情五月| 久久人人爽亚洲精品天堂| 在线视频欧美性高潮| 亚洲国产精品小视频| 亚洲欧洲在线观看| 欧美精品福利在线| 日本精品视频在线观看| 成人信息集中地欧美| 久久精品国产一区| 国产精品无av码在线观看| 日韩专区在线播放| 日韩在线免费视频| 日本精品免费一区二区三区| 日韩中文娱乐网| 日韩在线观看网站| 亚洲激情在线视频| 亚洲天天在线日亚洲洲精| 久久躁日日躁aaaaxxxx| 亚洲欧美成人网| 一本久久综合亚洲鲁鲁| 国外色69视频在线观看| 亚洲精品狠狠操| 91精品国产色综合久久不卡98| 国产精品va在线播放我和闺蜜| 亚洲精品视频免费在线观看| 亚洲xxx自由成熟| 亚洲综合成人婷婷小说| 日韩在线观看网址| 国产乱肥老妇国产一区二| 九九热这里只有在线精品视| 亚洲精品综合精品自拍| 亚洲男人天堂视频| 国产精品wwww| 欧美日韩国产一区中文午夜| 久久精品亚洲国产| 亚洲成人免费在线视频| 欧美性极品少妇精品网站| 国产精品精品视频一区二区三区| 91在线免费观看网站| 国产精品入口免费视频一| 91在线视频九色| 亚洲欧美日韩视频一区| 92版电视剧仙鹤神针在线观看| 日韩三级影视基地| 国产视频久久久久久久| 国产精品久久久久久久av电影| 亚洲欧美国产精品专区久久| 亚洲国产精品va在线| 午夜精品蜜臀一区二区三区免费| 日本成人激情视频| 国产精品久久久999| 国产激情久久久| 亚洲网站在线播放| 91在线精品视频| 国产一区二区三区免费视频| 欧美午夜片在线免费观看| 精品偷拍各种wc美女嘘嘘| 国产精品69久久久久| 色诱女教师一区二区三区| 国产99久久精品一区二区| 日韩av电影中文字幕| 欧美精品日韩www.p站| 国产精品久久久av久久久| 亚洲人成免费电影| 日本久久久久久久久久久| 午夜精品视频在线| 国产精品免费看久久久香蕉| 久久精品成人动漫| 国产精品一区二区久久久| 国产日韩综合一区二区性色av| 日韩欧美亚洲综合| 久久久国产精彩视频美女艺术照福利| 最新91在线视频| 国内精品模特av私拍在线观看| 国产成人精品久久二区二区| 亚洲国产日韩欧美在线图片| 国内精品久久久久| 亚洲欧美国产日韩中文字幕| 亚洲视频777| 国产剧情日韩欧美| 亚洲第一区中文99精品| 高清欧美性猛交xxxx| 国产精品观看在线亚洲人成网| 欧美xxxx综合视频| 亚洲男人av电影| 亚洲精品久久久久久久久久久久| 欧美国产日韩二区| 亚洲激情中文字幕| 色av吧综合网| 亚洲激情视频网站|