Javascript shake html element
Asked Answered
A

6

14

I'm trying to shake an html element for my game.

I found this code here:

shake = function (sprite, magnitude = 16, angular = false) {

  //A counter to count the number of shakes
  var counter = 1;

  //The total number of shakes (there will be 1 shake per frame)
  var numberOfShakes = 10;

  //Capture the sprite's position and angle so you can
  //restore them after the shaking has finished
  var startX = sprite.x,
      startY = sprite.y,
      startAngle = sprite.rotation;

  // Divide the magnitude into 10 units so that you can 
  // reduce the amount of shake by 10 percent each frame
  var magnitudeUnit = magnitude / numberOfShakes;

  //The `randomInt` helper function
  var randomInt = (min, max) => {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  };

  //Add the sprite to the `shakingSprites` array if it
  //isn't already there
  if(shakingSprites.indexOf(sprite) === -1) {
    //console.log("added")
    shakingSprites.push(sprite);

    //Add an `updateShake` method to the sprite.
    //The `updateShake` method will be called each frame
    //in the game loop. The shake effect type can be either
    //up and down (x/y shaking) or angular (rotational shaking).
    sprite.updateShake = () => {
      if(angular) {
        angularShake();
      } else {
        upAndDownShake();
      }
    };
  }

  //The `upAndDownShake` function
  function upAndDownShake() {

    //Shake the sprite while the `counter` is less than 
    //the `numberOfShakes`
    if (counter < numberOfShakes) {

      //Reset the sprite's position at the start of each shake
      sprite.x = startX;
      sprite.y = startY;

      //Reduce the magnitude
      magnitude -= magnitudeUnit;

      //Randomly change the sprite's position
      sprite.x += randomInt(-magnitude, magnitude);
      sprite.y += randomInt(-magnitude, magnitude);

      //Add 1 to the counter
      counter += 1;
    }

    //When the shaking is finished, restore the sprite to its original 
    //position and remove it from the `shakingSprites` array
    if (counter >= numberOfShakes) {
      sprite.x = startX;
      sprite.y = startY;
      shakingSprites.splice(shakingSprites.indexOf(sprite), 1);
    }
  }

  //The `angularShake` function
  //First set the initial tilt angle to the right (+1) 
  var tiltAngle = 1;

  function angularShake() {
    if (counter < numberOfShakes) {

      //Reset the sprite's rotation
      sprite.rotation = startAngle;

      //Reduce the magnitude
      magnitude -= magnitudeUnit;

      //Rotate the sprite left or right, depending on the direction,
      //by an amount in radians that matches the magnitude
      sprite.rotation = magnitude * tiltAngle;
      counter += 1;

      //Reverse the tilt angle so that the sprite is tilted
      //in the opposite direction for the next shake
      tiltAngle *= -1;
    }

    //When the shaking is finished, reset the sprite's angle and
    //remove it from the `shakingSprites` array
    if (counter >= numberOfShakes) {
      sprite.rotation = startAngle;
      shakingSprites.splice(shakingSprites.indexOf(sprite), 1);
      //console.log("removed")
    }
  }

}

However it only works for canvas sprites. How can I get it to work with HTML elements? Thanks.

Aquarelle answered 1/5, 2016 at 3:59 Comment(4)
why not use css animation ?Didache
@zb' the shake function shown here is pretty sophisticated, it lets me set the angle, magnitude, and naturally lowers the amount of shaking as it ends.Aquarelle
@Aquarelle My answer isn't your solution?Frowsty
@Frowsty I'm looking for a flexible js solution.Aquarelle
D
18

I have adapted your function so that it works on DOM elements. It is a pretty hefty shake though, you might want to play with the parameters to damped it a little bit:

var shakingElements = [];

var shake = function (element, magnitude = 16, angular = false) {
  //First set the initial tilt angle to the right (+1) 
  var tiltAngle = 1;

  //A counter to count the number of shakes
  var counter = 1;

  //The total number of shakes (there will be 1 shake per frame)
  var numberOfShakes = 15;

  //Capture the element's position and angle so you can
  //restore them after the shaking has finished
  var startX = 0,
      startY = 0,
      startAngle = 0;

  // Divide the magnitude into 10 units so that you can 
  // reduce the amount of shake by 10 percent each frame
  var magnitudeUnit = magnitude / numberOfShakes;

  //The `randomInt` helper function
  var randomInt = (min, max) => {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  };

  //Add the element to the `shakingElements` array if it
  //isn't already there
  if(shakingElements.indexOf(element) === -1) {
    //console.log("added")
    shakingElements.push(element);

    //Add an `updateShake` method to the element.
    //The `updateShake` method will be called each frame
    //in the game loop. The shake effect type can be either
    //up and down (x/y shaking) or angular (rotational shaking).
    if(angular) {
      angularShake();
    } else {
      upAndDownShake();
    }
  }

  //The `upAndDownShake` function
  function upAndDownShake() {

    //Shake the element while the `counter` is less than 
    //the `numberOfShakes`
    if (counter < numberOfShakes) {

      //Reset the element's position at the start of each shake
      element.style.transform = 'translate(' + startX + 'px, ' + startY + 'px)';

      //Reduce the magnitude
      magnitude -= magnitudeUnit;

      //Randomly change the element's position
      var randomX = randomInt(-magnitude, magnitude);
      var randomY = randomInt(-magnitude, magnitude);

      element.style.transform = 'translate(' + randomX + 'px, ' + randomY + 'px)';

      //Add 1 to the counter
      counter += 1;

      requestAnimationFrame(upAndDownShake);
    }

    //When the shaking is finished, restore the element to its original 
    //position and remove it from the `shakingElements` array
    if (counter >= numberOfShakes) {
      element.style.transform = 'translate(' + startX + ', ' + startY + ')';
      shakingElements.splice(shakingElements.indexOf(element), 1);
    }
  }

  //The `angularShake` function
  function angularShake() {
    if (counter < numberOfShakes) {
      console.log(tiltAngle);
      //Reset the element's rotation
      element.style.transform = 'rotate(' + startAngle + 'deg)';

      //Reduce the magnitude
      magnitude -= magnitudeUnit;

      //Rotate the element left or right, depending on the direction,
      //by an amount in radians that matches the magnitude
      var angle = Number(magnitude * tiltAngle).toFixed(2);
      console.log(angle);
      element.style.transform = 'rotate(' + angle + 'deg)';
      counter += 1;

      //Reverse the tilt angle so that the element is tilted
      //in the opposite direction for the next shake
      tiltAngle *= -1;

      requestAnimationFrame(angularShake);
    }

    //When the shaking is finished, reset the element's angle and
    //remove it from the `shakingElements` array
    if (counter >= numberOfShakes) {
      element.style.transform = 'rotate(' + startAngle + 'deg)';
      shakingElements.splice(shakingElements.indexOf(element), 1);
      //console.log("removed")
    }
  }

};

DEMO

Have a look at the demo in the fiddle. The red block is the regular upAndDownShake, whereas the green one uses angularShake:

https://jsfiddle.net/12aueufy/1/

Downfall answered 10/5, 2016 at 14:56 Comment(0)
F
29

You can use css animation like this example

@-webkit-keyframes shake {
    0% { -webkit-transform: translate(2px, 1px) rotate(0deg); } 
    10% { -webkit-transform: translate(-1px, -2px) rotate(-1deg); }
    20% { -webkit-transform: translate(-3px, 0px) rotate(1deg); }
    30% { -webkit-transform: translate(0px, 2px) rotate(0deg); }
    40% { -webkit-transform: translate(1px, -1px) rotate(1deg); }
    50% { -webkit-transform: translate(-1px, 2px) rotate(-1deg); }
    60% { -webkit-transform: translate(-3px, 1px) rotate(0deg); }
    70% { -webkit-transform: translate(2px, 1px) rotate(-1deg); }
    80% { -webkit-transform: translate(-1px, -1px) rotate(1deg); }
    90% { -webkit-transform: translate(2px, 2px) rotate(0deg); }
    100% { -webkit-transform: translate(1px, -2px) rotate(-1deg); }
}
.shake:hover {
    -webkit-animation-name: shake;
    -webkit-animation-duration: 0.5s;
    -webkit-transform-origin:50% 50%;
    -webkit-animation-iteration-count: infinite;
}
.shake {
    display:inline-block
}
<div class="shake">Shake me</div>
<img class="shake" src="https://www.w3.org/2008/site/images/logo-w3c-screen-lg" />

To change speed of shaking, change values of animation-duration, translate(), rotate().

If you want to shake element using javascript see jsfiddle

Frowsty answered 1/5, 2016 at 7:25 Comment(0)
D
18

I have adapted your function so that it works on DOM elements. It is a pretty hefty shake though, you might want to play with the parameters to damped it a little bit:

var shakingElements = [];

var shake = function (element, magnitude = 16, angular = false) {
  //First set the initial tilt angle to the right (+1) 
  var tiltAngle = 1;

  //A counter to count the number of shakes
  var counter = 1;

  //The total number of shakes (there will be 1 shake per frame)
  var numberOfShakes = 15;

  //Capture the element's position and angle so you can
  //restore them after the shaking has finished
  var startX = 0,
      startY = 0,
      startAngle = 0;

  // Divide the magnitude into 10 units so that you can 
  // reduce the amount of shake by 10 percent each frame
  var magnitudeUnit = magnitude / numberOfShakes;

  //The `randomInt` helper function
  var randomInt = (min, max) => {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  };

  //Add the element to the `shakingElements` array if it
  //isn't already there
  if(shakingElements.indexOf(element) === -1) {
    //console.log("added")
    shakingElements.push(element);

    //Add an `updateShake` method to the element.
    //The `updateShake` method will be called each frame
    //in the game loop. The shake effect type can be either
    //up and down (x/y shaking) or angular (rotational shaking).
    if(angular) {
      angularShake();
    } else {
      upAndDownShake();
    }
  }

  //The `upAndDownShake` function
  function upAndDownShake() {

    //Shake the element while the `counter` is less than 
    //the `numberOfShakes`
    if (counter < numberOfShakes) {

      //Reset the element's position at the start of each shake
      element.style.transform = 'translate(' + startX + 'px, ' + startY + 'px)';

      //Reduce the magnitude
      magnitude -= magnitudeUnit;

      //Randomly change the element's position
      var randomX = randomInt(-magnitude, magnitude);
      var randomY = randomInt(-magnitude, magnitude);

      element.style.transform = 'translate(' + randomX + 'px, ' + randomY + 'px)';

      //Add 1 to the counter
      counter += 1;

      requestAnimationFrame(upAndDownShake);
    }

    //When the shaking is finished, restore the element to its original 
    //position and remove it from the `shakingElements` array
    if (counter >= numberOfShakes) {
      element.style.transform = 'translate(' + startX + ', ' + startY + ')';
      shakingElements.splice(shakingElements.indexOf(element), 1);
    }
  }

  //The `angularShake` function
  function angularShake() {
    if (counter < numberOfShakes) {
      console.log(tiltAngle);
      //Reset the element's rotation
      element.style.transform = 'rotate(' + startAngle + 'deg)';

      //Reduce the magnitude
      magnitude -= magnitudeUnit;

      //Rotate the element left or right, depending on the direction,
      //by an amount in radians that matches the magnitude
      var angle = Number(magnitude * tiltAngle).toFixed(2);
      console.log(angle);
      element.style.transform = 'rotate(' + angle + 'deg)';
      counter += 1;

      //Reverse the tilt angle so that the element is tilted
      //in the opposite direction for the next shake
      tiltAngle *= -1;

      requestAnimationFrame(angularShake);
    }

    //When the shaking is finished, reset the element's angle and
    //remove it from the `shakingElements` array
    if (counter >= numberOfShakes) {
      element.style.transform = 'rotate(' + startAngle + 'deg)';
      shakingElements.splice(shakingElements.indexOf(element), 1);
      //console.log("removed")
    }
  }

};

DEMO

Have a look at the demo in the fiddle. The red block is the regular upAndDownShake, whereas the green one uses angularShake:

https://jsfiddle.net/12aueufy/1/

Downfall answered 10/5, 2016 at 14:56 Comment(0)
G
5

Maybe you should take a look in Animate.css, https://daneden.github.io/animate.css/, this is a css library that provides a lot of animations, including a shake one... I hope that this might work for your problem!

Grunter answered 1/5, 2016 at 6:32 Comment(0)
B
2

It's possible to have the animation done entirely with very simple CSS and JS. The strategy involves two things:

  • transition: all 500ms; — This bit of CSS means that anytime we want to do a transition (i.e., animation), that transition will complete in 500ms.
  • transform(rotate) — Then every CSS change takes place in 500ms, so, we can shake the icon with margin-left and margin-right, or, we can use transform(rotate), let's try the latter!

Note: You can use any type of CSS effect here, try experimenting with something else! I'm curious if there's anything better than transform(rotate).

Demo

const wiggletime = 100;

function rotateElement(el) {
  el.classList.add('rotateable');
  el.style.transform = 'rotate(20deg)';

  setTimeout(function() {
    el.style.transform = 'rotate(-20deg)';
    setTimeout(function() {
      el.style.transform = 'rotate(0deg)';
    }, wiggletime);
  }, wiggletime);

  return true;
}

function squishElement(el) {
  el.classList.add('rotateable');
  el.style.transform = 'scaleX(1.5)';

  setTimeout(function() {
    el.style.transform = 'scaleX(0.5)';
    setTimeout(function() {
      el.style.transform = 'scaleX(1)';
    }, wiggletime);
  }, wiggletime);

  return true;
}

function shakeElement(el) {
  el.classList.add('rotateable');
  el.style.marginLeft = '20px';

  setTimeout(function() {
    el.style.marginLeft = '-20px';
    setTimeout(function() {
      el.style.marginLeft = '0px';
    }, wiggletime);
  }, wiggletime);

  return true;
}

function skewElement(el) {
  el.classList.add('rotateable');
  el.style.transform = 'skewX(20deg)';

  setTimeout(function() {
    el.style.transform = 'skewX(-20deg)';
    setTimeout(function() {
      el.style.transform = 'skewX(0deg)';
    }, wiggletime);
  }, wiggletime);

  return true;
}

function borderElement(el) {
  el.classList.add('rotateable');
  el.style.border = '4px solid black';
  el.style.marginLeft = '-4px';
  el.style.marginTop = '-4px';

  setTimeout(function() {
    el.style.border = '2px solid black';
    el.style.marginLeft = '-2px';
    el.style.marginTop = '-2px';
    setTimeout(function() {
      el.style.border = '1px solid black';
      el.style.marginLeft = '0px';
      el.style.marginTop = '0px';
    }, wiggletime);
  }, wiggletime);

  return true;
}

document.getElementById('rotate-me').addEventListener('click', function(ev) {
  rotateElement(this);
});

document.getElementById('shake-me').addEventListener('click', function(ev) {
  shakeElement(this);
});

document.getElementById('squish-me').addEventListener('click', function(ev) {
  squishElement(this);
});

document.getElementById('skew-me').addEventListener('click', function(ev) {
  skewElement(this);
});

document.getElementById('border-me').addEventListener('click', function(ev) {
  borderElement(this);
});
.rotateable {
    transition: all 100ms;
}
<button id="shake-me">Shake Me!</button>

<br><br>

<button id="rotate-me">Rotate Me!</button>

<br><br>

<button id="squish-me">Squish Me!</button>

<br><br>

<button id="skew-me">Skew Me!</button>

<br><br>

<button id="border-me" style="position:absolute;">Border Me!</button>

Demo - Extended

Why not extended animations?

const wiggletime = 100;

function rotateElement(el) {
  el.classList.add('rotateable');
  el.style.transform = 'rotate(20deg)';

  setTimeout(function() {
    el.style.transform = 'rotate(-20deg)';
    setTimeout(function() {
      el.style.transform = 'rotate(10deg)';
      setTimeout(function() {
        el.style.transform = 'rotate(-10deg)';
        setTimeout(function() {
          el.style.transform = 'rotate(0deg)';
        }, wiggletime);
      }, wiggletime);
    }, wiggletime);
  }, wiggletime);

  return true;
}

function squishElement(el) {
  el.classList.add('rotateable');
  el.style.transform = 'scaleX(1.5)';

  setTimeout(function() {
    el.style.transform = 'scaleX(0.5)';
    setTimeout(function() {
      el.style.transform = 'scaleX(1.25)';
      setTimeout(function() {
        el.style.transform = 'scaleX(0.75)';
        setTimeout(function() {
          el.style.transform = 'scaleX(1)';
        }, wiggletime);
      }, wiggletime);
    }, wiggletime);
  }, wiggletime);

  return true;
}

function skewElement(el) {
  el.classList.add('rotateable');
  el.style.transform = 'skewX(20deg)';

  setTimeout(function() {
    el.style.transform = 'skewX(-20deg)';
    setTimeout(function() {
      el.style.transform = 'skewX(10deg)';
      setTimeout(function() {
        el.style.transform = 'skewX(-10deg)';
        setTimeout(function() {
          el.style.transform = 'skewX(0deg)';
        }, wiggletime);
      }, wiggletime);
    }, wiggletime);
  }, wiggletime);

  return true;
}

function shakeElement(el) {
  el.classList.add('rotateable');
  el.style.marginLeft = '20px';

  setTimeout(function() {
    el.style.marginLeft = '-20px';
    setTimeout(function() {
      el.style.marginLeft = '10px';
      setTimeout(function() {
        el.style.marginLeft = '-10px';
        setTimeout(function() {
          el.style.marginLeft = '0px';
        }, wiggletime);
      }, wiggletime);
    }, wiggletime);
  }, wiggletime);

  return true;
}

document.getElementById('rotate-me').addEventListener('click', function(ev) {
  rotateElement(this);
});

document.getElementById('shake-me').addEventListener('click', function(ev) {
  shakeElement(this);
});

document.getElementById('squish-me').addEventListener('click', function(ev) {
  squishElement(this);
});

document.getElementById('skew-me').addEventListener('click', function(ev) {
  skewElement(this);
});
.rotateable {
  transition: all 100ms;
}
<button id="shake-me">Shake Me!</button>

<br><br>

<button id="rotate-me">Rotate Me!</button>

<br><br>

<button id="squish-me">Squish Me!</button>

<br><br>

<button id="skew-me">Skew Me!</button>
Basement answered 2/12, 2022 at 0:2 Comment(0)
P
0
function shake(e, oncomplete, distance, time) {
var time = 500;
var distance = 5;

var start = (new Date()).getTime();
animate();

function animate() {
    var now = (new Date()).getTime();
    // Get current time
    var elapsed = now - start;
    // How long since we started
    var fraction = elapsed / time;
    // What fraction of total time?
    if (fraction < 1) {
        var x = distance * Math.sin(fraction * 4 * Math.PI);
        e.style.left = x + "px";
        // We're aiming for a smooth 40 frames/second animation.
        setTimeout(animate, Math.min(25, time - elapsed));
    } else {
        // Otherwise, the animation is complete
        if (oncomplete) oncomplete(e);
        // Invoke completion callback
    }
   }
}

function shakeme(event1) {
    shake(event1.target);
}

document.getElementById("wood").addEventListener("mouseover", shakeme, false);

HTML Element

<button id="wood">Hello World</button>

Source

http://javascriipt.blogspot.com/2015/02/how-to-mimic-shake-animation-effect.html

Potentiality answered 9/5, 2016 at 6:8 Comment(0)
I
0

You could use some library for javascript animations like Velocity

It's really flexible and easy to use.

Please check this working sample, I'm pretty sure that you can create wished effect in minutes

Hope it help

Ineffectual answered 9/5, 2016 at 21:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.