var settings = { timeRandom: 1000, // max time in ms between random Fireworks particlesBase: 80, // min number of particles per Firework particlesRandom: 20, // max number of additional random particles per firework sizeBase: 8, // min size of particles sizeRandom: 10, // max additional random size velocityBase: 4, // min velocity of particles velocityRandom: 4, // max additional random velocity lifetimeBase: 800, // min lifetime of particle lifetimeRandom: 400, // max additional random lifetime gravity: 0.06 // gravity of particles } ;(function(require, window, document, undefined) { require.config({ baseUrl: 'http://cdn.qoopido.com/portfolio/assets/vendor/qoopido.js/dist/3.5.0/min' }); var MATH_PI_TIMES_TWO = 2 * Math.PI, MATH_PI_DIV_TWO = Math.PI / 2, COLORS = [ '33,141,166', '149,186,0', '255,180,0', '166,60,148', '255,255,51', '255,0,102', '255,140,0' ], EASINGS = [ easeInSine, easeInQuad, easeInCubic ]; function easeInSine(t, d) { return 1 * Math.cos(t / d * MATH_PI_DIV_TWO) -1 + 1; } function easeInQuad(t, d) { return -1 * (t /= d) * t + 1; } function easeInCubic(t, d) { return -1 * (t /= d) * t * t + 1; } require([ 'base' ], function() { require([ 'pool/module', 'pool/dom', 'pool/array', 'pool/object' ], function(mPoolModule, mPoolDom) { require([ 'support', 'renderer', 'particle', 'dom/element', 'dom/collection', 'vector/2d' ], function(mSupport, mRenderer, mParticle, mDomElement, mDomCollection, mVector) { var qWindow = mDomElement.create(window), qCanvas = mDomElement.create('').insertBefore('#console'), qCanvasElement = qCanvas.element, qCanvasContext = qCanvasElement.getContext('2d'), renderer = mRenderer, poolModule = mPoolModule.create(mParticle), poolDom = mPoolDom.create(), poolVector = mPoolModule.create(mVector), poolMetrics = poolModule.metrics, consoleFps = mDomElement.create('#fps'), consoleTotal = mDomElement.create('#total'), consoleRecycled = mDomElement.create('#recycled'), consoleDestroyed = mDomElement.create('#destroyed'), consoleInPool = mDomElement.create('#inpool'), consoleInUse = mDomElement.create('#inuse'), consoleInQueue = mDomElement.create('#inqueue'), width = 0, height = 0, now = new Date().getTime(), last = now, delta = 0, fps = 60, ratio = 1, particles = [], paused = false; function generateRandomFirework() { if(paused === false) { addFirework(); } setTimeout(generateRandomFirework, Math.random() * settings.timeRandom); } function addFirework(event) { var count = ((Math.random() * settings.particlesRandom + settings.particlesBase) / ratio) >> 0, x = event ? parseInt(event.clientX || event.pageX, 10) : (Math.random() * width) >> 0, y = event ? parseInt(event.clientY || event.pageY, 10) : (Math.random() * height) >> 0, angle = MATH_PI_TIMES_TWO / count, color = ''.concat('rgba(', COLORS[((Math.random() * (COLORS.length - 1)) + 0.5) >> 0], ','), size = (size = ((Math.random() * settings.sizeRandom) >> 0) + settings.sizeBase) + (size % 2), canvas = poolDom.obtain('canvas'), acceleration = poolVector.obtain(0, settings.gravity), hSize, context, fill; canvas.width = canvas.height = size; canvas.usage = count; acceleration.usage = count; hSize = size / 2; context = canvas.getContext('2d'); fill = context.createRadialGradient(hSize, hSize, 0, hSize, hSize, hSize); fill.addColorStop(0, ''.concat(color, '1)')); fill.addColorStop(1, ''.concat(color, '0)')); context.arc(hSize, hSize, hSize, 0, MATH_PI_TIMES_TWO, false); context.fillStyle = fill; context.fill(); while(count--) { var randomVelocity = settings.velocityBase + Math.random() * settings.velocityRandom, particleAngle = count * angle, particle = poolModule.obtain(x, y); particle.velocity.x = Math.cos(particleAngle) * randomVelocity; particle.velocity.y = Math.sin(particleAngle) * randomVelocity; particle.acceleration.push(acceleration); particle.canvas = canvas; particle.size = size; particle.easing = particle.easing ? particle.easing : EASINGS[(Math.random() * (EASINGS.length - 1) + 0.5) >> 0]; particle.birthtime = now; particle.lifetime = particle.lifetime? particle.lifetime : (settings.lifetimeBase + Math.random() * settings.lifetimeRandom) >> 0; particle.deathtime = now + particle.lifetime; particles.push(particle); } if(event) { event.preventDefault(); event.stopPropagation(); return false; } } function renderParticle(particle) { qCanvasContext.save(); qCanvasContext.globalCompositeOperation = 'lighter'; qCanvasContext.globalAlpha = Math.random() * particle.easing(now - particle.birthtime, particle.lifetime); qCanvasContext.drawImage(particle.canvas, particle.position.x >> 0, particle.position.y >> 0); qCanvasContext.restore(); } function initializeSettings() { var definitions = {}; mDomCollection .create('#console [data-setting]') .each(function() { var self = this, parameter = self.getAttribute('data-setting').split(':'), setting = parameter[0], range = parameter[1].split(','), stepping = parseFloat(range.pop()), previous = mDomElement.create('', { 'data-control': setting }).addClass('previous').setContent('-').appendTo(self), value = mDomElement.create('').addClass('value').setContent(settings[setting]).appendTo(self), next = mDomElement.create('', { 'data-control': setting }).addClass('next').setContent('+').appendTo(self); definitions[setting] = { value: value, range: range, stepping: stepping } }); mDomElement .create('#console') .on('click', '[data-control]', function(event) { var self = mDomElement.create(this), setting = self.getAttribute('data-control'), definition = definitions[setting]; event.preventDefault(); event.stopPropagation(); if(self.hasClass('previous')) { definition.value.setContent(settings[setting] = Math.max(definition.range[0], Math.min(definition.range[1], settings[setting] - definition.stepping))); } else if(self.hasClass('next')) { definition.value.setContent(settings[setting] = Math.max(definition.range[0], Math.min(definition.range[1], settings[setting] + definition.stepping))); } return false; }); } function onResize() { width = qCanvasElement.width = window.innerWidth; height = qCanvasElement.height = window.innerHeight; } function onTick(type, lFps, lRatio) { var i = 0, particle, position, size, canvas, acceleration; now = new Date().getTime(); fps = (fps + lFps) / 2; ratio = lRatio; delta = now - last; qCanvasContext.fillStyle = 'rgba(0,0,0,0.15)'; qCanvasContext.fillRect(0, 0, width, height); for(; (particle = particles[i]) !== undefined; i++) { particle.update(); position = particle.position; size = particle.size; if(now > particle.deathtime || position.x < -size || position.y < -size || position.x > width + size || position.y > height + size) { particles.splice(i, 1); canvas = particle.canvas, acceleration = particle.acceleration[0]; particle.dispose(); canvas.usage -= 1; acceleration.usage -= 1; if(canvas.usage <= 0) { canvas = particle.canvas = canvas.dispose(); } if(acceleration.usage <= 0) { acceleration = acceleration.dispose(); } i -= 1; } else { renderParticle(particle); } } if(delta > 1000) { consoleFps.setContent(fps >> 0); last = now; fps = lFps; } consoleTotal.setContent(poolMetrics.total); consoleRecycled.setContent(poolMetrics.recycled); consoleDestroyed.setContent(poolMetrics.destroyed); consoleInPool.setContent(poolMetrics.inPool); consoleInUse.setContent(poolMetrics.inUse); consoleInQueue.setContent(poolMetrics.inQueue); } function onSuspend() { paused = true; } function onResume(type, delta) { var i = 0, particle; for(; (particle = particles[i]) !== undefined; i++) { particle.birthtime += delta; particle.deathtime += delta; } paused = false; } qWindow.on('resize orientationchange', onResize); qCanvas.on('touchstart click', addFirework); renderer.on('tick', onTick); renderer.on('suspend', onSuspend); renderer.on('resume', onResume); initializeSettings(); onResize(); generateRandomFirework(); }); }); }); }(require, window, document));