下列 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: // upthis.target.y = this.y - size;break;case 1: // rightthis.target.x = this.x + size;break;case 2: // downthis.target.y = this.y + size;break;case 3: // leftthis.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轴 - 5y: event.clientY - 5, // 鼠标当前位置y轴 - 5scale: 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对象的透明度小于等于0doc.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);
