canvas烟花-娱乐

网上看到一个释放烟花的canvas案例,很好看哦。

新建文本,把下面代码复制进去,后缀名改为html,用浏览器打开即可。

看懂注释后,可以自己修改烟花的各个效果。我试过让烟花炸成了心型。:-)

<!DOCTYPE html>
<html dir="ltr" lang="zh-CN">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
    <title>HTML5 Canvas烟花特效 场景十分华丽在线演示</title>
    <style>
        /* basic styles for black background and crosshair cursor */
        body {
            background: #000;
            margin: 0;
        }

        canvas {
            cursor: crosshair;
            display: block;
        }
    </style>

</head>
<body>
    <div style="text-align:center;clear:both">
        <script src="/gg_bd_ad_720x90.js" type="text/javascript"></script>
        <script src="/follow.js" type="text/javascript"></script>
    </div>
    <canvas id="canvas">Canvas is not supported in your browser.</canvas>
    <script>
        // 当动画在canvas上,最好使用requestAnimationFrame代替setTimeout和setInterval
        // 不支持在所有的浏览器,有时需要一个前缀,所以我们需要一个重置
        window.requestAnimFrame = (function () {
            return window.requestAnimationFrame ||
                        window.webkitRequestAnimationFrame ||
                        window.mozRequestAnimationFrame ||
                        function (callback) {
                            window.setTimeout(callback, 1000 / 60);
                        };
        })();

        // 设置基本变量
        var canvas = document.getElementById('canvas'),
                ctx = canvas.getContext('2d'),
                // 全屏幕尺寸
                cw = window.innerWidth,
                ch = window.innerHeight,
                // firework collection 烟花集合
                fireworks = [],
                // particle collection 爆炸粒子集合
                particles = [],
                // starting hue 开始色调
                hue = 120,
                // when launching fireworks with a click, too many get launched at once without a limiter, one launch per 5 loop ticks
                // 通过点击释放烟花没有限制,每5次循环释放一次
                limiterTotal = 5,
                limiterTick = 0,
                // this will time the auto launches of fireworks, one launch per 80 loop ticks
                // 自动发射80循环一次
                timerTotal = 20,
                timerTick = 0,
                mousedown = false,
                // mouse x coordinate  X坐标
                mx,
                // mouse y coordinate  Y坐标
                my;

        // set canvas dimensions 设置画布尺寸
        canvas.width = cw;
        canvas.height = ch;

        // now we are going to setup our function placeholders for the entire demo

        // get a random number within a range
        // 获取范围内随机数
        function random(min, max) {
            return Math.random() * (max - min) + min;
        }

        // calculate the distance between two points
        // 计算两点距离
        function calculateDistance(p1x, p1y, p2x, p2y) {
            var xDistance = p1x - p2x,
                    yDistance = p1y - p2y;
            return Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2));
        }

        // 创建烟花
        function Firework(sx, sy, tx, ty) {
            // actual coordinates  实际坐标
            this.x = sx;
            this.y = sy;
            // starting coordinates  开始坐标
            this.sx = sx;
            this.sy = sy;
            // target coordinates  目标坐标
            this.tx = tx;
            this.ty = ty;
            // distance from starting point to target  计算飞行距离
            this.distanceToTarget = calculateDistance(sx, sy, tx, ty);
            this.distanceTraveled = 0;
            // track the past coordinates of each firework to create a trail effect, increase the coordinate count to create more prominent trails
            // 追踪每个烟花经过的坐标,来创建一个跟踪效果,增加坐标数量来创造更多杰出的轨迹
            this.coordinates = [];
            this.coordinateCount = 3;
            // populate initial coordinate collection with the current coordinates
            // 填充初始坐标集合与当前坐标
            while (this.coordinateCount--) {
                this.coordinates.push([this.x, this.y]);
            }
            this.angle = Math.atan2(ty - sy, tx - sx); // 从x轴到指定坐标点(x, y)的角度(以弧度为单位)
            this.speed = 2;  // 速度
            this.acceleration = 1.05;  // 加速度
            this.brightness = random(50, 70);  // 亮度
            // circle target indicator radius
            // 圆形目标指示器半径
            this.targetRadius = 1;
        }

        // update firework  更新烟花
        Firework.prototype.update = function (index) {
            // remove last item in coordinates array 删除烟花坐标数组里最后一个坐标
            this.coordinates.pop();
            // add current coordinates to the start of the array 当前的坐标添加到烟花坐标数组的开始
            this.coordinates.unshift([this.x, this.y]);

            // 循环圆形目标指示器半径
            if (this.targetRadius < 8) {
                this.targetRadius += 0.3;
            } else {
                this.targetRadius = 1;
            }

            // speed up the firework  烟花速度
            this.speed *= this.acceleration;

            // get the current velocities based on angle and speed
            // 获取x,y两个方向的速度
            var vx = Math.cos(this.angle) * this.speed,
                    vy = Math.sin(this.angle) * this.speed;
            // how far will the firework have traveled with velocities applied?
            // 烟花会随速度飞行多远
            this.distanceTraveled = calculateDistance(this.sx, this.sy, this.x + vx, this.y + vy);

            // if the distance traveled, including velocities, is greater than the initial distance to the target, then the target has been reached
            // 如果距离, 包括速度大于初始距离的目标, 那么目标已经达到
            if (this.distanceTraveled >= this.distanceToTarget) {
                createParticles(this.tx, this.ty); // 创建粒子
                // remove the firework, use the index passed into the update function to determine which to remove
                // 删除烟花,使用索引传递到更新函数来确定删除
                fireworks.splice(index, 1);
            } else {
                // target not reached, keep traveling 目标没有达到,继续飞行
                this.x += vx;
                this.y += vy;
            }
        }

        // draw firework 绘制烟花
        Firework.prototype.draw = function () {
            ctx.beginPath();
            // move to the last tracked coordinate in the set, then draw a line to the current x and y
            // 移动到最后一个跟踪坐标,然后画一条线到当前的x和y
            ctx.moveTo(this.coordinates[this.coordinates.length - 1][0], this.coordinates[this.coordinates.length - 1][1]);
            ctx.lineTo(this.x, this.y);
            // 色调(0或360表示红色,120表示绿色,240表示蓝色);饱和度;亮度
            ctx.strokeStyle = 'hsl(' + hue + ', 100%, ' + this.brightness + '%)';
            ctx.stroke();

            ctx.beginPath();
            // draw the target for this firework with a pulsing circle
            // 使用圆跳动画这个烟花的目标点
            ctx.arc(this.tx, this.ty, this.targetRadius, 0, Math.PI * 2); // 画圆
            ctx.stroke();
        }

        // create particle  创建爆炸粒子
        function Particle(x, y) {
            this.x = x;
            this.y = y;
            // track the past coordinates of each particle to create a trail effect, increase the coordinate count to create more prominent trails
            // 追踪每个爆炸粒子经过的坐标,来创建一个跟踪效果,增加坐标数量来创造更多杰出的轨迹
            this.coordinates = [];
            this.coordinateCount = 5;
            while (this.coordinateCount--) {
                this.coordinates.push([this.x, this.y]);
            }
            // set a random angle in all possible directions, in radians
            // 在所有可能的方向,设置一个随机角弧度
            this.angle = random(0, Math.PI * 2);
            this.speed = random(1, 10);
            // friction will slow the particle down 摩擦会使粒子慢下来
            this.friction = 0.95;
            // gravity will be applied and pull the particle down  重力将应用粒子拉下来
            this.gravity = 2;
            // set the hue to a random number +-20 of the overall hue variable
            // 在烟花色度范围内随机取爆炸粒子色度
            this.hue = random(hue - 50, hue + 50);
            this.brightness = random(50, 80);
            this.alpha = 1;
            // set how fast the particle fades out  设置粒子消失的速度
            this.decay = random(0.015, 0.03);
        }

        // update particle  更新爆炸粒子
        Particle.prototype.update = function (index) {
            // remove last item in coordinates array  删除爆炸粒子坐标数组里最后一个坐标
            this.coordinates.pop();
            // add current coordinates to the start of the array  当前的坐标添加到爆炸粒子坐标数组的开始
            this.coordinates.unshift([this.x, this.y]);
            // slow down the particle  减缓粒子
            this.speed *= this.friction;
            // apply velocity
            this.x += Math.cos(this.angle) * this.speed;
            this.y += Math.sin(this.angle) * this.speed + this.gravity;
            // fade out the particle 粒子消失参数
            this.alpha -= this.decay;

            // remove the particle once the alpha is low enough, based on the passed in index
            // 删除粒子,使用索引传递到更新函数来确定删除
            if (this.alpha <= this.decay) {
                particles.splice(index, 1);
            }
        }

        // draw particle  绘制爆炸粒子
        Particle.prototype.draw = function () {
            ctx.beginPath();
            // move to the last tracked coordinates in the set, then draw a line to the current x and y
            // 移动到最后一个坐标跟踪设置,然后画一条线到当前的x和y
            ctx.moveTo(this.coordinates[this.coordinates.length - 1][0], this.coordinates[this.coordinates.length - 1][1]);
            ctx.lineTo(this.x, this.y);
            // 色调(0或360表示红色,120表示绿色,240表示蓝色);饱和度;亮度
            ctx.strokeStyle = 'hsla(' + this.hue + ', 100%, ' + this.brightness + '%, ' + this.alpha + ')';
            ctx.stroke();
        }

        // create particle group/explosion  创建爆炸粒子组
        function createParticles(x, y) {
            // increase the particle count for a bigger explosion, beware of the canvas performance hit with the increased particles though
            // 增加粒子数创造更大的爆炸,谨防增加粒子造成canvas与性能影响
            var particleCount = parseInt(random(20, 50));
            while (particleCount--) {
                particles.push(new Particle(x, y));
            }
        }

        // 主循环
        function loop() {
            // this function will run endlessly with requestAnimationFrame
            // 这个函数将无限运行requestAnimationFrame
            requestAnimFrame(loop);

            // increase the hue to get different colored fireworks over time
            // 增加颜度,随着时间的推移得到不同颜色的烟火
            hue += random(0.5,10);

            // normally, clearRect() would be used to clear the canvas
            // we want to create a trailing effect though
            // setting the composite operation to destination-out will allow us to clear the canvas at a specific opacity, rather than wiping it entirely
            // 设置复合操作destination-out将使我们能够把画布清理在在一个特定的不透明度,而不是完全抹去它
            ctx.globalCompositeOperation = 'destination-out'; // 在源图像外显示目标图像。只有源图像外的目标图像部分会被显示,源图像是透明的。
            // decrease the alpha property to create more prominent trails
            // 降低alpha属性来创建更加显著的轨迹(0.5是透明度)
            ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
            ctx.fillRect(0, 0, cw, ch);
            // change the composite operation back to our main mode  改变复合操作回到我们的主要模式
            // lighter creates bright highlight points as the fireworks and particles overlap each other
            ctx.globalCompositeOperation = 'lighter'; // 显示源图像 + 目标图像。

            // loop over each firework, draw it, update it
            // 循环绘制烟花
            var i = fireworks.length;
            while (i--) {
                fireworks[i].draw();
                fireworks[i].update(i);
            }

            // 循环绘制粒子
            var i = particles.length;
            while (i--) {
                particles[i].draw();
                particles[i].update(i);
            }

            // launch fireworks automatically to random coordinates, when the mouse isn't down
            // 当鼠标不点击,自动随机坐标发射烟火
            if (timerTick >= timerTotal) {
                if (!mousedown) {
                    var sw = random(0, cw);
                    // start the firework at the bottom middle of the screen, then set the random target coordinates, the random y coordinates will be set within the range of 

the top half of the screen
                    // 启动烟花在屏幕中间的底部, 然后设置随机目标坐标, 随机y坐标将范围内的屏幕的上半部分
                    fireworks.push(new Firework(sw, ch, sw, random(0, ch / 2)));
                    timerTick = 0;
                }
            } else {
                timerTick++;
            }

            // limit the rate at which fireworks get launched when mouse is down
            // 限制鼠标点击时烟花发射的速度
            if (limiterTick >= limiterTotal) {
                if (mousedown) {
                    // start the firework at the bottom middle of the screen, then set the current mouse coordinates as the target
                    // 启动烟花在屏幕中间的底部,然后将鼠标当前坐标设置为目标
                    fireworks.push(new Firework(mx, ch, mx, my));
                    limiterTick = 0;
                }
            } else {
                limiterTick++;
            }
        }

        // mouse event bindings
        // update the mouse coordinates on mousemove
        // 更新鼠标当前坐标
        canvas.addEventListener('mousemove', function (e) {
            mx = e.pageX - canvas.offsetLeft;
            my = e.pageY - canvas.offsetTop;
        });

        // toggle mousedown state and prevent canvas from being selected
        // 切换mousedown状态,防止canvas被选中
        canvas.addEventListener('mousedown', function (e) {
            e.preventDefault();
            mousedown = true;
        });

        canvas.addEventListener('mouseup', function (e) {
            e.preventDefault();
            mousedown = false;
        });

        // 登录启动
        window.onload = loop;
    </script>
</body>
</html>
原文地址:https://www.cnblogs.com/ariter/p/5983370.html