Creating different particle effects by manipulating DOM, without using canvas.
A Pen by Viraj Soni on CodePen.
<div id="smoke"></div> | |
<div id="flames"></div> | |
<div id="skyshot"></div> |
Creating different particle effects by manipulating DOM, without using canvas.
A Pen by Viraj Soni on CodePen.
function getsupportedprop(proparray) { | |
var root = document.documentElement //reference root element of document | |
for (var i = 0; i < proparray.length; i++) { //loop through possible properties | |
if (proparray[i] in root.style) { //if property exists on element (value will be string, empty string if not set) | |
return proparray[i] //return that string | |
} | |
} | |
} | |
var csstransform = getsupportedprop(['transform', 'MozTransform', 'webkitTransform', 'msTransform', 'OTransform']) | |
var csstransition = getsupportedprop(['transition', 'MozTransition', 'webkitTransition', 'msTransition', 'oTransition']) | |
var Emitter = function(opts, parent) { | |
this.particles = []; | |
this.opts = opts; | |
this.total = 0; | |
this.opts.i = Date.now(); | |
this.particleType = opts.particleType || Particle; | |
this.ele = document.createElement('div'); | |
this.ele.className = 'emitter'; | |
this.ele.setAttribute('aria-hidden', 'true'); | |
this.ele.setAttribute('style', 'position:absolute;-webkit-transform:translate(' + this.opts.center[0] + '%, ' + this.opts.center[1] + '%) translateZ(0);-moz-transform:translate(' + this.opts.center[0] + '%, ' + this.opts.center[1] + '%) translateZ(0);-ms-transform:translate(' + this.opts.center[0] + '%, ' + this.opts.center[1] + '%) translateZ(0);-o-transform:translate(' + this.opts.center[0] + '%, ' + this.opts.center[1] + '%) translateZ(0);transform:translate(' + this.opts.center[0] + '%, ' + this.opts.center[1] + '%) translateZ(0);'); | |
for (var i = 0; i < opts.particles; i++) { | |
this.total++; | |
this.particles.push(new this.particleType(this.ele, opts)); | |
} | |
parent = document.getElementById(opts.attachTo) | |
parent.appendChild(this.ele); | |
} | |
Emitter.prototype.destroy = function() { | |
this.ele.parentNode.removeChild(this.ele); | |
} | |
var Particle = function(parent, opts) { | |
// this.ele = this.pool.pop(); // Broken | |
if (!this.ele) { | |
this.ele = document.createElement('div'); | |
this.ele.setAttribute('style', 'position:absolute;'); | |
this.ele.className = 'particle'; | |
} | |
parent.appendChild(this.ele); | |
this.reset(opts); | |
var _this = this; | |
this.ele.addEventListener('webkitTransitionEnd', isTransitionOver, false); | |
this.ele.addEventListener('oTransitionEnd', isTransitionOver, false); | |
this.ele.addEventListener('transitionend', isTransitionOver, false); | |
function isTransitionOver(event) { | |
// function( event ) { | |
// console.log('sac') | |
if (opts.loop) { | |
_this.reset(opts, 0); | |
} else { | |
// console.log('cleaning up') | |
if (_this.ele.parentElement) { | |
_this.ele.parentElement.removeChild(_this.ele); | |
} | |
_this.pool.push(_this.ele); | |
} | |
// } | |
} | |
if (!opts.touch) { | |
if ((Modernizr.touch) && (md.mobile())) { | |
if (_this.ele.parentElement) { | |
_this.ele.parentElement.removeChild(_this.ele); | |
} | |
} | |
} | |
} | |
// Save old particles here because creating them is OMG EXPENSIVE. | |
Particle.prototype.pool = []; | |
Particle.prototype.beforeDraw = function(p) { | |
this.ele.style.opacity = 1; | |
} | |
Particle.prototype.reset = function(opts, p) { | |
if (opts.colorFn) { | |
this.color = opts.colorFn(); | |
} else { | |
this.color = opts.color || 'white'; | |
} | |
this.r = this.fuzz(opts.r) || 4; | |
// this.width = this.fuzz(opts.width) || 10; | |
// this.height = this.fuzz(opts.height) || 10; | |
this.ang = this.fuzz(opts.ang) || Math.PI * 2 * Math.random(); | |
this.spd = this.fuzz(opts.spd) || Math.random() / 5; | |
this.life = this.fuzz(opts.life) || 250 + Math.random() * 250; | |
this.i = opts.i || 0; | |
this.animate = opts.animate || ['scale']; | |
this.rNow = this.r; | |
// this.wNow = this.width; | |
// this.hNow = this.height; | |
this.angNow = Math.PI / 2 + this.ang; | |
this.spdNow = this.spd; | |
this.colorNow = this.color; | |
this.draw(0, 1); | |
var _this = this; | |
window.setTimeout(function() { | |
_this.draw(_this.life, 0); | |
}); | |
} | |
Particle.prototype.fuzz = function(value) { | |
if (!value) { | |
return false; | |
} | |
return value[0] + (value[1] * 2 * Math.random() - value[1]); | |
} | |
Particle.prototype.draw = function(i, p) { | |
if (p === 1) { | |
this.ele.classList.add('notransition'); | |
} else { | |
this.ele.style[csstransition] = this.life / 1000 + 's'; | |
this.ele.classList.remove('notransition'); | |
} | |
this.beforeDraw(p); | |
var radiusOffset = (0 - this.rNow / 2); | |
this.ele.style[csstransform] = [ | |
'translate(' + radiusOffset + 'px,' + radiusOffset + 'px)', // Center on circle | |
'rotate(' + this.angNow + 'rad)', // Rotate in whichever direction we're emitting. | |
'translateZ(0)', | |
'translate(' + (i * this.spdNow) + 'px,0)' // Move however far we need to go. | |
].join(' '); | |
this.ele.style.width = this.rNow + 'px'; | |
this.ele.style.height = this.rNow + 'px'; | |
this.ele.style.backgroundColor = this.colorNow; | |
this.ele.style.borderRadius = this.rNow + 'px'; | |
} | |
var ParticleSkyBurst = function(parent, opts) { | |
Particle.call(this, parent, opts); | |
} | |
for (var i in Particle.prototype) { | |
ParticleSkyBurst.prototype[i] = Particle.prototype[i]; | |
} | |
ParticleSkyBurst.prototype.beforeDraw = function(p) { | |
this.ele.style.opacity = p; | |
} | |
ParticleSkyBurst.prototype.draw = function(i, p) { | |
if (p === 1) { | |
this.ele.classList.add('notransition'); | |
} else { | |
this.ele.style[csstransition] = 1000 + 'ms'; | |
this.ele.classList.remove('notransition'); | |
} | |
this.beforeDraw(p); | |
var radiusOffset = (0 - this.rNow / 2); | |
this.ele.style[csstransform] = [ | |
'translate(' + radiusOffset + 'px,' + radiusOffset + 'px)', // Center on circle | |
'rotate(' + this.angNow + 'rad)', // Rotate in whichever direction we're emitting. | |
'translateZ(0)', | |
'translate(' + (i * this.spdNow) + 'px,0)' // Move however far we need to go. | |
].join(' '); | |
// this.ele.style.width = this.rNow+'px'; | |
// this.ele.style.height = this.rNow+'px'; | |
this.ele.style.backgroundColor = this.colorNow; | |
this.ele.style.borderRadius = this.rNow + 'px'; | |
this.ele.style.width = '10px'; | |
this.ele.style.height = '2px'; | |
} | |
var skyShotColors = ['#E0F2FE', '#F7921E', '#ED1B24', '#29AAE3', '#FFDD19', '#3AB54B', '#FFDD19']; | |
function skyShot_1(x,y, skyShotColor) { | |
var skyShot_1 = new Emitter({ | |
particles:50, | |
attachTo: 'skyshot', | |
particleType: ParticleSkyBurst, | |
color: skyShotColor, | |
ang: [Math.PI,Math.PI], | |
r: [4,3], | |
spd: [.04,.04], | |
life: [1200,1200], | |
center: [x,y], | |
loop:false, | |
touch:false | |
}); | |
} | |
setInterval(function(){ | |
for(i=0;i<1;i++) { | |
randomSkyShotColor = skyShotColors[Math.floor(Math.random() * skyShotColors.length)]; | |
skyShot_1(0, 0, randomSkyShotColor); | |
} | |
}, 3000) | |
function bikeSmokeFunction(attachTo) { | |
bikeSmoke = new Emitter({ | |
particles:50, | |
attachTo: attachTo, | |
color:'rgba(255,255,255,.5)', | |
ang: [1.5,0.5], | |
r: [4,3], | |
spd: [.02,.01], | |
life: [1000,150], | |
center: [0,0], | |
loop:true, | |
touch:false | |
}); | |
} | |
bikeSmokeFunction('smoke'); | |
function sutliBombStart(x,y) { | |
sutliBombParticles = new Emitter({ | |
particles:50, | |
attachTo: 'flames', | |
color:'orange', | |
ang: [Math.PI,Math.PI], | |
r: [4,3], | |
spd: [.05,.05], | |
life: [100,100], | |
center: [x,y], | |
loop:true | |
}); | |
} | |
sutliBombStart(0,0); |
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script> |
body { | |
background: #000; | |
overflow: hidden; | |
} | |
#smoke { | |
position: absolute; | |
left: 20%; | |
top: 50%; | |
} | |
#flames { | |
position: absolute; | |
left: 50%; | |
top: 50%; | |
} | |
#skyshot { | |
position: absolute; | |
left: 80%; | |
top: 50%; | |
} | |
.particle { | |
transition-timing-function: ease-in; | |
} | |
.notransition { | |
-webkit-transition: none !important; | |
-moz-transition: none !important; | |
-o-transition: none !important; | |
transition: none !important; | |
} | |