csscss-animationsscalecubic-bezier

CSS: How do I scale an element at a steady speed using cubic-bezier?


I have a 16384px X 16384px canvas. I start off with my canvas scaled down to 512px. I then scale my canvas to its original size.

The scaling (in and out) works.

The problem is that even though I've set the timing function to linear it doesn't look "linear". It speeds up at the start and slows down at the end.

You can see what I'm describing here:

let canvas = document.querySelector('canvas')
let button = document.querySelector('button')
let ctx = canvas.getContext('2d')

button.onclick = function() {
    if (canvas.classList.contains('scale')) {
        canvas.classList.remove('scale')
    } else {
    canvas.classList.add('scale')
  }
}

let img = new Image()
img.height = '256px'
img.width = '256px'

img.onload = function() {
  let y = 0
  while (y < 16384) {
    let x = 0
    while (x < 16384) {
      ctx.drawImage(img, x, y)
      x+=256
    }
    y+=256
  }
  
  button.classList.remove('hidden')
}

img.src = 'https://i.etsystatic.com/16276846/r/il/ab3661/1503703028/il_570xN.1503703028_frl8.jpg'
canvas {
  position: absolute;
  transform: translate(-50%, -50%) scale3d(0.03125, 0.03125, 0.03125);
  transition: transform 5s linear;
  top: 50%;
  left: 50%;
}

canvas.scale {
  transform: translate(-50%, -50%) scale3d(1, 1, 1);
}

button {
  position: absolute;
  top: 0;
  left: 0;
}

.hidden {
  display: none;
}
<canvas width="16384px" height="16384px"></canvas>
<button class="hidden">
scale
</button>

I understand that this is a known "problem" related to the size of the element being scaled (in my case it's pretty huge) and (I assume) the duration (?) of the scaling (in my case it's 5 seconds).

I know that the "solution" is to use a cubic-bezier() timing function but I don't know what values to use in order to create a steady (as much as possible) scaling in (and out) animation.

Is there some "formula" to figure out the values cubic-bezier() should have in my context ?

Maybe what I'm describing as a "problem" is actually an optical illusion (that things are speeding up or slowing down even though this never actually happens) that you cannot prevent when scaling (?)

Note: I don't mind increasing the duration of the transition from 5s to whatever (<=10s) is needed in order to create a steady scale in/out.


Solution

  • Chrome Devtools allows you to edit timing functions and suggests "quadratic" as one on the function sets
    This example uses "quadratic in" and "quadratic out"

    let canvas = document.querySelector('canvas')
    let button = document.querySelector('button')
    let ctx = canvas.getContext('2d')
    
    button.onclick = function() {
        if (canvas.classList.contains('scale')) {
            canvas.classList.remove('scale')
        } else {
        canvas.classList.add('scale')
      }
    }
    
    let img = new Image()
    img.height = '256px'
    img.width = '256px'
    
    img.onload = function() {
      let y = 0
      while (y < 16384) {
        let x = 0
        while (x < 16384) {
          ctx.drawImage(img, x, y)
          x+=256
        }
        y+=256
      }
      
      button.classList.remove('hidden')
    }
    
    img.src = 'https://i.etsystatic.com/16276846/r/il/ab3661/1503703028/il_570xN.1503703028_frl8.jpg'
    canvas {
      position: absolute;
      transform: translate(-50%, -50%) scale3d(0.03125, 0.03125, 0.03125);
      transition: transform 5s linear;
      top: 50%;
      left: 50%;
      
        transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
    }
    
    canvas.scale {
      transform: translate(-50%, -50%) scale3d(1, 1, 1);
      
      
        transition-timing-function: cubic-bezier(0.55, 0.09, 0.68, 0.53);
    }
    
    button {
      position: absolute;
      top: 0;
      left: 0;
    }
    
    .hidden {
      display: none;
    }
    <canvas width="16384px" height="16384px"></canvas>
    <button class="hidden">
    scale
    </button>