canvas--案例(星)


沒事的時候在看些canvas方面的文章,並寫了一兩個小demo。。下面的代碼就是其中之一

(感興趣的先去看小姐姐的原文吧,從canvas基礎講起且非常詳細,真的很不錯o.o 小姐姐好像是餓了么的大神來着。。。仰望~~)

https://juejin.im/post/5986d6e1f265da3e241e8cdb(尊重原創)

如果在你只想要看代碼,個人感覺自己寫的要比小姐姐的原碼要清晰簡潔點(lian pi 真特么厚。。。哈哈哈哈h)

Talk is cheap, show me the code

總結:(寫在前面吧)

  在使用緩存Canvas優化Star移動時,我的做法是在cache方法中創建緩存畫布換完star將畫布對象return出來,初衷是為了降低代碼的復雜度,將代碼模塊化;但是好像動畫卡了(應該是和每次都創建畫布有關。。。~~~~(>_<)~~~~),最后還是和原代碼一樣在全局上創建緩存畫布(這樣寫動畫效果很流暢,唉之后再想想如何簡化代碼寫法吧^_^切。。就是偷懶。。哼!)

一點建議:(對自己說的。。)

  在借鑒和學習別人的案例時,最好將整體效果或功能,划分成各個小塊功能去逐一實現;對於某一小塊功能的實現最好先自己思考如何實現(反正也不是讓你一下想好實現全部功能,

感覺原作者自己寫的時候也不是一蹴而就的吧),功能實現后再和原代碼相互印證(不同思想的碰撞bulabula。。。)學習大多數知識時也多是如此吧;

引以為戒:(批斗大會。。)

  剛開始敲代碼的時候,總是想着如何快速實現全部效果(。。。干巴巴的,麻麻賴賴的,一點都不圓圓潤,盤他~~~^_^),沒有用自己掌握的canvas基礎知識去想如何實現功能,反而在很多地方卡殼了(不是很關鍵的地方糾結)花費的時間更長了;

  同一個功能的代碼,要相信自己思考后所寫的,不一定比原代碼差哦(獨立思考很重要),不要總是糾結為什么和別人的不一樣。。。其實原代碼里有很多地方變量的對應以及函數的聲明是不必要的而且有點雜亂,強迫症不能忍~~(所以相信自己用心寫的代碼)。

對於工作的反思(懶得再寫一篇博客了。。就放着了,怎樣~.~hhhh)

  之前一直覺得只要把產品方提的需求做出來就大吉大利今晚吃雞了,即使是之前沒做過的或者不懂的(不要緊,可以搬前輩大神們的嘛。。),實際工作中遇到問題多是如此,這樣功能或效果同樣達到了要求(也僅僅是達到要求),但自己並沒有規划如何開發,導致所寫的代碼真的是不忍直視(很za亂~~),因為多數有點東平西湊的嫌疑,感覺不是自己在馭使代碼,更多的是個被駕馭的角色。。。

  希望自己在以后的板磚中,多花點時間去規划如何去做,戒驕戒躁a le。。。

/**

* 敲代碼的時候有個缺點,遇到一些代碼寫法時,總是想着為什么這樣寫、憑什么要這樣寫、有沒有其他的寫法了bulabula。。。盤他~。~

* 毫無疑問這種刨根問底的學習是很好的優點,但是從遇到疑問到查閱資料解決疑問是需要時間成本的。

* 假如說你只是遇到一兩個疑問而恰巧通過查閱資料又快速的解決了它,那么很幸運你又get到了新知識,替你高興。。。

* 但實際搬磚的過程中你會遇到很多的疑問,並且查閱資料也不是這么快可以解決的,而此時你還非要去全部解決掉(一根筋。。。),查啊查..

* 最后花費大量的時間(最氣人的是可能疑問並沒有解決掉。。嗚嗚嗚...),

* 人的精力是有限的,本來雄赳赳氣昂昂的准備搬磚來着,最后卻被這樣那樣的問題所困擾,磨滅了興致導致不了了之,

* 雖然會有些收獲但是這種付出和收獲,顯然不是最優的方式,所以在搬磚的時候不要過於糾結,還沒做出來就想着怎么去優化,哼!。。就是耍流氓(不如先定他個小目標----一鼓作氣把功能盤出來再說),

* 把疑問先找個地方記下來(' 小本本 ' 了解一下。。。哈哈哈),等功能全部實現再回過頭來解決掉'小本本'上記得內容,

* 最后功德圓滿--成功禿頂--步入高級搬磚猿--迎娶心中女神--成為人生贏家。。。

*

*/

/**em。。。大家好!我是'小本本'(手動滑稽~)

* 疑問:

* 1. clientX、layerX、offsetX、pageX、screenX、x 的區別?。。。

*/

效果圖:(說那麽多廢話干嘛。。。有圖就別嗶嗶嗶)

 下面獻上代碼,歡迎各位大神review(害羞😳~。~)

<!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, #dcdcdc 0%, #000 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);
</script>
<script>
    let canvasStar = new CanvasStar(200);
    canvasStar.render();
</script>


<script>
/**小本本
 * 疑問:
 *   1. clientX、layerX、offsetX、pageX、screenX、x 的區別?
*/
</script>
</html>

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~已實踐 2019/01/23


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM