canvasStar icon indicating copy to clipboard operation
canvasStar copied to clipboard

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

Open joyjoe opened this issue 7 years ago • 1 comments

先膜拜一下妹子

提几点建议

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

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

joyjoe avatar Aug 15 '17 09:08 joyjoe

;(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;

});

joyjoe avatar Aug 15 '17 09:08 joyjoe