SOURCE

console 命令行工具 X clear

                    
>
console
var RENDERER = {
	TREE_COUNT : 7,
	LIGHT_COUNT : 200,
	INTERVAL_Z : 400,
	FOCUS : 100,
	TREE_LUMINANCE : {min : 3, max : 15},
	TREE_CHANGE_COUNT : 10,
	TREE_CHANGE_DELTA : 0.5,
	DELTA_THETA : Math.PI / 100,
	
	init : function(){
		this.setParameters();
		this.reconstructMethod();
		this.createElements();
		this.render();
	},
	setParameters : function(){
		this.$container = $('#jsi-forest-container');
		this.width = this.$container.width();
		this.height = this.$container.height();
		this.context = $('<canvas />').attr({width : this.width, height : this.height}).appendTo(this.$container).get(0).getContext('2d');
		
		this.backgroundDark = this.context.createLinearGradient(0, 0, 0, this.height);
		this.backgroundDark.addColorStop(0, 'hsl(220, 80%, 40%)');
		this.backgroundDark.addColorStop(0.3, 'hsl(220, 80%, 10%)');
		this.backgroundDark.addColorStop(0.4, 'hsl(220, 80%, 3%)');
		this.backgroundDark.addColorStop(0.6, 'hsl(120, 30%, 3%)');
		this.backgroundDark.addColorStop(1, 'hsl(120, 30%, 10%)');
		this.backgroundLight = this.context.createLinearGradient(0, 0, 0, this.height);
		this.backgroundLight.addColorStop(0, 'hsl(220, 80%, 60%)');
		this.backgroundLight.addColorStop(0.4, 'hsl(220, 80%, 60%)');
		this.backgroundLight.addColorStop(0.6, 'hsl(120, 50%, 60%)');
		this.backgroundLight.addColorStop(1, 'hsl(120, 50%, 60%)');
		
		this.theta = 0;
		this.trees = [];
		this.lights = [];
		this.treeIndex = 0;
		this.orderAsc = true;
		this.distance = Math.sqrt(Math.pow(this.width / 2, 2) + Math.pow(this.height / 2, 2));
		this.maxLuminaceCount = (this.TREE_LUMINANCE.max - this.TREE_LUMINANCE.min) * this.TREE_CHANGE_COUNT / this.TREE_CHANGE_DELTA;
	},
	reconstructMethod : function(){
		this.render = this.render.bind(this);
	},
	createElements : function(){
		var treeLight = TREE_CREATOR.create(this.TREE_LUMINANCE.max),
			treeDarks = [],
			centerX = this.width / 2,
			centerY = this.height / 2,
			x = this.width * 1.6,
			y = this.height * 1.6,
			maxZ = this.INTERVAL_Z *  this.TREE_COUNT;
			
		for(var i = this.TREE_LUMINANCE.min, max = this.TREE_LUMINANCE.max; i <= max; i += this.TREE_CHANGE_DELTA){
			treeDarks.push(TREE_CREATOR.create(i));
		}
		for(var i = 0, length = this.TREE_COUNT; i < length; i++){
			var z = this.INTERVAL_Z * (length - i);
			this.trees.push(new TREE(treeLight, treeDarks, centerX, centerY, x, y, z, maxZ, this.FOCUS));
			this.trees.push(new TREE(treeLight, treeDarks, centerX, centerY, -x, y, z, maxZ, this.FOCUS));
			
			if(i > this.TREE_COUNT / 2){
				this.trees.push(new TREE(treeLight, treeDarks, centerX, centerY, x * 2, y, z + this.INTERVAL_Z / 2, maxZ / 2, this.FOCUS));
				this.trees.push(new TREE(treeLight, treeDarks, centerX, centerY, -x * 2, y, z + this.INTERVAL_Z / 2, maxZ / 2, this.FOCUS));
			}
		}
		for(var i = 0, length = this.LIGHT_COUNT; i < length; i++){
			this.lights.push(new LIGHT(this.width, this.height, centerX, centerY, maxZ, this.FOCUS));
		}
		this.shadow = SHADOW.init(this.width, this.height);
	},
	render : function(){
		requestAnimationFrame(this.render);
		
		this.context.fillStyle = this.backgroundDark;
		this.context.fillRect(0, 0, this.width, this.height);
		
		for(var i = 0, length = this.trees.length, index = this.treeIndex / this.TREE_CHANGE_COUNT | 0; i < length; i++){
			this.trees[i].render(this.context, false, index);
		}
		this.shadow.render(this.context);
		this.context.save();
		this.context.beginPath();
		
		for(var i = 0, length = this.lights.length; i < length; i++){
			this.lights[i].render(this.context);
		}
		this.context.clip();
		this.context.fillStyle = this.backgroundLight;
		this.context.fillRect(0, 0, this.width, this.height);
		
		for(var i = 0, length = this.trees.length; i < length; i++){
			this.trees[i].render(this.context, true);
		}
		this.shadow.render(this.context);
		this.context.globalCompositeOperation = 'lighter';
		
		var backgroundCover = this.context.createRadialGradient(this.width / 2, this.height / 2, 0, this.width / 2, this.height / 2, this.distance),
			hue = 180 + 90 * Math.sin(this.theta);
			
		backgroundCover.addColorStop(0, 'hsl(' + hue + ', 80%, 10%)');
		backgroundCover.addColorStop(1, 'hsl(' + hue + ', 80%, 40%)');
		this.context.fillStyle = backgroundCover;
		this.context.fillRect(0, 0, this.width, this.height);
		this.context.restore();
		this.changeTreeIndex();
		
		this.theta += this.DELTA_THETA;
		this.hue %= Math.PI * 2;
	},
	changeTreeIndex : function(){
		if(this.orderAsc){
			if(++this.treeIndex == this.maxLuminaceCount){
				this.treeIndex--;
				this.orderAsc = false;
			}
		}else{
			if(--this.treeIndex == 0){
				this.treeIndex++;
				this.orderAsc = true;
			}
		}
	}
};
var TREE_CREATOR = {
	WIDTH : 120,
	HEIGHT : 200,
	TRUNK_RATE : 0.8,
	BRANCH_RADIAN : Math.PI / 6,
	BRANCH_RATE : 0.55,
	BRANCH_LEVEL : 8,
	COLOR : 'hsl(120, 80%, %luminance%)',
	
	create : function(luminance){
		this.setParameters(luminance);
		this.drawTree(this.WIDTH / 2, this.HEIGHT, Math.PI / 2, this.HEIGHT / 4, 3, 0);
		return {canvas : this.canvas, x : -this.WIDTH / 2, y : -this.HEIGHT};
	},
	setParameters : function(luminance){
		this.canvas = $('<canvas />').attr({width : this.WIDTH, height : this.HEIGHT}).get(0);
		this.context = this.canvas.getContext('2d');
		this.context.strokeStyle = this.COLOR.replace('%luminance', luminance);
	},
	drawTree : function(x, y, radian, length, width, level){
		if (level > this.BRANCH_LEVEL) {
			return;
		}
		var sin = length * Math.sin(radian),
			cos = length * Math.cos(radian);
			
		this.drawTree(x + cos * this.TRUNK_RATE, y - sin * this.TRUNK_RATE, radian, length * this.TRUNK_RATE, width * this.TRUNK_RATE, level + 1);
		
		for(var i = -1; i <= 1; i += 2){
			this.drawTree(x + cos * this.BRANCH_RATE, y - sin * this.BRANCH_RATE, radian + this.BRANCH_RADIAN * i, length * this.BRANCH_RATE, width * this.BRANCH_RATE, level + 1);
		}
		this.context.lineWidth = width;
		this.context.beginPath();
		this.context.moveTo(x, y);
		this.context.lineTo(x + length*Math.cos(radian), y - length*Math.sin(radian));
		this.context.stroke();
	}
};
var TREE = function(treeLight, treeDarks, centerX, centerY, x, y, z, maxZ, focus){
	this.treeLight = treeLight;
	this.treeDarks = treeDarks;
	this.centerX = centerX;
	this.centerY = centerY;
	this.x = x;
	this.y = y;
	this.z = z;
	this.maxZ = maxZ;
	this.focus = focus;
};
TREE.prototype = {
	MAGNIFICATION : 10,
	
	render : function(context, isLight, index){
		var tree = isLight ? this.treeLight : this.treeDarks[index],
			rate = this.focus / (this.focus + this.z),
			x = this.centerX + this.x * rate,
			y = this.centerY + this.y * rate;
			
		rate *= this.MAGNIFICATION;
		
		context.save();
		context.translate(x, y);
		context.scale(rate, rate);
		context.drawImage(tree.canvas, tree.x, tree.y);
		context.restore();
		
		if(--this.z < 0){
			this.z = this.maxZ;
		}
	}
};
var LIGHT = function(width, height, centerX, centerY, maxZ, focus){
	this.width = width;
	this.height = height;
	this.centerX = centerX;
	this.centerY = centerY;
	this.maxZ = maxZ;
	this.focus = focus;
	this.init();
};
LIGHT.prototype = {
	VELOCITY_RANGE : {min : -3, max : 3},
	MAX_RADIUS : 12,
	
	init : function(){
		this.x = this.getRandomValue({min : -this.width * 2, max : this.width * 2});
		this.y = this.getRandomValue({min : -this.height, max : this.height});
		this.z = this.getRandomValue({min : 0, max : this.maxZ});
		this.vx = this.getRandomValue(this.VELOCITY_RANGE);
		this.vy = this.getRandomValue(this.VELOCITY_RANGE);
		this.rate = 1;
	},
	getRandomValue : function(range){
		return range.min + (range.max - range.min) * Math.random();
	},
	render : function(context){
		var rate = this.focus / (this.focus + this.z),
			x = this.centerX + this.x * rate,
			y = this.centerY - this.y * rate;
		
		context.moveTo(x, y);
		context.arc(x, y, this.MAX_RADIUS * rate * this.rate, 0, Math.PI * 2, false);
		
		if(--this.z < -this.focus || x < 0 || x > this.width || y < 0 || y > this.height){
			this.init();
			this.rate = 0;
		}
		this.x += this.vx * rate;
		this.y += this.vy * rate;
		
		if(this.rate >= 1){
			return;
		}
		this.rate += 0.01;
	}
};
var SHADOW = {
	SHAKE_INTERVAL : Math.PI / 120,
	COLOR : 'rgba(0, 0, 0, 0.3)',
	
	init : function(width, height){
		this.width = width;
		this.height = height;
		this.theta = 0;
		return this;
	},
	render : function(context){
		context.save();
		context.fillStyle = this.COLOR;
		context.translate(this.width / 2, this.height);
		context.scale(0.6, 1.2);
		context.rotate(Math.PI / 60 * Math.sin(this.theta));
		
		context.beginPath();
		context.arc(0, -108, 8, 0, Math.PI * 2, false);
		context.fill();
		
		context.beginPath();
		context.moveTo(5, -100);
		context.bezierCurveTo(40, -100, 40, -70, 30, -40);
		context.lineTo(25, -40);
		context.lineTo(25, 5);
		context.lineTo(5, 5);
		context.lineTo(5, -40);
		context.lineTo(-5, -40);
		context.lineTo(-5, 5);
		context.lineTo(-25, 5);
		context.lineTo(-25, -40);
		context.lineTo(-30, -40);
		context.bezierCurveTo(-40, -70, -40, -100, -5, -100);
		context.fill();
		context.restore();
		
		this.theta += this.SHAKE_INTERVAL;
		this.theta %= Math.PI * 2;
	}
};
$(function(){
	RENDERER.init();
});
<div id="jsi-forest-container" class="container"></div>
html, body{
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    overflow: hidden;
    background-color: #101010;
}
.container{
    position: absolute;
    width: 500px;
    height: 500px;
    top: 50%;
    left: 50%;
    margin-top: -250px;
    margin-left: -250px;
}