<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <link href="https://cdn.bootcss.com/meyer-reset/2.0/reset.css" rel="stylesheet"> <title>particleEditor.json</title> <style> html, body { margin: 0; background: #131417; color: #FFF; font-family: 'Inconsolata', monospace; height: 100%; } body { box-sizing: border-box; padding: 1em; font-size: 18px; display: flex; } @media (max-width: 650px) { body { font-size: 16px; } } @media (max-width: 500px) { body { font-size: 14px; } } .container { margin: auto; width: 30em; } .container * { box-sizing: border-box; } .container .title { margin-bottom: 0.5em; opacity: 0; -webkit-animation: inDown 0.25s forwards; animation: inDown 0.25s forwards; -webkit-animation-delay: 0.5s; animation-delay: 0.5s; } .container .scene { padding-top: 50%; position: relative; -webkit-animation: inUp 0.25s; animation: inUp 0.25s; } .container .scene .info { position: absolute; z-index: 100; top: 0.5em; left: 0.5em; opacity: 0; pointer-events: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .container .scene:hover .info { opacity: 1; } .container .scene canvas { background: #333; position: absolute; top: 0; left: 0; width: 100%; height: 100%; border-radius: 3px; image-rendering: optimizeSpeed; /* Legal fallback */ image-rendering: -moz-crisp-edges; /* Firefox */ image-rendering: -o-crisp-edges; /* Opera */ image-rendering: -webkit-optimize-contrast; /* Safari */ image-rendering: optimize-contrast; /* CSS3 Proposed */ image-rendering: crisp-edges; /* CSS4 Proposed */ image-rendering: pixelated; /* CSS4 Proposed */ -ms-interpolation-mode: nearest-neighbor; /* IE8+ */ } .container .scene .scale { position: absolute; bottom: 0; right: 0; display: flex; } .container .scene .scale .btn, .container .scene .scale .label { min-width: 1.5em; height: 1.5em; line-height: 1.5em; text-align: center; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .container .scene .scale .btn { cursor: pointer; } .container .scene .scale .btn:hover { background: rgba(255, 255, 255, 0.1); } .container .settings { margin-top: 1em; margin-bottom: 1em; padding: 1em; background: #333; border-radius: 3px; height: 15em; opacity: 0; -webkit-animation: inDown 0.25s forwards; animation: inDown 0.25s forwards; -webkit-animation-delay: 0.25s; animation-delay: 0.25s; } .container .settings textarea { font-family: 'Inconsolata', monospace; width: 100%; height: 100%; font-size: 0.9em; padding: 0; resize: none; color: #FFF; border: none; background: none; outline: none; color: #4d7; letter-spacing: 0.05em; line-height: 1.5em; } @-webkit-keyframes inUp { from { -webkit-transform: translateY(-100%); transform: translateY(-100%); opacity: 0; } to { -webkit-transform: translateY(0%); transform: translateY(0%); opacity: 1; } } @keyframes inUp { from { -webkit-transform: translateY(-100%); transform: translateY(-100%); opacity: 0; } to { -webkit-transform: translateY(0%); transform: translateY(0%); opacity: 1; } } @-webkit-keyframes inDown { from { -webkit-transform: translateY(100%); transform: translateY(100%); opacity: 0; } to { -webkit-transform: translateY(0%); transform: translateY(0%); opacity: 1; } } @keyframes inDown { from { -webkit-transform: translateY(100%); transform: translateY(100%); opacity: 0; } to { -webkit-transform: translateY(0%); transform: translateY(0%); opacity: 1; } } </style> </head> <body> <a href="https://codepen.io/sean_codes/pen/mvmjNa" target="_blank" rel="noopener noreferrer">https://codepen.io/sean_codes/pen/mvmjNa</a> <div class="container"> <div class="title">particleEditor.json</div> <div class="scene"> <canvas></canvas> <div class="info">move mouse/click to burst</div> <div class="scale"> <div class="btn scale-down">-</div> <div class="label">1</div> <div class="btn scale-up">+</div> </div> </div> <div class="settings"> <textarea></textarea> </div> </div> </body> <script> console.clear() var canvas = document.querySelector('canvas') var particleSystem = new ParticleSystem(canvas) var settings = JSON.parse(`{ "shape": "circle", "pattern": "chaos", "width": 100, "height": 0, "count": 5, "interval": 1, "gaussian": 1, "life": 120, "colors": [ [ "#fff", "#fff" ], [ "#f22", "#fff" ] ], "vel": { "min": 0.1, "max": 4 }, "accel": 0, "friction": 0.99, "size": { "min": 1, "max": 4 }, "grow": 0, "wobble": { "time": 30, "amount": 0.25 }, "direction": { "min": 270, "max": 270 }, "alpha": 1, "fade": 0.02 }`) //----------------------------------------------------------------------------------// //----------------------------------| interface |-----------------------------------// //----------------------------------------------------------------------------------// var mouse = { down: false, over: false, x: 0, y: 0 } var html = {} html.textarea = document.querySelector('textarea') html.canvas = document.querySelector('canvas') html.scale = document.querySelector('.scale') html.scaleUp = html.scale.querySelector('.scale-up') html.scaleDown = html.scale.querySelector('.scale-down') html.scaleLabel = document.querySelector('.label') var scaleCanvas = function(amount) { var value = Number(this.html.scaleLabel.innerHTML) value = Math.min(Math.max(0.5, value+amount), 12) this.html.scaleLabel.innerHTML = value particleSystem.changeScale(value) } html.scaleUp.addEventListener('click', function() { this.scaleCanvas(0.5) }.bind(this)) this.html.scaleDown.addEventListener('click', function() { this.scaleCanvas(-0.5) }.bind(this)) this.html.textarea.innerHTML = JSON.stringify(settings, null, 3) this.html.textarea.addEventListener('input', function(e) { try { var newValue = e.target.value settings = JSON.parse(newValue) } catch(e) { console.log('json error', e) } }.bind(this)) this.html.canvas.addEventListener('mousemove', function(e) { mouse.x = e.layerX / particleSystem.scale mouse.y = e.layerY / particleSystem.scale }) this.html.canvas.addEventListener('mousedown', function(e) { e.preventDefault(); mouse.down = true }) this.html.canvas.addEventListener('mouseup', function(e) { mouse.down = false }) this.html.canvas.addEventListener('mouseover', function(e) { mouse.over = true }) this.html.canvas.addEventListener('mouseout', function(e) { mouse.down = false mouse.over = false }) //----------------------------------------------------------------------------------// //-------------------------------------| loop |-------------------------------------// //----------------------------------------------------------------------------------// setInterval(function() { particleSystem.step() if (mouse.over && !mouse.down) return var width = canvas.width / particleSystem.scale var height = canvas.height / particleSystem.scale // pushes the particle emmiter left/right/up/down var totalDirection = 1 - (settings.direction.max - settings.direction.min) / 360 var direction = settings.direction.min + (settings.direction.max - settings.direction.min)/2 var directionRadians = degreeToRadian(direction) var xPush = Math.cos(directionRadians) * (width/2*totalDirection*0.75) var yPush = Math.sin(directionRadians) * (height/2*totalDirection*0.5) var xBurst = (mouse.down ? mouse.x : width/2 - xPush) var yBurst = (mouse.down ? mouse.y : height/2 - yPush) particleSystem.burst({ x: xBurst, y: yBurst, ...settings }) // center line // particleSystem.ctx.beginPath() // particleSystem.ctx.moveTo(canvas.width/2, 0) // particleSystem.ctx.lineTo(canvas.width/2, canvas.height) // particleSystem.ctx.stroke() // area circle particleSystem.ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)' particleSystem.ctx.beginPath() particleSystem.ctx.arc(xBurst, yBurst, settings.width*settings.scale/2, 0, Math.PI*2) particleSystem.ctx.closePath() particleSystem.ctx.stroke() }, 1000/60) //----------------------------------------------------------------------------------// //-------------------------------------| system |-----------------------------------// //----------------------------------------------------------------------------------// function ParticleSystem(canvas) { this.canvas = canvas this.ctx = canvas.getContext('2d') this.particles = [] this.scale = 1 this.changeScale = function(value) { this.scale = value this.resize() } this.step = function() { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height) for (var particle of this.particles) { this.stepParticle(particle) } } this.burst = function(settings) { if (Math.random() < settings.interval) { for(var i = 0; i < settings.count; i++) this.createParticle(settings) } this.particles = this.particles.filter(function(particle) { return particle.life }) } this.createParticle = function(overrides) { var settings = {} settings = Object.assign(settings, overrides) var size = randomRange(settings.size.min, settings.size.max) var vel = randomRange(settings.vel.min, settings.vel.max) var color = settings.colors[Math.floor(Math.random() * settings.colors.length)] var colorStart = color[0] var colorEnd = color[1] var rgbStart = this.hexToRgb(colorStart) var rgbEnd = this.hexToRgb(colorEnd) var rgbChange = { r: (rgbEnd.r - rgbStart.r) / settings.life, g: (rgbEnd.g - rgbStart.g) / settings.life, b: (rgbEnd.b - rgbStart.b) / settings.life, } var totalGaussian = Math.random() * settings.gaussian var life = settings.life - (settings.life * totalGaussian) var direction = randomRange(settings.direction.min, settings.direction.max) if (settings.pattern === 'halo') { var x = canYouPleaseLearnRadiansCos(direction) * (settings.height/2) var y = iDontReallyWantToSin(direction) * (settings.height/2) } if (settings.pattern === 'orb') { var x = canYouPleaseLearnRadiansCos(direction) * (settings.width/2*Math.random()) var y = iDontReallyWantToSin(direction) * (settings.height/2*Math.random()) } if (settings.pattern === 'chaos') { var x = randomRange(-settings.width/2, settings.width/2) var y = randomRange(-settings.height/2, settings.height/2) x -= x * totalGaussian y -= y * totalGaussian } var particle = { color: { value: rgbStart, change: rgbChange }, x: settings.x + x, y: settings.y + y, size: size, direction: direction, vel: vel, friction: settings.friction, accel: settings.accel, alpha: settings.alpha, fade: settings.fade, grow: settings.grow, life: settings.life, shape: settings.shape, wobble: { time: settings.wobble.time, timer: settings.wobble.time, amount: settings.wobble.amount, direction: Math.random() > 0.5 ? 1 : -1, } } this.particles.unshift(particle) } this.stepParticle = function(particle) { particle.life -= 1 particle.vel += particle.accel particle.vel *= particle.friction particle.wobble.amount *= particle.friction var hSpeed = canYouPleaseLearnRadiansCos(particle.direction) * particle.vel var vSpeed = iDontReallyWantToSin(particle.direction) * particle.vel particle.x += hSpeed particle.y += vSpeed if (particle.wobble.time) { particle.direction += particle.wobble.amount * particle.wobble.direction // console.log(particle.direction) particle.wobble.timer -= 1 if (particle.wobble.timer <= 0) { particle.wobble.timer = particle.wobble.time particle.wobble.direction *= -1 } } particle.color.value.r += particle.color.change.r particle.color.value.g += particle.color.change.g particle.color.value.b += particle.color.change.b particle.alpha = Math.max(particle.alpha - particle.fade, 0) particle.size = Math.max(particle.size + particle.grow, 0) if (particle.alpha <= 0) particle.life = 0 if (particle.size <= 0) particle.life = 0 this.ctx.fillStyle = particle.color.value.toString() this.ctx.globalAlpha = particle.alpha if (particle.shape == 'square') { this.ctx.fillRect( particle.x * this.scale - (particle.size/2 * this.scale), particle.y * this.scale - (particle.size/2 * this.scale), particle.size * this.scale, particle.size * this.scale ) } if (particle.shape == 'circle') { this.ctx.beginPath() this.ctx.arc( particle.x * this.scale, particle.y * this.scale, particle.size * this.scale, 0, Math.PI*2 ) this.ctx.fill() } this.ctx.globalAlpha = 1 } this.hexToRgb = function(hex) { if(hex.length == 4) { hex = hex[0]+hex[1]+hex[1]+hex[2]+hex[2]+hex[3]+hex[3] } return { r: parseInt('0x' + hex.slice(1, 3)), g: parseInt('0x' + hex.slice(3, 5)), b: parseInt('0x' + hex.slice(5, 7)), toString: function() { return `rgb(${this.r}, ${this.g}, ${this.b})` } } } this.resize = function() { this.particles = [] this.canvas.width = this.canvas.clientWidth this.canvas.height = this.canvas.clientHeight } window.addEventListener('resize', this.resize.bind(this)) this.resize() } //----------------------------------------------------------------------------------// //-------------------------------------| boring |-----------------------------------// //----------------------------------------------------------------------------------// function randomRange(min, max) { return min + Math.random() * (max - min) } function degreeToRadian(degree) { return degree * (Math.PI/180) } function canYouPleaseLearnRadiansCos(degree) { return Math.cos(degreeToRadian(degree)) } function iDontReallyWantToSin(degree) { return Math.sin(degreeToRadian(degree)) } </script> </html> 提示:你可以先修改部分代码再运行。 转载请注明:有爱前端 » json控制canvas 动画 喜欢 (11)or分享 (0)