下列 JavaScript
特效均来源于网络,由笔者整理,仅用于学习参考。
一、动画特效
带刺的花
<canvas id="barbedFlowers"></canvas>
// 函数主体
function barbedFlowers(el, width, height) {
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ('value' in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
};
};
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError('Cannot call a class as a function');
}
};
var Scene = function () {
function Scene() {
var _this = this;
_classCallCheck(this, Scene);
this.PI = Math.PI;
this.TAU = this.PI * 2;
this.GOLDEN = (Math.sqrt(5) + 1) / 2;
this.canvas = document.querySelector(el);
this.ctx = this.canvas.getContext('2d');
this.dpr = window.devicePixelRatio;
this.reset();
window.addEventListener('resize', function () {
return _this.reset();
});
this.loop();
};
createClass(Scene,
[
{
key: 'reset',
value: function reset() {
this.width = width || window.innerWidth;
this.height = height || window.innerHeight;
this.hwidth = this.width * 0.5;
this.hheight = this.height * 0.5;
this.canvas.width = this.width * this.dpr;
this.canvas.height = this.height * this.dpr;
this.ctx.scale(this.dpr, this.dpr);
this.ctx.translate(~~this.hwidth, ~~this.hheight);
this.ctx.globalCompositeOperation = 'lighter';
this.tick = 320;
}
},
{
key: 'loop',
value: function loop() {
var _this2 = this;
requestAnimationFrame(function () {
return _this2.loop();
});
this.tick++;
this.ctx.clearRect(-this.hwidth, -this.hheight, this.width, this.height);
var count = 150;
var angle = this.tick * -0.001;
var amp = 0;
for (var i = 0; i < count; i++) {
angle += this.GOLDEN * this.TAU + Math.sin(this.tick * 0.03) * 0.001;
amp += (i - count / 2) * 0.01 + Math.cos(this.tick * 0.015) * 1;
var x = Math.cos(angle) * amp + Math.cos(this.tick * 0.0075) * (count - i) *
0.3;
var y = Math.sin(angle) * amp + Math.sin(this.tick * 0.0075) * (count - i) *
0.3;
var radius = 0.1 + i * 0.02;
var scale = 0.1 + amp * 0.1;
var hue = this.tick + angle / this.TAU * 0.4 + 60;
var saturation = 90;
var lightness = 60;
var alpha = 0.7 + Math.cos(this.tick * 0.03 + i) * 0.3;
this.ctx.save();
this.ctx.translate(x, y);
this.ctx.rotate(angle);
this.ctx.scale(scale, 1);
this.ctx.rotate(this.PI * 0.25);
this.ctx.fillStyle = 'hsla(' + hue + ', ' + saturation + '%, ' + lightness +
'%, ' + alpha + ')';
this.ctx.fillRect(-radius, -radius, radius * 2, radius * 2);
this.ctx.restore();
this.ctx.beginPath();
this.ctx.arc(x, y, radius * 12, 0, this.TAU);
this.ctx.fillStyle = 'hsla(' + hue + ', ' + saturation + '%, ' + lightness +
'%, ' + alpha * 0.05 + ')';
this.ctx.fill();
}
}
}
]);
return Scene;
}();
var scene = new Scene();
};
// 调用函数
barbedFlowers('#barbedFlowers', 500, 500);
矩阵
<canvas id="matrix"></canvas>
// 函数主体
function matrix(el, width, height) {
var canvas,
ctx,
width,
height,
size,
lines,
tick;
function line() {
this.path = [];
this.speed = rand(10, 20);
this.count = randInt(10, 30);
this.x = width / 2, +1;
this.y = height / 2 + 1;
this.target = {
x: width / 2,
y: height / 2
};
this.dist = 0;
this.angle = 0;
this.hue = tick / 5;
this.life = 1;
this.updateAngle();
this.updateDist();
};
line.prototype.step = function (i) {
this.x += Math.cos(this.angle) * this.speed;
this.y += Math.sin(this.angle) * this.speed;
this.updateDist();
if (this.dist < this.speed) {
this.x = this.target.x;
this.y = this.target.y;
this.changeTarget();
}
this.path.push({
x: this.x,
y: this.y
});
if (this.path.length > this.count) {
this.path.shift();
}
this.life -= 0.001;
if (this.life <= 0) {
this.path = null;
lines.splice(i, 1);
}
};
line.prototype.updateDist = function () {
var dx = this.target.x - this.x,
dy = this.target.y - this.y;
this.dist = Math.sqrt(dx * dx + dy * dy);
};
line.prototype.updateAngle = function () {
var dx = this.target.x - this.x,
dy = this.target.y - this.y;
this.angle = Math.atan2(dy, dx);
};
line.prototype.changeTarget = function () {
var randStart = randInt(0, 3);
switch (randStart) {
case 0: // up
this.target.y = this.y - size;
break;
case 1: // right
this.target.x = this.x + size;
break;
case 2: // down
this.target.y = this.y + size;
break;
case 3: // left
this.target.x = this.x - size;
}
this.updateAngle();
};
line.prototype.draw = function (i) {
ctx.beginPath();
var rando = rand(0, 10);
for (var j = 0, length = this.path.length; j < length; j++) {
ctx[(j === 0) ? 'moveTo' : 'lineTo'](this.path[j].x + rand(-rando, rando), this.path[j].y + rand(-rando, rando));
}
ctx.strokeStyle = 'hsla(' + rand(this.hue, this.hue + 30) + ', 80%, 55%, ' + (this.life / 3) + ')';
ctx.lineWidth = rand(0.1, 2);
ctx.stroke();
};
function rand(min, max) {
return Math.random() * (max - min) + min;
};
function randInt(min, max) {
return Math.floor(min + Math.random() * (max - min + 1));
};
function init() {
canvas = document.querySelector(el);
ctx = canvas.getContext('2d');
size = 30;
lines = [];
reset();
loop();
};
function reset() {
canvas.width = Math.ceil((width || window.innerWidth) / 2) * 2;
canvas.height = Math.ceil((height || window.innerHeight) / 2) * 2;
lines.length = 0;
tick = 0;
};
function create() {
if (tick % 10 === 0) {
lines.push(new line());
}
};
function step() {
var i = lines.length;
while (i--) {
lines[i].step(i);
};
};
function clear() {
ctx.globalCompositeOperation = 'destination-out';
ctx.fillStyle = 'hsla(0, 0%, 0%, 0.1';
ctx.fillRect(0, 0, width, height);
ctx.globalCompositeOperation = 'lighter';
};
function draw() {
ctx.save();
ctx.translate(width / 2, height / 2);
ctx.rotate(tick * 0.001);
var scale = 0.8 + Math.cos(tick * 0.02) * 0.2;
ctx.scale(scale, scale);
ctx.translate(-width / 2, -height / 2);
var i = lines.length;
while (i--) {
lines[i].draw(i);
}
ctx.restore();
};
function loop() {
requestAnimationFrame(loop);
create();
step();
clear();
draw();
tick++;
};
function onresize() {
reset();
};
window.addEventListener('resize', onresize);
init();
};
// 调用函数
matrix('#matrix', 500, 500);
粒子漩涡
<canvas id="particleVortex"></canvas>
// 函数主体
function particleVortex(el, width, height) {
function project3D(x, y, z, vars) {
var p, d;
x -= vars.camX;
y -= vars.camY - 8;
z -= vars.camZ;
p = Math.atan2(x, z);
d = Math.sqrt(x * x + z * z);
x = Math.sin(p - vars.yaw) * d;
z = Math.cos(p - vars.yaw) * d;
p = Math.atan2(y, z);
d = Math.sqrt(y * y + z * z);
y = Math.sin(p - vars.pitch) * d;
z = Math.cos(p - vars.pitch) * d;
var rx1 = -1000;
var ry1 = 1;
var rx2 = 1000;
var ry2 = 1;
var rx3 = 0;
var ry3 = 0;
var rx4 = x;
var ry4 = z;
var uc = (ry4 - ry3) * (rx2 - rx1) - (rx4 - rx3) * (ry2 - ry1);
var ua = ((rx4 - rx3) * (ry1 - ry3) - (ry4 - ry3) * (rx1 - rx3)) / uc;
var ub = ((rx2 - rx1) * (ry1 - ry3) - (ry2 - ry1) * (rx1 - rx3)) / uc;
if (!z) z = 0.000000001;
if (ua > 0 && ua < 1 && ub > 0 && ub < 1) {
return {
x: vars.cx + (rx1 + ua * (rx2 - rx1)) * vars.scale,
y: vars.cy + y / z * vars.scale,
d: (x * x + y * y + z * z)
};
} else {
return {
d: -1
};
}
};
function elevation(x, y, z) {
var dist = Math.sqrt(x * x + y * y + z * z);
if (dist && z / dist >= -1 && z / dist <= 1) return Math.acos(z / dist);
return 0.00000001;
};
function rgb(col) {
col += 0.000001;
var r = parseInt((0.5 + Math.sin(col) * 0.5) * 16);
var g = parseInt((0.5 + Math.cos(col) * 0.5) * 16);
var b = parseInt((0.5 - Math.sin(col) * 0.5) * 16);
return '#' + r.toString(16) + g.toString(16) + b.toString(16);
};
function interpolateColors(RGB1, RGB2, degree) {
var w2 = degree;
var w1 = 1 - w2;
return [w1 * RGB1[0] + w2 * RGB2[0], w1 * RGB1[1] + w2 * RGB2[1], w1 * RGB1[2] + w2 * RGB2[2]];
};
function rgbArray(col) {
col += 0.000001;
var r = parseInt((0.5 + Math.sin(col) * 0.5) * 256);
var g = parseInt((0.5 + Math.cos(col) * 0.5) * 256);
var b = parseInt((0.5 - Math.sin(col) * 0.5) * 256);
return [r, g, b];
};
function colorString(arr) {
var r = parseInt(arr[0]);
var g = parseInt(arr[1]);
var b = parseInt(arr[2]);
return '#' + ('0' + r.toString(16)).slice(-2) + ('0' + g.toString(16)).slice(-2) + ('0' + b.toString(16)).slice(-2);
};
function process(vars) {
if (vars.points.length < vars.initParticles)
for (var i = 0; i < 5; ++i) spawnParticle(vars);
var p, d, t;
p = Math.atan2(vars.camX, vars.camZ);
d = Math.sqrt(vars.camX * vars.camX + vars.camZ * vars.camZ);
d -= Math.sin(vars.frameNo / 80) / 25;
t = Math.cos(vars.frameNo / 300) / 165;
vars.camX = Math.sin(p + t) * d;
vars.camZ = Math.cos(p + t) * d;
vars.camY = -Math.sin(vars.frameNo / 220) * 15;
vars.yaw = Math.PI + p + t;
vars.pitch = elevation(vars.camX, vars.camZ, vars.camY) - Math.PI / 2;
var t;
for (var i = 0; i < vars.points.length; ++i) {
x = vars.points[i].x;
y = vars.points[i].y;
z = vars.points[i].z;
d = Math.sqrt(x * x + z * z) / 1.0075;
t = .1 / (1 + d * d / 5);
p = Math.atan2(x, z) + t;
vars.points[i].x = Math.sin(p) * d;
vars.points[i].z = Math.cos(p) * d;
vars.points[i].y += vars.points[i].vy * t * ((Math.sqrt(vars.distributionRadius) - d) * 2);
if (vars.points[i].y > vars.vortexHeight / 2 || d < .25) {
vars.points.splice(i, 1);
spawnParticle(vars);
}
};
};
function drawFloor(vars) {
var x, y, z, d, point, a;
for (var i = -25; i <= 25; i += 1) {
for (var j = -25; j <= 25; j += 1) {
x = i * 2;
z = j * 2;
y = vars.floor;
d = Math.sqrt(x * x + z * z);
point = project3D(x, y - d * d / 85, z, vars);
if (point.d != -1) {
size = 1 + 15000 / (1 + point.d);
a = 0.15 - Math.pow(d / 50, 4) * 0.15;
if (a > 0) {
vars.ctx.fillStyle = colorString(interpolateColors(rgbArray(d / 26 - vars.frameNo / 40), [0, 128, 32], .5 +
Math.sin(d / 6 - vars.frameNo / 8) / 2));
vars.ctx.globalAlpha = a;
vars.ctx.fillRect(point.x - size / 2, point.y - size / 2, size, size);
}
}
}
};
vars.ctx.fillStyle = '#82f';
for (var i = -25; i <= 25; i += 1) {
for (var j = -25; j <= 25; j += 1) {
x = i * 2;
z = j * 2;
y = -vars.floor;
d = Math.sqrt(x * x + z * z);
point = project3D(x, y + d * d / 85, z, vars);
if (point.d != -1) {
size = 1 + 15000 / (1 + point.d);
a = 0.15 - Math.pow(d / 50, 4) * 0.15;
if (a > 0) {
vars.ctx.fillStyle = colorString(interpolateColors(rgbArray(-d / 26 - vars.frameNo / 40), [32, 0, 128], .5 +
Math.sin(-d / 6 - vars.frameNo / 8) / 2));
vars.ctx.globalAlpha = a;
vars.ctx.fillRect(point.x - size / 2, point.y - size / 2, size, size);
}
}
}
};
};
function sortFunction(a, b) {
return b.dist - a.dist;
};
function draw(vars) {
vars.ctx.globalAlpha = .15;
vars.ctx.fillStyle = '#000';
vars.ctx.fillRect(0, 0, vars.canvas.width, vars.canvas.height);
drawFloor(vars);
var point, x, y, z, a;
for (var i = 0; i < vars.points.length; ++i) {
x = vars.points[i].x;
y = vars.points[i].y;
z = vars.points[i].z;
point = project3D(x, y, z, vars);
if (point.d != -1) {
vars.points[i].dist = point.d;
size = 1 + vars.points[i].radius / (1 + point.d);
d = Math.abs(vars.points[i].y);
a = .8 - Math.pow(d / (vars.vortexHeight / 2), 1000) * .8;
vars.ctx.globalAlpha = a >= 0 && a <= 1 ? a : 0;
vars.ctx.fillStyle = rgb(vars.points[i].color);
if (point.x > -1 && point.x < vars.canvas.width && point.y > -1 && point.y < vars.canvas.height)
vars.ctx.fillRect(point.x - size / 2, point.y - size / 2, size, size);
}
};
vars.points.sort(sortFunction);
};
function spawnParticle(vars) {
var p, ls;
pt = {};
p = Math.PI * 2 * Math.random();
ls = Math.sqrt(Math.random() * vars.distributionRadius);
pt.x = Math.sin(p) * ls;
pt.y = -vars.vortexHeight / 2;
pt.vy = vars.initV / 20 + Math.random() * vars.initV;
pt.z = Math.cos(p) * ls;
pt.radius = 200 + 800 * Math.random();
pt.color = pt.radius / 1000 + vars.frameNo / 250;
vars.points.push(pt);
};
function frame(vars) {
if (vars === undefined) {
var vars = {};
vars.canvas = document.querySelector(el);
vars.ctx = vars.canvas.getContext('2d');
vars.canvas.width = width || document.documentElement.clientWidth;
vars.canvas.height = height || document.documentElement.clientHeight;
window.addEventListener('resize', function () {
vars.canvas.width = width || document.documentElement.clientWidth;
vars.canvas.height = height || document.documentElement.clientHeight;
vars.cx = vars.canvas.width / 2;
vars.cy = vars.canvas.height / 2;
}, true);
vars.frameNo = 0;
vars.camX = 0;
vars.camY = 0;
vars.camZ = -14;
vars.pitch = elevation(vars.camX, vars.camZ, vars.camY) - Math.PI / 2;
vars.yaw = 0;
vars.cx = vars.canvas.width / 2;
vars.cy = vars.canvas.height / 2;
vars.bounding = 10;
vars.scale = 500;
vars.floor = 26.5;
vars.points = [];
vars.initParticles = 700;
vars.initV = .01;
vars.distributionRadius = 800;
vars.vortexHeight = 25;
}
vars.frameNo++;
requestAnimationFrame(function () {
frame(vars);
});
process(vars);
draw(vars);
};
frame();
};
// 调用函数
particleVortex('#particleVortex', 500, 500);
二、点击特效
点击出现爱心
(function (win, doc) {
let hearts = [];
let heartStyle =
`.heart{position:fixed;width:10px;height:10px;background-color:#fff;transform:rotate(45deg);z-index:9}.heart::before,.heart::after{position:absolute;width:inherit;height:inherit;content:'';background-color:inherit;border-radius:50%}.heart::before{top:-5px}.heart::after{left:-5px}`;
// 定义不同浏览器下的requestAnimationFrame函数实现,若都没有则用setTimeout实现
window.requestAnimationFrame = (function () {
return win.requestAnimationFrame ||
win.webkitRequestAnimationFrame ||
win.mozCancelAnimationFrame ||
win.oRequestAnimationFrame ||
win.msCancelRequestAnimationFrame ||
function (callback) {
setTimeout(callback, 1000 / 60);
};
})();
// 生成随机颜色
function randomColor() {
return `rgb(${~~(Math.random() * 255)},${~~(Math.random() * 255)},${~~(Math.random() * 255)})`;
};
// 生成样式
function css(css) {
let style = doc.createElement('style');
style.type = 'text/css';
try {
style.appendChild(doc.createTextNode(css));
} catch (err) {
style.stylesheet.cssText = css;
}
doc.getElementsByTagName('head')[0].appendChild(style);
};
// 创建爱心
function createHeart(event) {
let div = doc.createElement('div');
div.className = 'heart';
// 给hearts数组添加heart对象
hearts.push({
el: div,
x: event.clientX - 5, // 鼠标当前位置x轴 - 5
y: event.clientY - 5, // 鼠标当前位置y轴 - 5
scale: 1, // 缩放
alpha: 1, // 透明度
color: randomColor() // 颜色(随机颜色)
});
doc.body.appendChild(div);
};
// 移除已生成爱心
function removeHeart() {
for (let i = 0; i < hearts.length; i++) {
if (hearts[i].alpha <= 0) { // 若当前heart对象的透明度小于等于0
doc.body.removeChild(hearts[i].el); // 从body中移除当前heart对象
hearts.splice(i, 1); // 从heart数组中移除当前heart对象
continue;
}
hearts[i].y--;
hearts[i].scale += 0.004;
hearts[i].alpha -= 0.013;
hearts[i].el.style.cssText =
`top:${hearts[i].y}px;left:${hearts[i].x}px;background-color:${hearts[i].color};opacity:${hearts[i].alpha};transform:scale(${hearts[i].scale},${hearts[i].scale}) rotate(45deg)`;
}
requestAnimationFrame(removeHeart);
};
// 点击事件
function clickEvent() {
let click = typeof win.onclick === 'function' && win.onclick;
win.onclick = function (event) {
click && click();
createHeart(event);
};
};
// 初始化
function init() {
css(heartStyle); // 添加爱心样式
clickEvent(); // 添加点击事件(生成爱心)
removeHeart(); // 移除已生成爱心
};
init();
})(window, document);