天天看點

❤ ❤分享一個WEB前端canvas滑鼠滑過星空背景特效超好看❤ ❤一、實作效果二、代碼

一、實作效果

❤ ❤分享一個WEB前端canvas滑鼠滑過星空背景特效超好看❤ ❤一、實作效果二、代碼

二、代碼

<!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>canvas--again</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
        html,body {
            width: 100%;
            height: 100%;
            /* background: linear-gradient(to top, palevioletred 0%, #fff 100%); */
            background: no-repeat linear-gradient(to bottom, #011a39 0%, #009d7f 100%);
        }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
</body>
<script>
    (function(win, doc) {
        const maxW = win.innerWidth;
        const maxH = win.innerHeight;
        const maxSize = 5;
        const minSize = 1;
        let isMoving = false;
        let timer = null;

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

        let stars = [];      // 存放作為背景使用的星星
        let move_stars = []; // 存放滑鼠移動時繪制的星星

        function CanvasStar(num) {
            this.num = num;
        };

        CanvasStar.prototype = {
            render() {
                for (let i = 0; i < this.num; i++) {
                    let alpha = Math.random() + 0.1;
                    stars[i] = new Star(i, getOneRandom(maxW), getOneRandom(maxH), getOneRandom(maxSize, minSize), true, alpha);
                }
                animate();
            }
        };

        function Star(id, x, y, r, isCache, dot_alpha) {
            this.id = id;
            this.x = x;
            this.y = y;
            this.r = r;
            this.dot_alpha = dot_alpha;
            this.show = .5;    // 作用:控制 滑鼠繪制點的隐藏
            this.direct = getOneRandom(180) + 180;
            this.isCache = isCache;
            this.cacheCanvas = doc.createElement('canvas');
            this.ctx_cache = this.cacheCanvas.getContext('2d');
            if(isCache) {
                this.cache();
            }
        };

        Star.prototype = {

            draw() {
                // 繪制一個Star
                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.closePath();
                    
                    ctx.shadowColor = '#fff';
                    ctx.shadowBlur = 2 * this.r;
                    ctx.fillStyle = `rgba(255, 255, 255, ${ this.dot_alpha })`;
                    ctx.fill();
                    ctx.restore();
                }else {
                    ctx.drawImage(this.cacheCanvas, this.x - 3 * this.r, this.y - 3 * this.r)
                }
            },

            move() {
                this.y -= 0.25;
                if(this.y < -10) {
                    this.y = maxH + 10;
                }
                this.draw();
            },

            // 使滑鼠繪制的點抖動起來
            shake(arr) {
                this.show -= 0.01;
                if(this.show < 0) {
                    return;
                }
                let speed = .5;
                this.x += Math.cos(this.direct * Math.PI / 180) * speed;
                this.y += Math.sin(this.direct * Math.PI / 180) * speed;
                this.draw();
                this.link();
            },

            link() {
                if(!this.id) return;
                let len = move_stars.length;
                // 關鍵思想:取目前id,之前的4個點,每繪制一次就向前取4個點,以此類推
                // 而不是move_stars最後的四個點,否則的話隻有最後幾個點會被連接配接起來
                let arr = move_stars.slice(this.id - 3, this.id);
                let endIdx = arr.length - 1;

                ctx.save();
                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(i != endIdx && !!arr[i] && arr[i].show > 0) ctx.lineTo(arr[i].x, arr[i].y);
                }
                ctx.closePath();

                ctx.strokeStyle = 'rgba(255, 255, 255, 0.125)';
                ctx.stroke();
                ctx.restore();
            },
            cache() {
                this.cacheCanvas.width = 6 * this.r;
                this.cacheCanvas.height = 6 * this.r;

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

                this.ctx_cache.shadowColor = '#fff';
                this.ctx_cache.shadowBlur = 2 * this.r;
                this.ctx_cache.fillStyle = `rgba(255, 255, 255, ${this.dot_alpha})`;
                this.ctx_cache.fill();
                this.ctx_cache.restore();
            }
        };

        // 動畫
        function animate() {
            ctx.clearRect(0, 0, maxW, maxH);
            let len = stars.length;
            for(let i = 0; i < len; i++) {
                stars[i].move();
            }

            let len2 = move_stars.length;
            if(isMoving) {
                for(let i = 0; i < len2; i++) {
                    if(move_stars[i]) move_stars[i].shake(move_stars);
                }
            }else {
                move_stars = []
            }
            requestAnimationFrame(animate);
        };

        // 擷取區間内的随機數
        function getOneRandom(max, min = 0) {
            return Math.floor(Math.random() * (max - min) + min);
        };

        // 擷取正負号
        function getSign() {
            return Math.random() > .5 ? -1 : 1;
        };

        // 滑鼠移動事件
        doc.addEventListener('mousemove', function(e) {
            let x = e.clientX, y = e.clientY;
            // 控制繪制密度 以及點之間的距離    兩個重要的點
            // 密度是控制繪制的數量     dis_x = Math.abs(x - pre_star.x);
            // 距離是在已繪制的點基礎上、控制點的間距

            // 控制繪制密度 和 控制點之間的距離  不是一個功能哦(需要實際操作去體會, 文字很難表述~.~)
            // 沒有控制距離的話 繪制的圖形,太規則了,不夠分散    x = x + getSign() * getOneRandom(50)

            let len = move_stars.length;
            let dis_x, dis_y;
            if(!len) {
                move_stars.push( new Star(len, x, y, getOneRandom(maxSize, 3), true, 1) );
            }

            if(move_stars[len - 1]) {
                // 目前的星還沒有push到move_stars裡,是以上個星是move_stars[len - 1]
                let pre_star = move_stars[len - 1];
                if(pre_star) {
                    dis_x = Math.abs(x - pre_star.x);
                    dis_y = Math.abs(y - pre_star.y);
                }
                x = x + getSign() * getOneRandom(50);
                y = y + getSign() * getOneRandom(50);
                if(dis_x > 5 && dis_y > 5) move_stars.push( new Star(len, x, y, getOneRandom(maxSize, minSize), true, 1) );
            }

            isMoving = true;
            clearInterval(timer);   // 清除上一次的定時器(此時還沒觸發)
            timer = setInterval(function() {
                isMoving = false;
                clearInterval(timer);    // 滑鼠停止再清除下定時器
            }, 500)

        }, false);

        window.CanvasStar = CanvasStar;
    })(window, document);

    let canvasStar = new CanvasStar(200);
    canvasStar.render();
</script>


<script>
/**小本本
 * 疑問:
 *   1. clientX、layerX、offsetX、pageX、screenX、x 的差別?
*/
</script>
</html>
           
上一篇: 小明的煩惱
下一篇: MFC電腦地震

繼續閱讀