Фон из неоновых молний на HTML5 Canvas
1 2 3 4 5 |
#c { position: absolute; top: 0; left: 0; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
<canvas id="c"></canvas> <script src="/js/dat.gui.min.js"></script> <script> // if you just don't want to read the code but still are interested in how it works, here's a guide for you: http://codepen.io/towc/blog/a-guide-to-wavy-waved-radial-waves/ var w = c.width = window.innerWidth, h = c.height = window.innerHeight, ctx = c.getContext('2d'), opts = { rays: 30, maxRadius: Math.sqrt(w * w / 4 + h * h / 4), circleRadiusIncrementAcceleration: 2, radiantSpan: .4, rayAngularVelSpan: .005, rayAngularVelLineWidthMultiplier: 60, rayAngularAccWaveInputBaseIncrementer: .03, rayAngularAccWaveInputAddedIncrementer: .02, rayAngularAccWaveMultiplier: .0003, baseWaveInputIncrementer: .01, addedWaveInputIncrementer: .01, circleNumWaveIncrementerMultiplier: .1, cx: w / 2, cy: h / 2, tickHueMultiplier: 1, shadowBlur: 0, repaintAlpha: .2, apply: init }, rays = [], tick = 0, tickHueMultiplied, gui = new dat.GUI; function init() { rays.length = 0; for (var i = 0; i < opts.rays; ++i) rays.push(new Ray); if (tick === 0) { for (opt in opts) { if (isNaN(opts[opt])) gui.add(opts, opt); else gui.add(opts, opt, 0, opts[opt] * 3) } gui.close(); loop(); } } function loop() { window.requestAnimationFrame(loop); ++tick; ctx.globalCompositeOperation = 'source-over'; ctx.shadowBlur = 0; ctx.fillStyle = 'rgba(0,0,0,alp)'.replace('alp', opts.repaintAlpha); ctx.fillRect(0, 0, w, h); ctx.shadowBlur = opts.shadowBlur; ctx.globalCompositeOperation = 'lighter'; tickHueMultiplied = opts.tickHueMultiplier * tick; rays.map(function(ray) { ray.step(); }); } function Ray() { this.circles = [new Circle(0)]; this.rot = Math.random() * Math.PI * 2; this.angularVel = Math.random() * opts.rayAngularVelSpan * (Math.random() < .5 ? 1 : -1); this.angularAccWaveInput = Math.random() * Math.PI * 2; this.angularAccWaveInputIncrementer = opts.rayAngularAccWaveInputBaseIncrementer + opts.rayAngularAccWaveInputAddedIncrementer * Math.random(); var security = 100, count = 0; while (--security > 0 && this.circles[count].radius < opts.maxRadius) this.circles.push(new Circle(++count)); } Ray.prototype.step = function() { // this is just messy, but if you take your time to read it properly you'll understand it pretty easily this.rot += this.angularVel += Math.sin(this.angularAccWaveInput += this.angularAccWaveInputIncrementer) * opts.rayAngularAccWaveMultiplier; var rot = this.rot, x = opts.cx, y = opts.cy; ctx.lineWidth = Math.min(.00001 / Math.abs(this.angularVel), 10 / opts.rayAngularVelLineWidthMultiplier) * opts.rayAngularVelLineWidthMultiplier; ctx.beginPath(); ctx.moveTo(x, y); for (var i = 0; i < this.circles.length; ++i) { var circle = this.circles[i]; circle.step(); rot += circle.radiant; var x2 = opts.cx + Math.sin(rot) * circle.radius, y2 = opts.cy + Math.cos(rot) * circle.radius, mx = (x + x2) / 2, my = (y + y2) / 2; ctx.quadraticCurveTo(x, y, mx, my); x = x2; y = y2; } ctx.strokeStyle = ctx.shadowColor = 'hsl(hue,80%,50%)'.replace('hue', (((rot + this.rot) / 2) % (Math.PI * 2)) / Math.PI * 30 + tickHueMultiplied); ctx.stroke(); } function Circle(n) { this.radius = opts.circleRadiusIncrementAcceleration * Math.pow(n, 2); this.waveInputIncrementer = (opts.baseWaveInputIncrementer + opts.addedWaveInputIncrementer * Math.random()) * (Math.random() < .5 ? 1 : -1) * opts.circleNumWaveIncrementerMultiplier * n; this.waveInput = Math.random() * Math.PI * 2; this.radiant = Math.random() * opts.radiantSpan * (Math.random() < .5 ? 1 : -1); } Circle.prototype.step = function() { this.waveInput += this.waveInputIncrementer; this.radiant = Math.sin(this.waveInput) * opts.radiantSpan; } init(); window.addEventListener('resize', function() { w = c.width = window.innerWidth; h = c.height = window.innerHeight; opts.maxRadius = Math.sqrt(w * w / 4 + h * h / 4); opts.cx = w / 2; opts.cy = h / 2; init(); }); c.addEventListener('click', function(e) { opts.cx = e.clientX; opts.cy = e.clientY; }); </script> |