canvas--案例(星)

总结:
  在使用缓存Canvas优化Star移动时,我的做法是在cache方法中创建缓存画布换完star
再将画布对象return出来,初衷是为了降低代码的复杂度,将代码模块化;但是好像动画卡了
(应该是和每次都创建画布有关。。。~~~~(>_<)~~~~),最后还是和原代码一样在全局上创
建缓存画布(这样写动画效果很流畅,唉之后再想想如何简化代码写法吧^_^)
 
效果图:(感兴趣的还是去看小姐姐的原文吧,从canvas基础讲起很详细,真的很不错哦o.o)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>canvasStar</title>
    <style>
        html,body {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            overflow: hidden;
            /* palevioletred */
            background: linear-gradient(to bottom, #dcdcdc 0%, #000 100%);
        }
        /* .filter {
            width: 100%;
            height: 100%;
            position: absolute;
            top: 0;
            left: 0;
            background: #fe5757;
            animation: colorChange 20s ease-in-out infinite;
            animation-fill-mode: both;
            mix-blend-mode: overlay;
        }
        @keyframes colorChange {
            0%, 100% {
                opacity: 0;
            }
            50% {
                opacity: .7;
            }
        } */
    </style>
</head>
<body>
    <div class="filter"></div>
    <canvas id="canvas"></canvas>
</body>
<script>
    ;(function(win, doc) {
        let maxW = win.innerWidth,
        maxH = win.innerHeight,
        maxSize = 4,
        starArr = [];

        let timer = null,
        isMoving = false,
        dotArr = [],
        mouseX = null,
        mouseY = null;

        let canvas = doc.getElementById('canvas');
        canvas.width = maxW;
        canvas.height = maxH;
        let ctx = canvas.getContext('2d');

        let CanvasStar = function(num) {
            this.num = num;
        };
        CanvasStar.prototype = {
            init: function() {
                ctx.strokeStyle = "white";
                ctx.shadowColor = "white";
                for(let i = 0; i < this.num; i++) {
                    starArr[i] = new Star(getOneRandom(maxW), getOneRandom(maxH), getOneRandom(maxSize), true);
                }
                animate();
            },

            // 绘制多边形
            polygon: function(sides) {
                // 根据边长数计算平均角度
                let step = Math.PI / sides * 2;

                ctx.beginPath();
                for (let index = 0; index < sides; index++) {
                    let x = 150 + 100 * Math.cos( index * step);
                    let y = 150 + 100 * Math.sin(index * step);
                    ctx.lineTo(x, y);
                }
                ctx.closePath();

                ctx.fillStyle = 'palevioletred';
                ctx.fill();
                ctx.strokeStyle = 'palevioletred';
                ctx.stroke();
            },
        };

        // 绘制star
        function Star(x, y, size, isCache) {
            this.x = x;
            this.y = y;
            this.r = size;
            this.isCache = isCache;
            this.cacheCanvas = doc.createElement('canvas');
            this.ctx_cache = this.cacheCanvas.getContext('2d');
            if(isCache) {
                this.cache();
            }
        };

        Star.prototype = {
            draw: function() {
                if(!this.isCache) {
                    let alpha = Math.random() + 0.1;
                    ctx.save();
                    ctx.beginPath();
                    ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false);
                    /**
                     * ctx.arc(x, y, radius, starAngle, endAngle, anticlockwise) 
                     * x : 圆心的 x 坐标 
                     * y:圆心的 y 坐标 
                     * radius : 半径 
                     * starAngle :开始角度 
                     * endAngle:结束角度 
                     * anticlockwise :是否逆时针(true)为逆时针,(false)为顺时针
                    */
                    ctx.closePath();

                    // shadowBlur:设置或返回用于阴影的模糊级别(值越大越模糊)
                    ctx.shadowColor = "#fff";
                    ctx.shadowBlur = this.r * 2;
                    ctx.fillStyle = `rgba(255, 255, 255, ${alpha})`;
                    ctx.fill();
                    ctx.restore();
                }else {
                    ctx.drawImage(this.cacheCanvas, this.x - this.r, this.y - this.r);
                }
            },

            cache: function() {
                // let cacheCanvas = doc.createElement('canvas');
                // let ctx_cache = cacheCanvas.getContext('2d');
                // 以模糊距离和圆本身的大小(2r + r = 3r)为参考,
                // 设置缓存画布的大小(2(2r + r)= 6r)直径)
                this.ctx_cache.width = this.r * 6;
                this.ctx_cache.height = this.r * 6;

                this.ctx_cache.save();
                this.ctx_cache.beginPath();
                this.ctx_cache.arc(this.r * 3, this.r * 3, this.r, 0, 2 * Math.PI);
                this.ctx_cache.closePath();

                this.ctx_cache.shadowColor = '#fff';
                this.ctx_cache.shadowBlur = this.r * 2;
                let alpha = Math.random() + 0.1;
                this.ctx_cache.fillStyle = `rgba(255, 255, 255, ${alpha})`;
                this.ctx_cache.fill();
                this.ctx_cache.restore();

                // return cacheCanvas;
            },

            move: function() {
                let speed = 0.25;
                this.y -= speed;    // 向上移动
                if(this.y <= -10) {
                    this.y += maxH + 10;    // 向下移动到距离画布下部边缘10像素的位置
                }
                this.draw();
            },

        };
        
        // 随鼠标移动绘制点
        function Dot(index, x, y, r, speed, isCache) {
            this.index = index;
            this.x = x;
            this.y = y;
            this.r = r;
            this.alpha = .5;
            this.dot_alpha = .5;
            this.speed = speed;
            this.dotCanvas = doc.createElement("canvas");
            this.dotCtx = this.dotCanvas.getContext("2d");
            this.dotCtx.alpha = .5;
            this.direct = getOneRandom(180) + 180;
            this.isCache = isCache;
            // 控制弧度值在 PI 到 2PI 之间(sin值为负保证y坐标向上移动,cos值正负均有 保证x坐标左右都可以移动)
            if(isCache) {
                this.cache()
            }
        }

        Dot.prototype = {
            draw: function() {
                if(!this.isCache) {
                    ctx.save();
                    ctx.beginPath();
                    ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI, false);
                    ctx.closePath();
                    
                    ctx.shadowColor = "#fff";
                    ctx.shadowBlur = this.r * 2;
                    ctx.fillStyle = `rgba(255, 255, 255, ${this.alpha})`;
                    ctx.fill();
                    ctx.restore();
                }else {
                    ctx.drawImage(this.dotCanvas, this.x - 3 * this.r, this.y - 3 * this.r);
                }
            },

            move: function() {
                this.dot_alpha -= 0.01;
                if(this.dot_alpha <= 0) {
                    this.die(this.index)
                    return      // 终止后面代码执行
                }
                this.x = this.x + Math.cos( this.direct * Math.PI / 180 ) * this.speed;
                this.y = this.y + Math.sin( this.direct * Math.PI / 180 ) * this.speed;
                this.draw();
                this.link();
            },

            link: function() {
                if(!this.index) return;
                // 取当前点的 前四个点
                let arr = dotArr.slice(this.index - 5, this.index);
                // bug代码:let arr = dotArr.slice(this.index - 4);(复制上去即可复现,绘制的线的颜色不一样)
                // 原因:应该是连接到当前点的线重复绘制,造成比其他的线要粗
                // bug终于解决了哈哈哈哈h,昨天怎么都搞不明白,今早一敲代码立马解决了~。~开心啊
                // 真是柳暗花明又了然一bug。。。痛快
                let endIdx = arr.length - 1;
                for (let i = endIdx; i >= 0; i--) {

                    if(i == endIdx && !!arr[endIdx]) {
                        ctx.moveTo(arr[endIdx].x, arr[endIdx].y);
                        ctx.beginPath();
                        ctx.lineTo(this.x, this.y)
                    }
                    if(!!arr[i] && i !== endIdx) ctx.lineTo(arr[i].x, arr[i].y);

                }
                
                ctx.strokeStyle = "rgba(255, 255, 255, 0.125)";
                ctx.stroke();
                ctx.closePath();
            },

            cache: function() {
                // 6 * this.r 原因参考Star中cache的解释
                this.dotCtx.width = 6 * this.r;
                this.dotCtx.height = 6 * this.r;

                this.dotCtx.save();
                this.dotCtx.beginPath();
                this.dotCtx.arc(this.r * 3, this.r * 3, this.r, 0, 2 * Math.PI, false);
                this.dotCtx.closePath();
                
                // this.dotCtx.alpha -= 0.01;
                this.dotCtx.shadowColor = '#fff';
                this.dotCtx.shadowBlur = 2 * this.r;
                this.dotCtx.fillStyle = `rgba(255, 255, 255, ${this.dotCtx.alpha})`;
                this.dotCtx.fill();
                this.dotCtx.restore();
            },

            die: function(index) {
                dotArr[index] = null;
                delete dotArr[index];
            }
        }

        function drawIfMouseMoving() {
            if(!isMoving) return;
            
            let r = 5, speed = 0.5;
            if(!dotArr.length) {
                dotArr[0] = new Dot(0, mouseX, mouseY, r, speed, true);
                dotArr[0].draw();
                return;
            }

            // 控制点的密度
            let previousDot = dotArr[dotArr.length - 1];
            if(!!previousDot) {
                let preX = previousDot.x;
                let preY = previousDot.y;
                let diffX = Math.abs(preX - mouseX);
                let diffY = Math.abs(preY - mouseY);
                if(diffX < 5 || diffY < 5) return;
            }

            let maxDist = 50;
            let x = mouseX + getSign() * getOneRandom(50);
            let y = mouseY + getSign() * getOneRandom(50);
            dotArr[dotArr.length] = new Dot(dotArr.length, x, y, r, speed, true);
            dotArr[dotArr.length - 1].draw();
            dotArr[dotArr.length-1].link();
        };

        // 动画
        function animate() {
            ctx.clearRect(0, 0, maxW, maxH);
            for(let i in dotArr) {
                dotArr[i].move();
            }
            for(let i in starArr) {
                starArr[i].move();
            }
            drawIfMouseMoving();
            requestAnimationFrame(animate);
        };

        // 获取一个随机数
        function getOneRandom(max, min = 0) {
            return Math.floor(Math.random() * (max - min) + min);
        };

        // 获取正负号
        function getSign() {
            return Math.random() >= .5 ? -1 : 1;
        };
        win.CanvasStar = CanvasStar;

        doc.onmousemove = function(e) {
            isMoving = true;
            mouseX = e.clientX;
            mouseY = e.clientY;

            clearInterval(timer);   // 清除上一次的定时器(此时还没触发)
            timer = setInterval(function() {
                isMoving = false;
                clearInterval(timer); // 鼠标停止再清除下定时器
            }, 1000);
        };
        doc.ontouchmove = function(e) {
            isMoving = true;
            mouseX = e.clientX;
            mouseY = e.clientY;

            clearInterval(timer);   // 清除上一次的定时器(此时还没触发)
            timer = setInterval(function() {
                isMoving = false;
                clearInterval(timer); // 鼠标停止再清除下定时器
            }, 1000);
        };
    })(window, document);
</script>
<script>
    let num = 240;
    let canvasStar = new CanvasStar(num);
    // canvasStar.polygon(5); // 五边形
    canvasStar.init();

    /**总结:
     *      在使用缓存Canvas优化Star移动时,我的做法是在cache方法中创建缓存画布换完star
     * 再将画布对象return出来,初衷是为了降低代码的复杂度,将代码模块化;但是好像动画卡了
     * (应该是和每次都创建画布有关。。。~~~~(>_<)~~~~),最后还是和原代码一样在全局上创
     * 建缓存画布(这样写动画效果很流畅,唉之后再想想如何简化代码写法吧^_^)
    */
</script>
</html>

猜你喜欢

转载自www.cnblogs.com/Evermenot/p/10282941.html
今日推荐