JakeTheSnake
JakeTheSnake

Reputation: 2464

Smoke Trail Effect - Javascript

How can one make a smooth smoke trail effect with Javascript, out of the code I attached? The trail should follow an object, but have the position the object had a moment ago. The code I attached does have some sort of trail effect, but it is not smooth. Can give the trail a position using something like this: position:trail = position:object, 5 ms ago?

var left = parseInt(document.getElementById("thingy").style.left);

setInterval(fly, 10); 
function fly() {
  if (left > 300) {
      left = 300; 
    }; 
    left++; 
    document.getElementById("thingy").style.left = left + "px";  
  }

setInterval(trail, 100); 
function trail() { 
    document.getElementById("trail").style.left = left + "px";  
  }
<div id="thingy" style="position:absolute; top:100px; left: 0px; width: 100px; height: 100px; background-color:#000000;"></div>

<div id="trail" style="position:absolute; top:125px; left: 0px; width: 50px; height: 50px; background-color:#CCCCCC; z-index: -10;"></div>

If it is possible I would like to stay out of jQuery.

Upvotes: 3

Views: 1944

Answers (1)

Rick Hitchcock
Rick Hitchcock

Reputation: 35670

This solution clones the element each time it moves.

CSS3 transitions are used on the cloned nodes' background to simulate a smoke trail.

The code ensures there are never more than 100 cloned nodes.

var thingy= document.getElementById('thingy'),
    left = thingy.offsetLeft,
    shadows= [],
    delta= 4;

setInterval(fly, 10); 

function fly() {
  var shadow= thingy.cloneNode();
  shadow.classList.add('shadow');
  shadow.style.backgroundColor= 'silver';
  document.body.appendChild(shadow);
  setTimeout(function() {
    shadow.style.backgroundColor= 'white';
  },100);
    
  shadows.push(shadow);
  if(shadows.length>100) {
    shadows[0].parentNode.removeChild(shadows[0]);
    shadows.shift();
  }
  if(left+delta > document.body.offsetWidth-thingy.offsetWidth || left < 0) {
    delta= -delta;
  }
  left+= delta;
  thingy.style.left = left + 'px';
}
body {
  margin: 0;
  padding: 0;
}

#thingy {
  position: absolute; 
  top: 100px;
  left: 0px;
  width: 100px; 
  height: 100px;
  background-color: orange;
  border-radius: 50%;
}

.shadow {
  transition: all 1s;
  z-index: -1;
}
<div id="thingy"></div>


Update

For a "smokier" effect, you can use random values for the cloned nodes' width, height, transition, etc., like I've done in this Snippet:

var thingy= document.getElementById('thingy'),
    tleft = thingy.offsetLeft,
    ttop = thingy.offsetTop,
    smokes= [],
    deltaX= deltaY= 2;

setInterval(fly, 10); 

function fly() {
  if(Math.random()>0.5) {
    var smoke= thingy.cloneNode();

    smoke.classList.add('smoke');
    smoke.style.background= 'gray';
    smoke.style.opacity= 0.2;
    smoke.style.transition= Math.random()+'s';
    smoke.style.width= Math.random()*thingy.offsetWidth+'px';
    smoke.style.height= Math.random()*thingy.offsetHeight+'px';
    smoke.style.marginTop= smoke.offsetHeight+'px';
    smoke.style.borderRadius= (Math.random()*25+25)+'%';
    document.body.appendChild(smoke);
    setTimeout(function() {
      smoke.style.opacity= 0;
    },100);
    
    smokes.push(smoke);
    if(smokes.length>20) {
      smokes[0].parentNode.removeChild(smokes[0]);
      smokes.shift();
    }
  }
  
  if(tleft+deltaX > document.body.offsetWidth-thingy.offsetWidth   || tleft < 0) {
    deltaX= -deltaX;
  }
  if(ttop +deltaY > document.body.offsetHeight-thingy.offsetHeight || ttop  < 0) {
    deltaY= -deltaY;
  }
  tleft+= deltaX;
  ttop += deltaY;
  thingy.style.left = tleft + 'px';
  thingy.style.top  = ttop  + 'px';
}
body {
  margin: 0;
  padding: 0;
  background: black;
  height: 100vh;
}

#thingy {
  position: absolute; 
  top: 100px;
  left: 0px;
  width: 100px; 
  height: 100px;
  background-color: orange;
  border-radius: 50%;
}

.smoke {
  z-index: -1;
}
<div id="thingy"></div>

Upvotes: 5

Related Questions