H5用canvas放烟花

发布时间 2023-08-15 09:53:26作者: meieiem

很久很久以前的一个很流行的java Applet放烟花效果,当初移到android过,这次摸鱼时间翻译成js代码,用canvas实现
这么多年,终于能大致看懂这代码了,

已经实现透明效果,只需要给body弄个好看的背景图片就行,但需要主色为深色,看到的人谁有兴趣美化下,弄个背景加个声音啥的,不过没啥用就是的了,只是弄着好玩

谁要是弄得漂亮也给我看下,虽然我已经是个老头了,但也有一颗爱美的心

主要学到的是

1. js里面的各种Array,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray

2. java中,像素数组一般用int数组,一个int表示一个像素, 而js中一般用Uint8数组,四个uint8表示一个像素,转换要考虑高低位的问题,  这里getPix setPix中的转换过程 可以省略掉,但年纪大了,懒了

【感谢GPT的大力相助】

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>烟花</title>
    <style>
        #mainCanvas{
            width: 800px;
            height: 600px; 
            background-color:  rgba(255, 255, 255, 0);
            position: absolute;
            top: 0;
            left: 0;    
        }
    </style>
    <script src="fireworks.js"></script>
  </head>
  <body style="background: #000000 ;">
     <canvas ref="mainCanvas" id="mainCanvas" ></canvas>
     <script>
        var f=new Fireworks(mainCanvas);
     </script>
  </body>
</html>

Fireworks.js

 class Fireworks{    
    constructor(canvas) {
        //0xff000000前面的ff表示透明度,可以调小让背景图显示出来 不过这里没研究清楚
        this.alpha = 0xFF000000;
        this.fps = 12;
        this.canvas = canvas;
        this.width = 800;
        this.height = 600; 
        this.size= this.width*this.height;
        //减2行,以免超出   
        this.size2=this.size-this.width*2;    
        this.canvas.width = this.width;
        this.canvas.height = this.height;  

        this.ctx = canvas.getContext('2d');
         
        

        this.imageData = this.ctx.createImageData(this.width, this.height);

           
        this.canvas.addEventListener("mousedown", this.onDown.bind(this));
        //最多烟花数量,炸一次算50个,也就是同时最多10个烟花在,
        this.bits=500;
        //炸开后的烟花数量
        this.bit_max=50;
        //炸开角度,但具体影响还不清楚
        this.ru = 50;
        this.rv = 50;
        //中心点
        this.m_centerX = Math.floor(this.width / 2);
        this.m_centerY = Math.floor(this.height / 2);
      
        //实际图像数据 每四个位置表示一个像素 R G B A
        this.data=new ArrayBuffer(this.width*this.height*4);
        this.dataView = new DataView(this.data);
        //烟花计算数据
        this.bit_px = new Array(this.bits).fill(0);
        this.bit_py = new Array(this.bits).fill(0);
        this.bit_vx = new Array(this.bits).fill(0);
        this.bit_vy = new Array(this.bits).fill(0);
        this.bit_sx = new Int32Array(this.bits).fill(0);
        this.bit_sy = new Int32Array(this.bits).fill(0);
        this.bit_l = new Int32Array(this.bits).fill(0);
        this.bit_f = new Int32Array(this.bits).fill(0);
        this.bit_p = new Int32Array(this.bits).fill(0);
        this.bit_c = new Int32Array(this.bits).fill(0);

        this.m_centerX = Math.floor(this.width / 2);
        this.m_centerY = Math.floor(this.height / 2);



        this.initData();
        this.calculate();
    }
    initData(){ 
        //最开始的时候给弄透明了 
        for(var i=0;i<this.size;i++){         
            this.dataView.setUint32(i*4,0x00000000,true);                
        } 
        this.imageData.data.set(new Uint8ClampedArray(this.dataView.buffer));        
    }
    onDown(event){  
        this.m_mouseX = event.offsetX;
        this.m_mouseY = event.offsetY;
        let k = Math.floor(Math.random() * 256);
        let l = Math.floor(Math.random() * 256);
        let i1 = Math.floor(Math.random() * 256);
        let j1 = (k << 16) | (l << 8) | i1 | this.alpha;
        let k1 = 0;
        console.log(this.bits);
        console.log(this.bit_max);
        for (let l1 = 0; l1 < this.bits; l1++) {
           
            if (this.bit_f[l1] !== 0) {
                continue;
            }
            this.bit_px[l1] = this.m_mouseX;
            this.bit_py[l1] = this.m_mouseY;
            let d = Math.random() * 6.2800000000000002;
            let d1 = Math.random();
            this.bit_vx[l1] = Math.sin(d) * d1;
            this.bit_vy[l1] = Math.cos(d) * d1;
            this.bit_l[l1] = Math.floor(Math.random() * 100) + 100;
            this.bit_p[l1] = Math.floor(Math.random() * 3);
            this.bit_c[l1] = j1;
            this.bit_sx[l1] = this.m_mouseX;
            this.bit_sy[l1] = this.height - 5;
            this.bit_f[l1] = 2;
            if (++k1 === this.bit_max) {
                break;
            }
        }

     //这里播放开始声音

        //这里尝试点击后修改一个像素点的颜色 
       /* let pix=this.getPix(y*this.width+x);
        console.log(pix);
        this.setPix(y*this.width+x,0xffff00f0);
        this.imageData.data.set(new Uint8ClampedArray(this.dataView.buffer));  */
    }
    getPix(byteOffset){
        //获取像素点       
        return this.dataView.getUint32(byteOffset*4, true);
    }
    setPix(byteOffset,val){
        this.dataView.setUint32(byteOffset*4,val,true);
    }
    bit_set(x,y,v){
        //设置像素点 
        this.setPix(y * this.width + x,v);
    }
    fade(){ 
        //全图慢慢扩散和淡化 
        for (let j = 0; j < this.size2; j++) { 
            //取四个点 
            const k = this.getPix(j); 
            //最后会是黑色,所以如果一个点的值是黑色,就可以不运算了 
            if(k==this.alpha){
                this.setPix(j,0x00000000);
                continue;
            }
            if(k==0x00000000){
                continue;
            }
            //右边
            const l = this.getPix(j+1);
            //下一行的点
            const i1 = this.getPix(j + this.width);
            //下一行右边
            const j1 = this.getPix(j + this.width + 1);

            let i = (k & 0xff0000) >> 16;
            let k1 = ((((l & 0xff0000) >> 16) - i) * 50 >> 8) + i;
            i = (k & 0xff00) >> 8;
            let l1 = ((((l & 0xff00) >> 8) - i) * 50 >> 8) + i;
            i = k & 0xff;
            let i2 = (((l & 0xff) - i) * 50 >> 8) + i;
            i = (i1 & 0xff0000) >> 16;
            let j2 = ((((j1 & 0xff0000) >> 16) - i) * 50 >> 8) + i;
            i = (i1 & 0xff00) >> 8;
            let k2 = ((((j1 & 0xff00) >> 8) - i) * 50 >> 8) + i;
            i = i1 & 0xff;
            let l2 = (((j1 & 0xff) - i) * 50 >> 8) + i;
            let i3 = ((j2 - k1) * 50 >> 8) + k1;
            let j3 = ((k2 - l1) * 50 >> 8) + l1;
            let k3 = ((l2 - i2) * 50 >> 8) + i2;
            let val = (i3 << 16) | (j3 << 8) | k3 | this.alpha; 
            this.setPix(j,val);
          }
        
    }
    //计算烟花
    rend() {
      
        let flag2 = false;
        for (let k = 0; k < this.bits; k++) {            
            switch (this.bit_f[k]) {
                default:
                    break; 
                case 1: 
                    this.bit_vy[k] += Math.random() / 50;
                    this.bit_px[k] += this.bit_vx[k];
                    this.bit_py[k] += this.bit_vy[k];
                    this.bit_l[k]--;
                    if (this.bit_l[k] === 0 || this.bit_px[k] < 0.0 || this.bit_py[k] < 0.0 || this.bit_px[k] > this.width || this.bit_py[k] > this.height - 3) {
                        this.bit_c[k] = this.alpha;
                        this.bit_f[k] = 0;
                    } else if (this.bit_p[k] === 0) {
                        if (Math.floor(Math.random() * 2) === 0) {
                            this.bit_set(Math.floor(this.bit_px[k]), Math.floor(this.bit_py[k]), -1);
                        }
                    } else {
                        this.bit_set(Math.floor(this.bit_px[k]), Math.floor(this.bit_py[k]), this.bit_c[k]);
                    }
                    break;
    
                case 2:
                    //这里是飞行速度,
                    this.bit_sy[k] -= 10;
                    if (this.bit_sy[k] <= this.bit_py[k]) { 
                        this.bit_f[k] = 1;
                        flag2 = true;
                    }
                    if (Math.floor(Math.random() * 20) === 0) {
                        let i = Math.floor(Math.random() * 2);
                        let j = Math.floor(Math.random() * 5);
                        this.bit_set(this.bit_sx[k] + i, this.bit_sy[k] + j, -1);
                    }
                    break;
            }
        }
       
        if (flag2  ) {
            //播放爆炸声音
        }
    }
    
    calculate(){ 
        this.fade();
        this.rend();
        this.imageData.data.set(new Uint8ClampedArray(this.dataView.buffer));
        
        this.ctx.putImageData(this.imageData,0,0);  
        setTimeout(()=>{this.calculate()},50);
    }

}

  

 

原始java代码



import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.*;
import java.awt.image.MemoryImageSource;
import java.util.Random;


@SuppressWarnings("serial")
public class Test extends Applet implements Runnable {
    public static void main(String[]args){
        Test test=new Test();
        test.init();

    }
    public Test() {
        m_mouseX = 0;
        m_mouseY = 0;
        m_sleepTime = 5;
        isError = false;
        isInitialized = false;
        rand = new Random();
        bits = 50;
        bit_px = new double[bits];
        bit_py = new double[bits];
        bit_vx = new double[bits];
        bit_vy = new double[bits];
        bit_sx = new int[bits];
        bit_sy = new int[bits];
        bit_l = new int[bits];
        bit_f = new int[bits];
        bit_p = new int[bits];
        bit_c = new int[bits];
        ru = 50;
        rv = 50;
    }

    public void init() {
        String s ="50";
        if (s != null)
            bits = Integer.parseInt(s);
        s = "30";
        if (s != null)
            bit_max = Integer.parseInt(s);
        s = "20";
        if (s != null)
            ru = Integer.parseInt(s);
        s = "100";
        if (s != null)
            rv = Integer.parseInt(s);
        s ="0";
        if (s != null)
            bit_sound = Integer.parseInt(s);
        m_nAppX = getSize().width;
        m_nAppY = getSize().height;
        m_centerX = m_nAppX / 2;
        m_centerY = m_nAppY / 2;
        m_mouseX = m_centerX;
        m_mouseY = m_centerY;
        resize(m_nAppX, m_nAppY);
        pixls = m_nAppX * m_nAppY;
        pixls2 = pixls - m_nAppX * 2;
        pix0 = new int[pixls];
        offImage = new MemoryImageSource(m_nAppX, m_nAppY, pix0, 0, m_nAppX);
        offImage.setAnimated(true);
        dbImg = createImage(offImage);
        for (int i = 0; i < pixls; i++)
            pix0[i] = 0xff000000;

        //sound1 = getAudioClip(getDocumentBase(), "firework.au");
        //sound2 = getAudioClip(getDocumentBase(), "syu.au");
        for (int j = 0; j < bits; j++)
            bit_f[j] = 0;

        isInitialized = true;
        start();
    }
    private boolean stop;

    public void run() {
        while (!isInitialized)
            try {
                Thread.sleep(200L);
            } catch (InterruptedException interruptedexception) {
            }
        do {
            for (int j = 0; j < pixls2; j++) {
                int k = pix0[j];
                int l = pix0[j + 1];
                int i1 = pix0[j + m_nAppX];
                int j1 = pix0[j + m_nAppX + 1];
                int i = (k & 0xff0000) >> 16;
                int k1 = ((((l & 0xff0000) >> 16) - i) * ru >> 8) + i;
                i = (k & 0xff00) >> 8;
                int l1 = ((((l & 0xff00) >> 8) - i) * ru >> 8) + i;
                i = k & 0xff;
                int i2 = (((l & 0xff) - i) * ru >> 8) + i;
                i = (i1 & 0xff0000) >> 16;
                int j2 = ((((j1 & 0xff0000) >> 16) - i) * ru >> 8) + i;
                i = (i1 & 0xff00) >> 8;
                int k2 = ((((j1 & 0xff00) >> 8) - i) * ru >> 8) + i;
                i = i1 & 0xff;
                int l2 = (((j1 & 0xff) - i) * ru >> 8) + i;
                int i3 = ((j2 - k1) * rv >> 8) + k1;
                int j3 = ((k2 - l1) * rv >> 8) + l1;
                int k3 = ((l2 - i2) * rv >> 8) + i2;
                pix0[j] = i3 << 16 | j3 << 8 | k3 | 0xff000000;
            }

            rend();
            offImage.newPixels(0, 0, m_nAppX, m_nAppY);
            try {
                Thread.sleep(m_sleepTime);
            } catch (InterruptedException interruptedexception1) {
            }
        } while (!stop);
    }

    public void update(Graphics g) {
        paint(g);
    }

    public void paint(Graphics g) {
        g.drawImage(dbImg, 0, 0, this);
    }

    public void start() {
        if (isError)
            return;
        isRunning = true;
        if (runner == null) {
            runner = new Thread(this);
            runner.start();
        }
    }

    @SuppressWarnings("deprecation")
    public void stop() {
        if (runner != null) {
            runner.stop();
            runner = null;
        }
    }

    public boolean mouseMove(Event event, int i, int j) {
        m_mouseX = i;
        m_mouseY = j;
        return true;
    }

    public boolean mouseDown(Event event, int i, int j) {
        m_mouseX = i;
        m_mouseY = j;
        int k = (int) (rand.nextDouble() * 256D);
        int l = (int) (rand.nextDouble() * 256D);
        int i1 = (int) (rand.nextDouble() * 256D);
        int j1 = k << 16 | l << 8 | i1 | 0xff000000;
        int k1 = 0;
        for (int l1 = 0; l1 < bits; l1++) {
            if (bit_f[l1] != 0)
                continue;
            bit_px[l1] = m_mouseX;
            bit_py[l1] = m_mouseY;
            double d = rand.nextDouble() * 6.2800000000000002D;
            double d1 = rand.nextDouble();
            bit_vx[l1] = Math.sin(d) * d1;
            bit_vy[l1] = Math.cos(d) * d1;
            bit_l[l1] = (int) (rand.nextDouble() * 100D) + 100;
            bit_p[l1] = (int) (rand.nextDouble() * 3D);
            bit_c[l1] = j1;
            bit_sx[l1] = m_mouseX;
            bit_sy[l1] = m_nAppY - 5;
            bit_f[l1] = 2;
            if (++k1 == bit_max)
                break;
        }

        if (bit_sound > 1)
            sound2.play();
        return true;
    }

    public boolean mouseExit(Event event, int i, int j) {
        m_mouseX = i;
        m_mouseY = j;
        return true;
    }

    // (JAVA世纪网,java2000.net)
    void rend() {
        boolean flag2 = false;
        for (int k = 0; k < bits; k++)
            switch (bit_f[k]) {
                default:
                    break;

                case 1: // '\001'
                    bit_vy[k] += rand.nextDouble() / 50D;
                    bit_px[k] += bit_vx[k];
                    bit_py[k] += bit_vy[k];
                    bit_l[k]--;
                    if (bit_l[k] == 0 || bit_px[k] < 0.0D || bit_py[k] < 0.0D || bit_px[k] > (double) m_nAppX
                            || bit_py[k] > (double) (m_nAppY - 3)) {
                        bit_c[k] = 0xff000000;
                        bit_f[k] = 0;
                    } else if (bit_p[k] == 0) {
                        if ((int) (rand.nextDouble() * 2D) == 0)
                            bit_set((int) bit_px[k], (int) bit_py[k], -1);
                    } else {
                        bit_set((int) bit_px[k], (int) bit_py[k], bit_c[k]);
                    }
                    break;

                case 2: // '\002'
                    bit_sy[k] -= 5;
                    if ((double) bit_sy[k] <= bit_py[k]) {
                        bit_f[k] = 1;
                        flag2 = true;
                    }
                    if ((int) (rand.nextDouble() * 20D) == 0) {
                        int i = (int) (rand.nextDouble() * 2D);
                        int j = (int) (rand.nextDouble() * 5D);
                        bit_set(bit_sx[k] + i, bit_sy[k] + j, -1);
                    }
                    break;
            }

        if (flag2 && bit_sound > 0)
            sound1.play();
    }

    void bit_set(int i, int j, int k) {
        int l = i + j * m_nAppX;
        pix0[l] = k;
    }

    private int m_nAppX;
    private int m_nAppY;
    private int m_centerX;
    private int m_centerY;
    private int m_mouseX;
    private int m_mouseY;
    private int m_sleepTime;
    private boolean isError;
    boolean isRunning;
    boolean isInitialized;
    Thread runner;
    int pix0[];
    MemoryImageSource offImage;
    Image dbImg;
    int pixls;
    int pixls2;
    Random rand;
    int bits;
    double bit_px[];
    double bit_py[];
    double bit_vx[];
    double bit_vy[];
    int bit_sx[];
    int bit_sy[];
    int bit_l[];
    int bit_f[];
    int bit_p[];
    int bit_c[];
    int bit_max;
    int bit_sound;
    int ru;
    int rv;
    AudioClip sound1;
    AudioClip sound2;
}