Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

用canvas实现流星特效 #11

Open
wengjq opened this issue Jun 23, 2017 · 2 comments
Open

用canvas实现流星特效 #11

wengjq opened this issue Jun 23, 2017 · 2 comments
Labels

Comments

@wengjq
Copy link
Owner

wengjq commented Jun 23, 2017

最近帮公司网站banner实现了几个动画特效,踩了一些坑,这里mark下,下面给个流星demo:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>canvas梦幻星空背景</title>
	<style>
		*{margin: 0;padding: 0;}
		html,body{height: 100%;}
		body{background: rgba(0, 0, 0, 0.9);}
	</style>
</head>
<body>
<script>
(function () { 
    var context;//画布上下文
    var boundaryHeight;//画布高,边界值
    var boundaryWidth;//画布宽,边界值
    var starArr = [];
    var meteorArr = [];
    var STAR_COUNT = 500;//星星数,常量
    var METEOR_COUNT = 4;//流星数,常量
    var METEOR_SEPARATE = 300; //流星之间间隔,常量
    var meteorCoordinate = [];//存所以流星的坐标
    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 < meteorCoordinate.length; k++) {
                    if (Math.abs(_this.x - meteorCoordinate[k]) < METEOR_SEPARATE) { //这里如果流星之间距离过小,会把其他流星隔断,严重影响效果。
                        return getCoordinate();
                    }   
                }

                meteorCoordinate[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]);

}());

效果如下:

meteor2
怎么看起来有点丑!!! gif的问题。。。

@wengjq wengjq added the 其他 label Jun 23, 2017
@XJIANBIN
Copy link

getCoordinate 中 return getCoordinate() ,如果流星距离设置过大,将大量递归,程序直接溢出奔溃了。您项目中没这个问题吗?

@wengjq
Copy link
Owner Author

wengjq commented Sep 15, 2017

@XJIANBIN 这个如果整体宽度小确实会有这个问题,可以通过加保护或者减少流星数量去fixed。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants