418 lines
15 KiB
JavaScript
418 lines
15 KiB
JavaScript
|
/**
|
|||
|
* @author https://github.com/chengquan223
|
|||
|
* @Date 2017-02-16
|
|||
|
*/
|
|||
|
(function () {
|
|||
|
//全局变量
|
|||
|
var canvas, ctx, w, h, centerX, centerY;
|
|||
|
var imgOut = document.createElement('img'),
|
|||
|
imgIn = document.createElement('img');
|
|||
|
imgOut.src = 'https://zos.alipayobjects.com/rmsportal/JorDDgUVJljaIxDBpiBM.png';
|
|||
|
imgIn.src = 'https://zos.alipayobjects.com/rmsportal/uIbqAshSeUuRPrRXNOrN.png';
|
|||
|
|
|||
|
var initCanvas = function () {
|
|||
|
canvas = document.createElement('canvas');
|
|||
|
ctx = canvas.getContext('2d');
|
|||
|
w = canvas.width = document.body.clientWidth;
|
|||
|
h = canvas.height = 560;
|
|||
|
centerX = w / 2;
|
|||
|
centerY = h / 2;
|
|||
|
canvas.id = 'myCanvas';
|
|||
|
document.getElementById('canvasWrapper').appendChild(canvas);
|
|||
|
};
|
|||
|
|
|||
|
function Circle(x, y, r) {
|
|||
|
this.x = x;
|
|||
|
this.y = y;
|
|||
|
this.r = r;
|
|||
|
}
|
|||
|
|
|||
|
Circle.prototype = {
|
|||
|
draw: function () {
|
|||
|
ctx.save();
|
|||
|
ctx.lineWidth = '.6';
|
|||
|
ctx.strokeStyle = '#2123C0';
|
|||
|
ctx.beginPath();
|
|||
|
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
|
|||
|
ctx.stroke();
|
|||
|
ctx.restore();
|
|||
|
},
|
|||
|
birthPoint: function (isLeft) {
|
|||
|
var angle = 180;
|
|||
|
if (isLeft) {
|
|||
|
angle = -angle;
|
|||
|
}
|
|||
|
angle = Math.floor(Math.random() * +angle);
|
|||
|
return {
|
|||
|
x: Math.floor(this.x + Math.sin(angle * Math.PI / 180) * (this.r + (Math.random() * 15) + 5)),
|
|||
|
y: Math.floor(this.y + Math.cos(angle * Math.PI / 180) * (this.r + (Math.random() * 15) + 5))
|
|||
|
};
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
function Point(x, y) {
|
|||
|
this.x = x;
|
|||
|
this.y = y;
|
|||
|
this.size = Math.random() * 5 + 7;
|
|||
|
this.opacity = Math.random() * 0.25 + 0.1;
|
|||
|
this.t = 0;
|
|||
|
this.interval = Math.random() * 0.2 + 0.01;
|
|||
|
// this.d = Math.floor(Math.random() * 300 + 100);
|
|||
|
this.isRun = true;
|
|||
|
}
|
|||
|
|
|||
|
Point.prototype = {
|
|||
|
draw: function () {
|
|||
|
ctx.save();
|
|||
|
ctx.globalAlpha = this.opacity;
|
|||
|
ctx.translate(this.x, this.y);
|
|||
|
ctx.rotate(0 * Math.PI / 180);
|
|||
|
ctx.drawImage(imgOut, -this.size / 2, -this.size / 2, this.size, this.size); //用图片代替粒子,效率更高
|
|||
|
ctx.restore();
|
|||
|
},
|
|||
|
relive: function () {
|
|||
|
var radius = pointLayer.circle.r,
|
|||
|
distance = 220,
|
|||
|
gap = 20,
|
|||
|
x = Math.floor(Math.random() * ((centerX - radius - gap) - (centerX - radius - gap - distance)) + centerX - radius - gap - distance),
|
|||
|
y = Math.floor(Math.random() * ((centerY + 5) - (centerY - 10)) + centerY - 10);
|
|||
|
if (Math.random() > 0.5) {
|
|||
|
x = Math.floor(Math.random() * ((centerX + radius + gap + distance) - (centerX + radius + gap)) + centerX + radius + gap);
|
|||
|
}
|
|||
|
return new Point(x, y);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
function Wave() {
|
|||
|
this.width = w;
|
|||
|
this.height = 150;
|
|||
|
this.K = 2;
|
|||
|
this.F = 3;
|
|||
|
this.MAX = (this.height / 2) - 4;
|
|||
|
this.noise = Math.min(50, 1) * this.MAX;
|
|||
|
}
|
|||
|
|
|||
|
Wave.prototype = {
|
|||
|
_createLines: function () {
|
|||
|
var lines = this.lines = [],
|
|||
|
noise = Math.min(50, 1) * this.MAX;
|
|||
|
lines.push({
|
|||
|
phase: 25,
|
|||
|
speed: 0.045,
|
|||
|
attenuation: 3.8,
|
|||
|
color: 'rgba(103,174,245,.75)',
|
|||
|
width: .55,
|
|||
|
distance: 0
|
|||
|
}, {
|
|||
|
phase: 5,
|
|||
|
speed: 0.035,
|
|||
|
attenuation: 8,
|
|||
|
color: 'rgba(132,219,86,.65)',
|
|||
|
width: .45,
|
|||
|
distance: 30
|
|||
|
}, {
|
|||
|
phase: 140,
|
|||
|
speed: 0.045,
|
|||
|
attenuation: 3.5,
|
|||
|
color: 'rgba(140,87,255,.85)',
|
|||
|
width: .55,
|
|||
|
distance: 140
|
|||
|
}, {
|
|||
|
phase: 160,
|
|||
|
speed: 0.045,
|
|||
|
attenuation: 4.5,
|
|||
|
color: 'rgba(245,139,192,0.71)',
|
|||
|
width: .55,
|
|||
|
distance: 40
|
|||
|
});
|
|||
|
},
|
|||
|
_globalAttenuationFn: function (x) {
|
|||
|
return Math.pow(this.K * 4 / (this.K * 4 + Math.pow(x, 4)), this.K * 4);
|
|||
|
},
|
|||
|
_draw: function (line) {
|
|||
|
line.phase = (line.phase + line.speed) % (Math.PI * 64);
|
|||
|
ctx.moveTo(100, 0);
|
|||
|
ctx.beginPath();
|
|||
|
ctx.save();
|
|||
|
ctx.strokeStyle = line.color;
|
|||
|
ctx.lineWidth = line.width || .1;
|
|||
|
var x, y;
|
|||
|
for (var i = -this.K; i <= this.K; i += 0.03) {
|
|||
|
x = Math.floor(this.width * ((i + this.K) / (this.K * 4)) + w / 4);
|
|||
|
y = Math.floor(h / 2 + this.noise * this._globalAttenuationFn(i) * (1 / line.attenuation) * Math.sin(this.F * i - line.phase));
|
|||
|
ctx.lineTo(x, y);
|
|||
|
}
|
|||
|
ctx.stroke();
|
|||
|
ctx.restore();
|
|||
|
},
|
|||
|
render: function () {
|
|||
|
var self = this;
|
|||
|
self.lines.forEach(function (line) {
|
|||
|
self._draw(line);
|
|||
|
});
|
|||
|
},
|
|||
|
start: function () {
|
|||
|
this._createLines();
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
var Tween = {
|
|||
|
Quad: {
|
|||
|
easeIn: function (t, b, c, d) {
|
|||
|
return c * (t /= d) * t + b;
|
|||
|
},
|
|||
|
easeOut: function (t, b, c, d) {
|
|||
|
return -c * (t /= d) * (t - 2) + b;
|
|||
|
},
|
|||
|
easeInOut: function (t, b, c, d) {
|
|||
|
if ((t /= d / 2) < 1) return c / 2 * t * t + b;
|
|||
|
return -c / 2 * ((--t) * (t - 2) - 1) + b;
|
|||
|
}
|
|||
|
},
|
|||
|
Cubic: {
|
|||
|
easeIn: function (t, b, c, d) {
|
|||
|
return c * (t /= d) * t * t + b;
|
|||
|
},
|
|||
|
easeOut: function (t, b, c, d) {
|
|||
|
return c * ((t = t / d - 1) * t * t + 1) + b;
|
|||
|
},
|
|||
|
easeInOut: function (t, b, c, d) {
|
|||
|
if ((t /= d / 2) < 1) return c / 2 * t * t * t + b;
|
|||
|
return c / 2 * ((t -= 2) * t * t + 2) + b;
|
|||
|
}
|
|||
|
},
|
|||
|
Quart: {
|
|||
|
easeIn: function (t, b, c, d) {
|
|||
|
return c * (t /= d) * t * t * t + b;
|
|||
|
},
|
|||
|
easeOut: function (t, b, c, d) {
|
|||
|
return -c * ((t = t / d - 1) * t * t * t - 1) + b;
|
|||
|
},
|
|||
|
easeInOut: function (t, b, c, d) {
|
|||
|
if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b;
|
|||
|
return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
|
|||
|
}
|
|||
|
},
|
|||
|
};
|
|||
|
|
|||
|
//运动点层
|
|||
|
var pointLayer = {
|
|||
|
init: function () {
|
|||
|
var points = this.points = [];
|
|||
|
this.circle = new Circle(w / 2, h / 2, 100);
|
|||
|
for (var i = 0; i < 200; i++) {
|
|||
|
var random = Math.floor(Math.random() * ((w / 2 + w / 4) - (w / 2 - w / 4)) + (w / 2 - w / 4)),
|
|||
|
point = new Point(random, centerY),
|
|||
|
isLeft = random < this.circle.x,
|
|||
|
endPoint = this.circle.birthPoint(isLeft);
|
|||
|
point.ex = endPoint.x;
|
|||
|
point.ey = endPoint.y;
|
|||
|
point.d = Math.random() * 100 + 50;
|
|||
|
// point.d = Math.floor(Math.sqrt(Math.pow((point.x - point.ex), 2) + Math.pow((point.y - point.ey), 2)));
|
|||
|
points.push(point);
|
|||
|
}
|
|||
|
},
|
|||
|
render: function () {
|
|||
|
var self = this;
|
|||
|
var points = self.points;
|
|||
|
|
|||
|
points.forEach(function (point, i) {
|
|||
|
//1.刚进页面,满足isRun=true、透明度<0.8
|
|||
|
//2.当透明度>=0.8时,开始运动,当运动时间t>=d时,设置为isRun=false,
|
|||
|
//3.当isRun=false时,逐渐降低透明度至0,当透明度<0时。复活粒子
|
|||
|
if (point.isRun && point.opacity < 0.8) {
|
|||
|
point.draw();
|
|||
|
point.opacity += point.interval;
|
|||
|
// point.opacity += 0.035;
|
|||
|
return;
|
|||
|
}
|
|||
|
if (point.isRun && point.opacity >= 0.8) {
|
|||
|
point.x = Tween.Cubic.easeInOut(point.t, point.x, point.ex - point.x, point.d);
|
|||
|
point.y = Tween.Cubic.easeInOut(point.t, point.y, point.ey - point.y, point.d);
|
|||
|
point.draw();
|
|||
|
if (point.t < point.d) {
|
|||
|
point.t++;
|
|||
|
if (point.x === point.ex && point.y === point.ey) {
|
|||
|
point.isRun = false;
|
|||
|
}
|
|||
|
} else {
|
|||
|
point.isRun = false;
|
|||
|
}
|
|||
|
}
|
|||
|
if (!point.isRun && point.opacity > 0) {
|
|||
|
point.draw();
|
|||
|
point.opacity -= 0.05;
|
|||
|
return;
|
|||
|
}
|
|||
|
if (!point.isRun && point.opacity < 0) {
|
|||
|
var point = point.relive();
|
|||
|
var isLeft = point.x < self.circle.x;
|
|||
|
var endPoint = self.circle.birthPoint(isLeft);
|
|||
|
point.ex = endPoint.x;
|
|||
|
point.ey = endPoint.y;
|
|||
|
point.d = Math.sqrt(Math.pow((point.x - point.ex), 2) + Math.pow((point.y - point.ey), 2));
|
|||
|
points.splice(i, 1, point);
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
//背景层
|
|||
|
var bgLayer = {
|
|||
|
init: function () {
|
|||
|
var dataList = [{
|
|||
|
type: 'out', //外层圆
|
|||
|
count: 80, //粒子数目
|
|||
|
interval: 5, //粒子间隔
|
|||
|
radiusList: [100, 102, 104, 105, 106, 107, 108, 115, 122, 129, 136]
|
|||
|
}, {
|
|||
|
type: 'in', //内层圆
|
|||
|
count: 60,
|
|||
|
interval: 8,
|
|||
|
radiusList: [40, 45, 50, 55, 60, 65, 70, 75]
|
|||
|
}, {
|
|||
|
type: 'highlight', //高亮
|
|||
|
count: 0,
|
|||
|
interval: 8,
|
|||
|
radiusList: [40, 45, 50, 55, 60, 65, 70, 75]
|
|||
|
}];
|
|||
|
this.circles = circles = [];
|
|||
|
dataList.forEach(function (data) {
|
|||
|
data.radiusList.forEach(function (radius, index) {
|
|||
|
var circle = new Circle(centerX, centerY, radius);
|
|||
|
circle.type = data.type;
|
|||
|
circle.lineWidth = Math.random() * 0.9 + 1.6;
|
|||
|
circle.radian = Math.PI / Math.random();
|
|||
|
circle.radianNum = Math.random() * 3 + 3;
|
|||
|
circle.runSpeed = Math.random() * 0.02 + 0.005;
|
|||
|
circle.interval = Math.random() * 0.1 + 0.05;
|
|||
|
circle.particles = (function () {
|
|||
|
var particles = [];
|
|||
|
for (var i = 0; i < data.count - data.interval * index; i++) {
|
|||
|
particles.push({
|
|||
|
angle: Math.floor(Math.random() * 360),
|
|||
|
size: Math.random() * 5 + 7,
|
|||
|
opacity: Math.random()
|
|||
|
});
|
|||
|
}
|
|||
|
return particles;
|
|||
|
}());
|
|||
|
circles.push(circle);
|
|||
|
});
|
|||
|
});
|
|||
|
},
|
|||
|
render: function () {
|
|||
|
this._drawCircle();
|
|||
|
this._drawParticle();
|
|||
|
this._drawBar();
|
|||
|
},
|
|||
|
_drawCircle: function () {
|
|||
|
this.circles.forEach(function (circle) {
|
|||
|
var shadowColor = 'rgba(0,0,255,1)';
|
|||
|
if (circle.type && circle.type === 'in') {
|
|||
|
shadowColor = 'rgba(42,97,0,.4)';
|
|||
|
}
|
|||
|
ctx.save();
|
|||
|
ctx.beginPath();
|
|||
|
ctx.lineWidth = 1.2;
|
|||
|
ctx.strokeStyle = 'rgba(19,6,67,.6)';
|
|||
|
ctx.shadowColor = shadowColor;
|
|||
|
ctx.shadowBlur = 15;
|
|||
|
ctx.globalAlpha = 0.75;
|
|||
|
ctx.arc(centerX, centerY, circle.r, 0, Math.PI * 2);
|
|||
|
ctx.stroke();
|
|||
|
ctx.closePath();
|
|||
|
ctx.restore();
|
|||
|
});
|
|||
|
},
|
|||
|
_drawParticle: function () {
|
|||
|
this.circles.forEach(function (circle) {
|
|||
|
var img = imgOut;
|
|||
|
if (circle.type && circle.type === 'in') {
|
|||
|
img = imgIn;
|
|||
|
}
|
|||
|
circle.particles.forEach(function (particle) {
|
|||
|
var _x = circle.r * Math.cos(particle.angle * (Math.PI / 180)) + centerX;
|
|||
|
var _y = circle.r * Math.sin(particle.angle * (Math.PI / 180)) + centerY;
|
|||
|
ctx.save();
|
|||
|
ctx.globalAlpha = particle.opacity;
|
|||
|
ctx.translate(_x, _y);
|
|||
|
ctx.rotate(particle.angle * Math.PI / 180);
|
|||
|
ctx.drawImage(img, -particle.size / 2, -particle.size / 2, particle.size, particle.size); //用图片代替粒子,效率更高
|
|||
|
ctx.restore();
|
|||
|
particle.angle += circle.interval;
|
|||
|
});
|
|||
|
});
|
|||
|
},
|
|||
|
_drawBar: function () {
|
|||
|
this.circles.forEach(function (circle) {
|
|||
|
if (circle.type && circle.type === 'in' || circle.type && circle.type === 'highlight') {
|
|||
|
ctx.save();
|
|||
|
ctx.beginPath();
|
|||
|
ctx.globalCompositeOperation = 'lighter';
|
|||
|
ctx.lineWidth = circle.lineWidth;
|
|||
|
ctx.strokeStyle = 'rgba(58,255,255,.7)';
|
|||
|
ctx.arc(centerX, centerY, circle.r, circle.radian, circle.radian + Math.PI / circle.radianNum);
|
|||
|
ctx.stroke();
|
|||
|
ctx.restore();
|
|||
|
circle.radian += circle.runSpeed;
|
|||
|
}
|
|||
|
});
|
|||
|
},
|
|||
|
fillCenterCircle: function () {
|
|||
|
ctx.save();
|
|||
|
ctx.beginPath();
|
|||
|
ctx.fillStyle = '#050510';
|
|||
|
ctx.arc(centerX, centerY, 42, 0, Math.PI * 2);
|
|||
|
ctx.fill();
|
|||
|
ctx.restore();
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
(function () {
|
|||
|
'use strict';
|
|||
|
|
|||
|
var vendors = ['webkit', 'moz'];
|
|||
|
for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
|
|||
|
var vp = vendors[i];
|
|||
|
window.requestAnimationFrame = window[vp + 'RequestAnimationFrame'];
|
|||
|
window.cancelAnimationFrame = window[vp + 'CancelAnimationFrame'] || window[vp + 'CancelRequestAnimationFrame'];
|
|||
|
}
|
|||
|
if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) // iOS6 is buggy
|
|||
|
||
|
|||
|
!window.requestAnimationFrame || !window.cancelAnimationFrame) {
|
|||
|
var lastTime = 0;
|
|||
|
window.requestAnimationFrame = function (callback) {
|
|||
|
var now = Date.now();
|
|||
|
var nextTime = Math.max(lastTime + 16, now);
|
|||
|
return setTimeout(function () {
|
|||
|
callback(lastTime = nextTime);
|
|||
|
}, nextTime - now);
|
|||
|
};
|
|||
|
window.cancelAnimationFrame = clearTimeout;
|
|||
|
}
|
|||
|
})();
|
|||
|
|
|||
|
var init = function () {
|
|||
|
initCanvas();
|
|||
|
pointLayer.init();
|
|||
|
wave = new Wave();
|
|||
|
wave.start();
|
|||
|
bgLayer.init();
|
|||
|
}
|
|||
|
|
|||
|
var renderLoop = function () {
|
|||
|
ctx.clearRect(0, 0, w, h);
|
|||
|
bgLayer.render();
|
|||
|
wave.render();
|
|||
|
pointLayer.render();
|
|||
|
bgLayer.fillCenterCircle();
|
|||
|
}
|
|||
|
|
|||
|
init();
|
|||
|
|
|||
|
(function frame() {
|
|||
|
requestAnimationFrame(frame);
|
|||
|
renderLoop();
|
|||
|
}());
|
|||
|
})();
|