用canvas實現流星特效

wengjq發表於2019-03-01

最近給公司網站banner做了流星特效,踩了一些坑,在這裡mark下。

(function () { 
    var context;//畫布上下文
    var boundaryHeight;//畫布高,邊界值
    var boundaryWidth;//畫布寬,邊界值
    var starArr = [];
    var meteorArr = [];
    var STAR_COUNT = 500;//星星數,常量
    var METEOR_COUNT = 4;//流星數,常量
    var METEOR_SEPARATE = 300; //流星之間間隔,常量
    var meteorCoordinateArr = [];//存所有流星的座標陣列
    var playMeteorTimeout;
    var playStarsTimeout;

    //初始化畫布及context
    function init(container) {
        starArr = [];
        meteorArr = [];

        var canvas = document.createElement("canvas");
        container.appendChild(canvas);

        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;

        boundaryHeight = canvas.height;
        boundaryWidth =  canvas.width;

        //獲取context
        context = canvas.getContext("2d");
        context.fillStyle = "black";

        //畫星星
        for (var i = 0; i < STAR_COUNT; i++) {
            var star = new Star();
            star.init();
            star.draw();
            starArr.push(star);
        }
        //畫流星
        for (var j = 0; j < METEOR_COUNT; j++) {
            var rain = new MeteorRain();
            rain.init(j);
            rain.draw();
            meteorArr.push(rain);
        }

        playStars();//星星動起來
        playMeteor();//流星動起來
    }

    //建立一個星星物件
    var Star = function () {
        this.x = boundaryWidth * Math.random();//橫座標
        this.y = boundaryHeight * Math.random();//縱座標
        this.color = "";//星星顏色
    };

    Star.prototype = {
        constructor: Star,
        //初始化
        init: function () {
            this.getColor();
        },
        //產生隨機顏色
        getColor: function () {
            var _randomNum = Math.random();

            if (_randomNum < 0.5) {
                this.color = "gray";
            }
            else {
                this.color = "white";
            }

        },
        //繪製
        draw: function () {
            context.beginPath();
            //畫圓點
            context.arc(this.x, this.y, 0.05, 0, 2 * Math.PI);
            context.strokeStyle = this.color;
            context.stroke(); 
            context.closePath();
        }  
    }

    //星星閃起來
    function playStars() {
        for (var n = 0; n < STAR_COUNT; n++) {  
            starArr[n].getColor();  
            starArr[n].draw();  
        }  

        clearTimeout(playStarsTimeout);
        playStarsTimeout = setTimeout(playStars, 200);
    }


    //建立一個流星物件
    var MeteorRain = function () {
        this.x = -1;//流星的橫座標
        this.y = -1;//流星的縱座標
        this.length = -1;//流星的長度
        this.angle = 30; //傾斜角度
        this.width = -1;//流星所佔寬度,及矩形的寬度
        this.height = -1;//流星所佔高度,及矩形的高度
        this.speed = 1;//速度
        this.offset_x = -1;//橫軸移動偏移量
        this.offset_y = -1;//縱軸移動偏移量
        this.alpha = 1; //透明度
    };

    MeteorRain.prototype = {
        constructor: MeteorRain,
        //初始化
        init: function (i) {
            this.alpha = 1;//透明度
            this.angle = 30; //流星傾斜角
            this.speed = Math.ceil(Math.random() + 0.5); //流星的速度

            var x = Math.random() * 80 + 180;
            var cos = Math.cos(this.angle * 3.14 / 180);
            var sin = Math.sin(this.angle * 3.14 / 180) ;

            this.length = Math.ceil(x);//流星長度

            this.width = this.length * cos;  //流星所佔寬度,及矩形的寬度
            this.height = this.length * sin; //流星所佔高度,及矩形的高度
            this.offset_x = this.speed * cos * 3.5;
            this.offset_y = this.speed * sin * 3.5;

            this.getPos(i);
        },
        //計算流星座標
        countPos: function () {
            //往左下移動,x減少,y增加
            this.x = this.x - this.offset_x;
            this.y = this.y + this.offset_y;
        },
        //獲取隨機座標
        getPos: function (i) {
            _this = this;

            function getCoordinate() {
                _this.x = Math.random() * boundaryWidth; //x座標

                for (var k = 0; k < meteorCoordinateArr.length; k++) {
                    if (Math.abs(_this.x - meteorCoordinateArr[k]) < METEOR_SEPARATE) { //這裡如果流星之間距離過小,會把其他流星隔斷,嚴重影響效果。
                        return getCoordinate();
                    }   
                }

                meteorCoordinateArr[i] = _this.x;
            }

            getCoordinate();

            this.y = 0.2 * boundaryHeight;  //y座標
        },
        //畫流星
        draw: function () {
            context.save();
            context.beginPath();
            context.lineWidth = 2.5; //寬度
            context.globalAlpha = this.alpha; //設定透明度

            //建立橫向漸變顏色,起點座標至終點座標
            var line = context.createLinearGradient(this.x, this.y, this.x + this.width, this.y - this.height);

            //分段設定顏色
            line.addColorStop(0, "rgba(255, 255, 255, 1)");
            line.addColorStop(1, "rgba(255, 255,255 , 0)");

            if (this.alpha < 0 ) {
                this.alpha = -this.alpha;
            }
            //填充
            context.strokeStyle = line;
            //起點
            context.moveTo(this.x, this.y);
            //終點
            context.lineTo(this.x + this.width, this.y - this.height);

            context.closePath();
            context.stroke();
            context.restore();
        },
        move: function () {

            var x = this.x + this.width - this.offset_x;
            var y = this.y - this.height;

            this.alpha -= 0.002;

            //重新計算位置,往左下移動
            this.countPos();

            if (this.alpha <= 0)="" {="" this.alpha="0;" }="" else="" if(this.alpha=""> 1) {
                this.alpha = 1;
            }

            //畫一個矩形去清空流星
            context.clearRect(this.x - this.offset_x, y, this.width + this.offset_x, this.height); 
            //重繪
            this.draw(); 
        }
    }    
    //流星動起來
    function playMeteor() {
        for (var n = 0; n < METEOR_COUNT; n++) {  
            var rain = meteorArr[n];

            rain.move();//移動

            if (rain.y > boundaryHeight + 100) {//超出界限後重來
                context.clearRect(rain.x, rain.y - rain.height, rain.width, rain.height);
                meteorCoordinate[n] = 0;//清空座標陣列具體流星的座標
                meteorArr[n] = new MeteorRain(n);
                meteorArr[n].init(n);
            }
        }  

        clearTimeout(playMeteorTimeout);
        playMeteorTimeout = setTimeout(playMeteor, 5);
    }

    //初始化  
    init(document.getElementsByTagName("body")[0]);
}());    複製程式碼

相關文章