simplex-noise实现canvas闪电交互动画效果代码
代码语言:html
所属分类:动画
代码描述:simplex-noise实现canvas闪电交互动画效果代码
代码标签: simplex-noise canvas 闪电 交互 动画
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <style> body { font-family: Helvetica sans-serif; padding: 0; margin: 0; background-color: #222; overflow: hidden; -webkit-user-select: none; -moz-user-select: none; -o-user-select: none; -ms-user-select: none; user-select: none; } canvas { position: absolute; top: 0; left: 0; } </style> </head> <body > <canvas id='c'></canvas> <script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/simplex-noise.js"></script> <script> /** * requestAnimationFrame */ window.requestAnimationFrame = function () { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {window.setTimeout(callback, 1000 / 60);}; }(); /** * Lightning */ var Lightning = function (window) { /** * LightningAbstract */ var LightningAbstract = { points: null, children: null, _simplexNoise: new SimplexNoise(), render: function (ctx, controls) { this._update(controls); this._draw(ctx); }, _update: function (controls) { throw new Error('Not override'); }, _draw: function (ctx) { var points = this.points, isRoot = false,opts, p,p1,dx,dy,dist, lineWidth, i,len; isRoot = !this.parent; opts = isRoot ? this : this.parent; if (isRoot) {// is root var radius,gradient, children = this.children,c; ctx.save(); for (i = 0, len = points.length; i < len; i += len - 1) { p = points[i]; radius = Math.random() * (8 - 3) + 3; gradient = ctx.createRadialGradient(p.x, p.y, radius / 3, p.x, p.y, radius); gradient.addColorStop(0, this._colorToString(1)); gradient.addColorStop(1, this._colorToString(0)); ctx.fillStyle = gradient; ctx.beginPath(); ctx.arc(p.x, p.y, radius, 0, Math.PI * 2, false); ctx.fill(); } ctx.restore(); for (i = 0, len = children.length; i < len; i += len - 1) { children[i].render(ctx); } } ctx.save(); ctx.globalCompositeOperation = 'lighter'; ctx.lineCap = 'round'; ctx.fillStyle = 'rgba(0, 0, 0, 1)'; ctx.shadowBlur = opts.blur; ctx.shadowColor = this._colorToString(1); ctx.beginPath(); for (i = 0, len = points.length; i < len; i++) { p = points[i]; if (len > 1) { p1 = points[i === len - 1 ? i - 1 : i + 1]; dx = p.x - p1.x; dy = p.y - p1.y; dist = Math.sqrt(dx * dx + dy * dy); } else { dist = 0; } if (dist > 30) dist = 30; ctx.moveTo(p.x + dist, p.y); ctx.arc(p.x, p.y, dist, 0, Math.PI * 2, false); } ctx.fill(); ctx.restore(); ctx.save(); ctx.beginPath(); ctx.strokeStyle = this._colorToString(Math.random() * (opts.maxAlpha - opts.minAlpha) + opts.minAlpha); lineWidth = Math.random() * (opts.maxLineWidth - opts.minLineWidth) + opts.minLineWidth; ctx.lineWidth = isRoot ? lineWidth : lineWidth * 0.5; for (i = 0; i < len; i++) { p = points[i]; ctx[i === 0 ? 'moveTo' : 'lineTo'](p.x, p.y); } ctx.stroke(); ctx.restore(); }, _noise2d: function (x, y) { var octaves = 3, fallout = 0.5, amp = 1,f = 1,sum = 0, i; for (i = 0; i < octaves; ++i) { amp *= fallout; sum += amp * (this._simplexNoise.noise2D(x * f, y * f) + 1) * 0.5; f *= 2; } return sum; }, _filterApply: function (points, lineLength, segmentsNum, base, amp, offset) { var pointsOld = this.points, // spline spline = [], catmullRom = this._catmullRom, p0,p1,p2,p3,t,per, // noise p,next,angle,sin,cos,nx,av,ax,ay,bv,bx,by,m,px,py, // shortest shortest,dx,dy,distSq,minDist, i,len,j,k; // Spline // スプライン補完用に配列の前後にラインの始点, 終点の参照をそれぞれ複製する points.unshift(points[0]); points.push(points[points.length - 1]); per = 1 / segmentsNum; // スプライン曲線のポイントを取得 for (i = 0, len = points.length - 3; i < len; i++) { p0 = points[i]; p1 = points[i + 1]; p2 = points[i + 2]; p3 = points[i + 3]; for (j = 0; j < segmentsNum; j++) { t = (j + 1) * per; spline.push({ x: catmullRom(p0.x, p1.x, p2.x, p3.x, t), y: catmullRom(p0.y, p1.y, p2.y, p3.y, t) }); } } // 補完用に追加した参照を削除 points.pop(); // 削除のついでに描画の始点として追加 spline.unshift(points.shift()); // Noise points = []; len = spline.length; per = 1 / (len - 1); base = 1 / base; for (i = 0, len = spline.length; i < len; i++) { p = spline[i]; next = i === len - 1 ? p : spline[i + 1]; angle = Math.atan2(next.y - p.y, next.x - p.x); sin = Math.sin(angle); cos = Math.cos(angle); nx = i * base; av = lineLength * this._noise2d(nx - offset, offset) * 0.5 * amp; ax = av * sin; ay = av * cos; bv = lineLength * this._noise2d(nx + offset, offset) * 0.5 * amp; bx = bv * sin; by = bv * cos; m = Math.sin(Math.PI * (i * per)); px = p.x + (ax - bx) * m; py = p.y - (ay - by) * m; if (pointsOld.length) { p = pointsOld.shift(); p.x = px; p.y = py; } else { p = { x: px, y: py }; } points.push(p); } // Shortest shortest = [points[0]]; for (i = 0, len = points.length; i < len; i++) { p = points[i]; minDist = Infinity; k = -1; for (j = i; j < len; j++) { p2 = points[j]; dx = p.x - p2.x; dy = p.y - p2.y; distSq = dx * dx + dy * dy; if (p !== p2 && distSq < minDist * minDist) { minDist = Math.sqrt(distSq); k = j; } } if (k < 0) break; shortest.push(points[k]); i = k - 1; } return shortest; }, _catmullRom: function (p0, p1, p2, p3, t) { var v0 = (p2 - p0) * 0.5, v1 = (p3 - p1) * 0.5; return (2 * p1 - 2 * p2 + v0 + v1) * t * t * t + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t * t + v0 * t + p1; }, _colorToString: function (alpha) { var c = this.color; return this.colorType === 'rgb' ? 'rgba(' + c.join(',') + ',' + alpha + ')' : 'hsla(' + c[0] + ',' + c[1] + '%,' + c[2] + '%,' + alpha + ')'; } }; /** * @constructor */ function Lightning(segmentsNum) { this.points = []; this.children = []; this._params = []; this._offsets = []; } Lightning.prototype = extend(LightningAbstract, { color: [255, 255, 255], colorType: 'rgb', blur: 50, maxAlpha: 1, minAlpha: 0.75, maxLineWidth: 5, minLineWidth: 0.5, _params: null, _offsets: null, addParam: function (segmentsNum, base, amplitude, speed) { this._params.push({ segmentsNum: segmentsNum, base: base, amplitude: amplitude, speed: speed }); this._offsets[this._params.length - 1] = 0; }, createChild: function (base, amplitude, speed) { var child = new LChild(this, { base: base || this._params.base, amplitude: amplitude || this._params.amplitude, speed: speed || this._params.speed }); this.children.push(child); return child; }, _update: function (points) { var params = this._params,param, offsets = this._offsets, lineLength, p0,p1,dx,dy, i,ilen,j,jlen; for (i = 0, ilen = params.length; i < ilen; i++) { param = params[i]; lineLength = 0; for (j = 0, jlen = points.length; j < jlen; j++) { if (j !== jlen - 1) { p0 = points[j]; p1 = points[j + 1]; dx = p0.x - p1.x; dy = p0.y - p1.y; lineLength += dx * dx + dy * dy; } } lineLength = Math.sqrt(lineLength); offsets[i] += Math.random() * param.speed; points = this._filterApply(points, lineLength, param.segmentsNum, param.base, param.amplitude, offsets[i]); } this.points = points; } }); /** * LChild */ function LChild(parent, param) { this.parent = parent; this.points = []; this._param = param; } LChild.prototype = extend(LightningAbstract, { parent: null, _startStep: 0, _endStep: 0, _separate: 2, _param: null, _offset: 0, _lastChangeTime: 0, _update: function () { var parent = this.parent, plen = this.parent.points.length, param = this._param, points = [], currentTime, range,rangeLen,sep,seg, c0,c1,dx,dy,lineLength, i,j; // 一定時間ごと, あるいは親のポイントの数が子の終了ステップ位置を下回った場合に始点と終点の親からの取得位置を更新する currentTime = new Date().getTime(); if ( currentTime - this._lastChangeTime > 10000 * Math.random() || plen < this._endStep) { var stepMin = plen * 0.1 | 0, startStep = this._startStep = Math.random() * (plen / 3 * 2 | 0) | 0; this._endStep = start.........完整代码请登录后点击上方下载按钮下载查看
网友评论0