<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="js/jquery.min.js"></script>
<style>
html,body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
.container{
position: relative;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background-color: #000000;
}
.container > canvas{
position: absolute;
top: 0;
left: 0;
}</style>
</head>
<body>
<div id="jsi-sphere-container" class="container"></div>
<script>
var RENDERER = {
AUXILIARY_LINE_COUMT : 16,
MAX_ROTATION_ANGLE : Math.PI / 200,
FIREWORK_INTERVAL : {min : 30, max : 100},
init : function(){
this.setParameters();
this.setupData();
this.reconstructMethod();
this.bindEvent();
this.render();
},
setParameters : function(){
this.$document = $(document);
this.$window = $(window);
this.$container = $('#jsi-sphere-container');
this.width = this.$container.width();
this.height = this.$container.height();
this.contextBackground = $('<canvas />').attr({width : this.width, height : this.height}).appendTo(this.$container).get(0).getContext('2d');
this.contextForeground = $('<canvas />').attr({width : this.width, height : this.height}).appendTo(this.$container).get(0).getContext('2d');
this.camera = CAMERA.init(this);
this.angleX = this.MAX_ROTATION_ANGLE / 2;
this.angleY = this.MAX_ROTATION_ANGLE / 2;
this.points = [];
this.fireworks = [];
this.maxFireworkInterval = this.getRandomValue(this.FIREWORK_INTERVAL) | 0;
this.fireworkInterval = this.maxFireworkInterval;
this.base = Math.min(this.width, this.height);
this.scatterRadius = this.base * 3 / 2;
},
reconstructMethod : function(){
this.render = this.render.bind(this);
this.changeDepth = this.changeDepth.bind(this);
this.changeAngle = this.changeAngle.bind(this);
},
getRandomValue : function(range){
return range.min + (range.max - range.min) * Math.random();
},
bindEvent : function(){
this.$document.on('onwheel' in document ? 'wheel' : 'onmousewheel' in document ? 'mousewheel' : 'DOMMouseScroll', this.changeDepth);
this.$container.on('mousemove', this.changeAngle);
},
setupData : function(){
for(var i = 1; i < this.AUXILIARY_LINE_COUMT; i++){
for(var phi = 0, deltaPhi = Math.PI * 2 / (this.AUXILIARY_LINE_COUMT * 2 - Math.abs(this.AUXILIARY_LINE_COUMT * 2 - i * 4)); phi < Math.PI * 2; phi += deltaPhi){
this.points.push(new POINT(this.camera, this.getAxis3D(Math.PI / this.AUXILIARY_LINE_COUMT * i, phi)));
}
}
},
changeDepth : function(event){
event.preventDefault();
this.camera.move(0, 0, (event.originalEvent.deltaY || -event.originalEvent.wheelDelta || event.originalEvent.detail) >= 0 ? 3 : -2);
},
changeAngle : function(event){
var offset = this.$container.offset(),
x = event.clientX - offset.left + this.$window.scrollLeft(),
y = event.clientY - offset.top + this.$window.scrollTop();
this.angleY = (this.width / 2 - x) / this.width * 2 * this.MAX_ROTATION_ANGLE;
this.angleX = (this.height / 2 - y) / this.height * 2 * this.MAX_ROTATION_ANGLE;
},
getAxis3D : function(theta, phi){
var cosTheta = Math.cos(theta),
sinTheta = Math.sin(theta);
return {
x : this.scatterRadius * sinTheta * Math.cos(phi),
y : this.scatterRadius * cosTheta,
z : this.scatterRadius * sinTheta * Math.sin(phi)
};
},
render : function(){
requestAnimationFrame(this.render);
this.contextForeground.clearRect(0, 0, this.width, this.height);
this.contextBackground.fillStyle = 'hsla(0, 0%, 0%, 0.2)';
this.contextBackground.fillRect(0, 0, this.width, this.height);
for(var i = 0, length = this.points.length; i < length; i++){
this.points[i].rotateX(this.angleX);
this.points[i].rotateY(this.angleY);
}
for(var i = 0, length = this.fireworks.length; i < length; i++){
this.fireworks[i].rotateX(this.angleX);
this.fireworks[i].rotateY(this.angleY);
}
this.points.sort(function(point1, point2){
return point2.getAxis3D().z - point1.getAxis3D().z;
});
this.fireworks.sort(function(firework1, firework2){
return firework2.getAxis3D().z - firework1.getAxis3D().z;
});
for(var i = this.points.length - 1; i >= 0; i--){
this.points[i].render(this.contextForeground);
}
for(var i = this.fireworks.length - 1; i >= 0; i--){
if(!this.fireworks[i].render(this.contextBackground)){
this.fireworks.splice(i, 1);
}
}
if(--this.fireworkInterval == 0){
this.fireworks.push(new FIREWORK(this, this.camera));
this.maxFireworkInterval = this.getRandomValue(this.FIREWORK_INTERVAL) | 0;
this.fireworkInterval = this.maxFireworkInterval;
}
}
};
var CAMERA = {
INIT_AXIS : {X : 0, Y : 0, Z : -2300},
MIN_Z : -3000,
FOCUS_DISTANCE : 500,
MAGNIFICATION : 50,
OFFSET : 100,
init : function(renderer){
this.renderer = renderer;
this.x = this.INIT_AXIS.X;
this.y = this.INIT_AXIS.Y;
this.z = this.INIT_AXIS.Z;
return this;
},
move : function(deltaX, deltaY, deltaZ){
this.x += deltaX;
this.y += deltaY;
this.z -= deltaZ * this.MAGNIFICATION;
this.z = Math.min(this.z, -this.renderer.scatterRadius);
this.z = Math.max(this.z, this.MIN_Z);
},
rotate : function(deltaX, deltaY, deltaZ){
this.angle.x += deltaX;
this.angle.y += deltaY;
this.angle.z += deltaZ;
},
getCompensatedParameters : function(point){
var denominator = point.z - this.z;
if(denominator <= this.OFFSET){
return null;
}
var scale = this.FOCUS_DISTANCE / denominator,
luminance = Math.min(80, scale * 250),
x = this.renderer.width / 2 + (point.x + this.x) * scale,
y = this.renderer.height / 2 - (point.y + this.y) * scale,
radius = point.RADIUS * scale;
if(x + radius < 0 || x - radius > this.renderer.width || y + radius < 0 || y - radius > this.renderer.height){
return null;
}
return {x : x, y : y, radius : radius, luminance : luminance, scale : scale};
}
};
var BASE = function(methods){
this.parameters = null;
$.extend(this, methods);
};
BASE.prototype = {
COLOR : 'hsl(%h, 80%, %l%)',
rotateX : function(angle){
var sin = Math.sin(angle),
cos = Math.cos(angle),
y = this.y * cos - this.z * sin,
z = this.z * cos + this.y * sin;
this.y = y;
this.z = z;
},
rotateY : function(angle){
var sin = Math.sin(angle),
cos = Math.cos(angle),
x = this.x * cos - this.z * sin,
z = this.z * cos + this.x * sin;
this.x = x;
this.z = z;
},
rotateZ : function(angle){
var sin = Math.sin(angle),
cos = Math.cos(angle),
x = this.x * cos - this.y * sin,
y = this.y * cos + this.x * sin;
this.x = x;
this.y = y;
},
getAxis3D : function(){
return {x : this.x, y : this.y, z : this.z};
},
getAxis2D : function(){
this.parameters = this.camera.getCompensatedParameters(this);
if(!this.parameters){
return null;
}
this.parameters.color = this.COLOR.replace('%h', this.hue).replace('%l', this.parameters.luminance);
return this.parameters;
}
};
var POINT = function(camera, axis){
this.camera = camera;
this.x = axis.x;
this.y = axis.y;
this.z = axis.z;
};
POINT.prototype = new BASE({
RADIUS : 7,
hue : 180,
render : function(context){
var axis = this.getAxis2D();
if(!axis){
return;
}
context.save();
context.translate(axis.x, axis.y);
context.beginPath();
context.fillStyle = axis.color;
context.arc(0, 0, axis.radius, 0, Math.PI * 2, true);
context.fill();
context.restore();
}
});
var FIREWORK = function(renderer, camera){
this.renderer = renderer;
this.camera = camera;
this.init();
};
FIREWORK.prototype = new BASE({
PARTICLE_COUNT : 300,
DELTA_OPACITY : 0.01,
RADIUS : 12,
THRESHOLD : 30,
DELTA_THETA : Math.PI / 10,
GRAVITY : -Math.PI / 100000,
VELOCITY : Math.PI / 160,
init : function()
评论0