sunshine940326 / canvasStar

自己写的一个少女心满满的canvasStar特效
137 stars 68 forks source link

佩服妹子能写出这么牛逼的Canvas效果 #2

Open joyjoe opened 7 years ago

joyjoe commented 7 years ago

先膜拜一下妹子

提几点建议

  1. 代码应该精简 既然都用到构造函数了,那就应该把draw和cache里面绘制圆点的代码封装成一个原型链上的函数。 绘制多点之间连线的代码也可以通过循环来精简。另外在”点“对象代码中,有一些冗余的context属性设置,比如alpha等等。还可以考虑将Star和Dot的关系整合成父子继承关系,优化代码结构和提高性能。
  2. 配置项可以扩展 可以多设置些配置项,这样能够更好体现个性化。比如:颜色,多点连线的条数。 颜色值,可以放入配置中,这样不用局限于默认的白色。多点之间连线的条数,可以方便用循环代码来实现。不用像现在这样,写出毫无扩展性的代码。
  3. 扩展思维 在重绘函数drawIfMouseMoving中,是否可以考虑通过判断用户鼠标移动方向来设置Variable这样的偏移值方向,而不是目前这种随机方向。

下面把我个人优化后的代码贴出来,有错误欢迎指正。

joyjoe commented 7 years ago

;(function(global, factory){ "use strict"; if(typeof module === "object" && typeof module.exports === "object"){ // Node.js module.exports = global.document?factory(global, true):function(w){ if(!w.document){ throw new Error("require a window with a document"); } return factory(w); } }else{ factory(global); } })(typeof window !== "undefined"?window:this, function(window, noGlobal){ "use strict"; var config = { star_r: 3,//star半径 star_alpha: 10, //5,//star透明度 initPopulation: 150,//star总个数 moveDistance: 0.25,//star移动距离 dot_r: 5,//dot半径 dot_speeds: 0.5,//dot速度 dot_alpha: 0.5,//dot透明度 dot_aReduction: 0.01,//dot消失透明度条件 dotsMinDistance: 5,//dot之间最短距离 dotsMaxDistance: 50,//dot之间最长距离 linkCount: 5 };

var stars = [],
    dots = [],
    canvas = null,
    ctx = null,
    WIDTH = document.documentElement.clientWidth,
    HEIGHT = document.documentElement.clientHeight,
    mouseMoving = false,
    mouseMoveChecker,
    mouseX,
    mouseY;

function StarGlitter(container, obj){
    function init(){
        var _node = null;
        if(typeof(container) === "string"){
            if(container[0]==="."){
                _node = document.getElementsByClassName(container.substr(1))[0];
            }else if(container[0]==="#"){
                _node = document.getElementById(container.substr(1));
            }
        }else if(container instanceof HTMLElement){
            _node = container;
        }
        canvas = _node;
        canvas.setAttribute("width", WIDTH);
        canvas.setAttribute("height", HEIGHT);
        ctx = canvas.getContext("2d");
        // config
        if(obj instanceof Object){
            for(var item in obj){
                config[item] = obj[item];
            }
        }
        ctx.strokeStyle = "white";
        ctx.shadowColor = "white";
        for(var i = 0; i < config.initPopulation; i++){
            stars[i] = new Star(i, Math.floor(Math.random() * WIDTH), Math.floor(Math.random() * HEIGHT), true);
        }
    }
    function animate(){
        ctx.clearRect(0,0,WIDTH,HEIGHT);
        for(var i in stars){
            stars[i].move();
        }
        for(var i in dots){
            dots[i].move();
        }
        renderDraw();
        requestAnimationFrame(animate);
    }
    document.onmousemove = function(e){

            mouseMoving = true;
            mouseX = e.clientX;
            mouseY = e.clientY;
            clearInterval(mouseMoveChecker);
            mouseMoveChecker = setInterval(function(){
                mouseMoving = false;
            }, 20);
    }
    init();
    animate();
};

// 绘制star
function Star(id, x, y, useCache){
    this.init(id, x, y, useCache);
}

Star.prototype = {
    constructor: Star,
    init: function(id, x, y, useCache){
        this.id = id;
        this.x = x;
        this.y = y;
        this.useCache = useCache;
        this.cacheCanvas = document.createElement("canvas");
        this.cacheCtx = this.cacheCanvas.getContext("2d");
        this.r = Math.floor(Math.random() * config.star_r) + 1;
        this.cacheCtx.width = this.r * 6;
        this.cacheCtx.height = this.r * 6;
        var alpha = (Math.floor(Math.random() * 10) + 1) / config.star_alpha;
        this.color = "rgba(255,255,255,"+ alpha +")";
        if(this.useCache){
            this.cache();
        }else{
            this.draw();
        }
    },
    _render: function(ctx, x, y, r, start, end, flag, color){
        ctx.save();
        ctx.fillStyle = color || this.color;
        ctx.shadowColor = "white";
        ctx.shadowBlur = r * 2;
        ctx.beginPath();
        ctx.arc(x, y, r, start, end, flag);
        ctx.closePath();
        ctx.fill();
        ctx.restore();
    },
    draw: function(){
        if(this.useCache){
            ctx.drawImage(this.cacheCanvas, this.x - this.r, this.y - this.r);
        }else{
            this._render(ctx, this.x, this.y, this.r, 0, Math.PI * 2, false);
        }
    },
    cache: function(){
        this._render(this.cacheCtx, this.r * 3, this.r * 3, this.r, 0, Math.PI * 2, false);
    },
    move: function(){
        this.y -= config.moveDistance;
        if(this.y <= -10){
            this.y += (HEIGHT + 10);
        }
        this.draw();
    },
    die: function(){
        stars[this.id] = null;
        delete stars[this.id];
    }
}

function Dot(id, x, y, useCache){
    this.init(id, x, y, useCache);
}

Dot.prototype = {
    constructor: Dot,
    init: function(id, x, y, useCache){
        this.id = id;
        this.x = x;
        this.y = y;
        this.r = Math.floor(Math.random() * config.dot_r) + 1;
        this.speed = config.dot_speeds;
        this.a = config.dot_alpha;
        this.aReduction = config.dot_aReduction;
        this.useCache = useCache;
        this.dotCanvas = document.createElement("canvas");
        this.dotCtx = this.dotCanvas.getContext("2d");
        this.dotCtx.width = this.r * 6;
        this.dotCtx.height = this.r * 6;
        this.color = "rgba(255,255,255,"+ this.a +")";
        this.dir = Math.floor(Math.random() * 140) + 200;
        this.linkCount = config.linkCount;
        if(useCache){
            this.cache();
        }else{
            this.draw();
        }
    },
    _render: function(ctx, x, y, r, start, end, flag, color){
        ctx.save();
        ctx.fillStyle = color || this.color;
        ctx.shadowColor = "white";
        ctx.shadowBlur = r * 2;
        ctx.beginPath();
        ctx.arc(x, y, r, start, end, flag);
        ctx.closePath();
        ctx.fill();
        ctx.restore();
    },
    draw: function(){
        if(!this.useCache){
            this._render(ctx, this.x, this.y, this.r, 0, Math.PI *2 , false);
        }else{
            ctx.drawImage(this.dotCanvas, this.x - this.r * 3, this.y - this.r * 3);
        }
    },
    cache: function(){
        this._render(this.dotCtx, this.r * 3, this.r * 3, this.r, 0, Math.PI *2 , false);
    },
    link: function(){
        if(this.id == 0){
            return;
        }
        // 可以考虑其他连线策略
        ctx.beginPath();
        ctx.strokeStyle = this.linkColor;
        for(var i = 0; i <= this.linkCount; i++){
            var previousDot = getPreviousDot(this.id, i + 1);
            if(previousDot){
                if(i == 0){
                    ctx.moveTo(previousDot.x, previousDot.y);
                    ctx.lineTo(this.x, this.y);
                }else{
                    ctx.lineTo(previousDot.x, previousDot.y);
                }
            }else{
                break;
            }
        }
        ctx.stroke();
        ctx.closePath();
    },
    move: function(){
        this.a -=this.aReduction;
        if(this.a <= 0){
            this.die();
            return;
        }
        this.color = "rgba(255,255,255,"+ this.a +")";
        this.linkColor = "rgba(255,255,255,"+ this.a/4 +")";
        this.x = this.x + Math.cos(Math.PI/180*this.dir) * this.speed;
        this.y = this.y + Math.cos(Math.PI/180*this.dir) * this.speed;

        this.draw();
        this.link();
    },
    die: function(){
        dots[this.id] = null;
        delete dots[this.id];
    }
}

function renderDraw(){
    if(dots.length == 0){
        dots[0] = new Dot(0, mouseX, mouseY, true);
        dots[0].draw();
        return;
    }

    var previousDot = getPreviousDot(dots.length, 1);
    var prevX = previousDot.x;
    var prevY = previousDot.y;

    var diffX = Math.abs(prevX - mouseX);
    var diffY = Math.abs(prevY - mouseY);
    if(diffX < config.dotsMinDistance || diffY < config.dotsMinDistance){
        return;
    }
    // 下一个点的位置可以考虑加入鼠标移动方向的因素
    var xVariation = Math.random() > 0.5?-1:1;
    xVariation = xVariation * Math.floor(Math.random() * config.dotsMaxDistance) + 1;
    var yVariation = Math.random() > 0.5?-1:1;
    yVariation = yVariation * Math.floor(Math.random() * config.dotsMaxDistance) + 1;
    dots[dots.length] = new Dot(dots.length, mouseX+xVariation, mouseY+yVariation, true);
    dots[dots.length - 1].draw();
    dots[dots.length - 1].link();
}

function getPreviousDot(id, stepback){
    if(id == 0 || id - stepback < 0){
        return false;
    }
    if(typeof dots[id - stepback] !== "undefined"){
        return dots[id - stepback];
    }else{
        return false;
    }
}

if(!noGlobal){
    window.StarGlitter = StarGlitter;
}
return StarGlitter;

});