1

I was trying to make an animation using the style.transform property. The intention was to loop the transform scale property with slight increase in x and y on each round, but it failed. How can I achieve this effect?

x = document.getElementById("btn");
x.onclick = function() {
  for (let y = 0; y < 1; y += 0.1) {
    x.style.transform = 'scale(1.' + y + ',' + '1.' + y + ')';
  }
}
<button id='btn'>button</button>
Nikhil Singh
  • 1,486
  • 1
  • 13
  • 24
ino
  • 1,002
  • 1
  • 8
  • 25

4 Answers4

6

You should use CSS transitions. Style your button like so:

#btn {
  transition: transform 0.1s
}

That code will make the button transition during 0.1 seconds whenever the transform property is changed, for example the scale.

Then, from your JavaScript code, you juste have to assign the transform style once, and CSS will transition automatically.

x = document.getElementById("btn");

x.onclick = function() {
  x.style.transform = 'scale(2,2)'; // or any x and y value
}

x = document.getElementById("btn");

x.onclick = function() {
  x.style.transform = 'scale(2,2)'; // or any x and y value
}
#btn {
  transition: transform 0.1s
}
<button id="btn">button</button>
Mechanic
  • 5,015
  • 4
  • 15
  • 38
Oskar Zanota
  • 472
  • 3
  • 12
3

You could do it with a combination of setInterval, requestAnimationFrame and CSS transitions to control the speed of the animation, good performance and a smooth controlled transition.

const button = document.getElementById("btn");

function scaleButton(speed, size = 1) {
  let end = size * 2;
  let interval = setInterval(() => {
    if (size === end) {
      clearInterval(interval);
      button.style.transitionDuration = '';
    }
    size += 0.1;
    size = parseFloat(size.toFixed(1)); // Round to 1 decimal point.
    requestAnimationFrame(() => {
      button.style.transform = `scale(${size}, ${size})`;
    });
  }, speed);
  button.style.transitionDuration = `${speed}ms`;
}

button.onclick = function() {
  scaleButton(50);
};
html {
  width: 100%;
  height: 100%;
}

body {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
}

#btn {
  transition: transform linear;
}
<button id='btn'>button</button>
Emiel Zuurbier
  • 19,095
  • 3
  • 17
  • 32
1

Here is a soultion using requestAnimationFrame;

x = document.getElementById("btn");

let y = 0;
function animate (y) {
  if(y < 1) {
    y += 0.1;
    x.style.transform = `scale(${1 + y},${1 + y})`;
    requestAnimationFrame( () => animate(y) );
  }
}

x.onclick = function() {
    animate(0);
}
<button id='btn'>button</button>
Mechanic
  • 5,015
  • 4
  • 15
  • 38
1

The browser has an, how to say, an painting cycle. Every 16ms (i am not sure if its exactly 16ms) the browser does an repaint.

The problem that you have, is that your loop is already done before the next repaint cycle.

Here is an solution with async / await:

You can create an function called readyToAnimate it returns an promise that resolves the callback function of the requestAnimationFrame.

The requestAnimationFrame callback gets executed before the next repaint.

Now in your loop you can use await readyToAnimate(). It will wait till the browser is ready for the next repaint.

x = document.getElementById("btn");
x.onclick = async function() {
  for (let y = 0; y < 1; y += 0.1) {
    await readyToAnimate();
    x.style.transform = `translateX(${y * 200}px)`;
  }
}

function readyToAnimate() {
   return new Promise(res => requestAnimationFrame(res));
}
<button id='btn'>button</button>
bill.gates
  • 14,145
  • 3
  • 19
  • 47