Facilitar animaciones en Canvas | Programar Plus

El <canvas> elemento en HTML y Canvas API en JavaScript se combinan para formar una de las principales posibilidades de animación y gráficos de trama en la web. Un caso de uso común de lienzo es la generación de imágenes mediante programación para sitios web, en particular, juegos. Eso es exactamente lo que hice en un sitio web que creé para jugar al solitario. Las cartas, incluido todo su movimiento, está todo realizado en lienzo.

En este artículo, veamos específicamente la animación en lienzo y las técnicas para que se vean más suaves. Veremos específicamente cómo facilitar las transiciones, como “entrada fácil” y “salida fácil”, que no vienen gratis en el lienzo como lo hacen en CSS.

Comencemos con un lienzo estático. Dibujé en el lienzo una sola carta de juego que tomé del DOM:

Comencemos con una animación básica: mover ese naipe en el lienzo. Incluso para cosas bastante básicas como requiere trabajar desde cero en el lienzo, tendremos que comenzar a desarrollar funciones que podamos usar.

Primero, crearemos funciones para ayudar a calcular las coordenadas X e Y:

function getX(params) {
  let distance = params.xTo - params.xFrom;
  let steps = params.frames;
  let progress = params.frame;
  return distance / steps * progress;
}


function getY(params) {
  let distance = params.yTo - params.yFrom;
  let steps = params.frames;
  let progress = params.frame;
  return distance / steps * progress;
}

Esto nos ayudará a actualizar los valores de posición a medida que la imagen se anima. Luego, volveremos a renderizar el lienzo hasta que se complete la animación. Hacemos esto agregando el siguiente código a nuestro addImage() método.

if (params.frame < params.frames) {
  params.frame = params.frame + 1;
  window.requestAnimationFrame(drawCanvas);
  window.requestAnimationFrame(addImage.bind(null, params))
}

¡Ahora tenemos animación! Estamos aumentando constantemente en 1 unidad cada vez, lo que llamamos animación lineal.

Puede ver cómo la forma se mueve del punto A al punto B de forma lineal, manteniendo la misma velocidad constante entre puntos. Es funcional, pero carece de realismo. El inicio y la parada son discordantes.

Lo que queremos es que el objeto se acelere (entrada) y desacelere (salida), para que imite lo que haría un objeto del mundo real cuando entran en juego cosas como la fricción y la gravedad.

Una función de aceleración de JavaScript

Lo lograremos con una transición “cúbica” de entrada y salida. Hemos modificado una de las ecuaciones que se encuentran en las funciones de aceleración de Flash de Robert Penner para que sea adecuada para lo que queremos hacer aquí.

function getEase(currentProgress, start, distance, steps) {
  currentProgress /= steps/2;
  if (currentProgress < 1) {
    return (distance/2)*(Math.pow(currentProgress, 3)) + start;
  }
  currentProgress -= 2;
  return distance/2*(Math.pow(currentProgress, 3)+ 2) + start;
}

Insertando esto en nuestro código, que es un facilidad cúbica, obtenemos un resultado mucho más suave. Observe cómo la tarjeta se acelera hacia el centro del espacio y luego se ralentiza a medida que llega al final.

Facilitación avanzada con JavaScript

Podemos obtener una aceleración más lenta con facilidad cuadrática o sinusoidal.

function getQuadraticEase(currentProgress, start, distance, steps) {
  currentProgress /= steps/2;
  if (currentProgress <= 1) {
    return (distance/2)*currentProgress*currentProgress + start;
  }
  currentProgress--;
  return -1*(distance/2) * (currentProgress*(currentProgress-2) - 1) + start;
}
function sineEaseInOut(currentProgress, start, distance, steps) {
  return -distance/2 * (Math.cos(Math.PI*currentProgress/steps) - 1) + start;
};

Para una aceleración más rápida, vaya con una facilidad quíntica o exponencial:

function getQuinticEase(currentProgress, start, distance, steps) {
  currentProgress /= steps/2;
  if (currentProgress < 1) {
    return (distance/2)*(Math.pow(currentProgress, 5)) + start;
  }
  currentProgress -= 2;
  return distance/2*(Math.pow(currentProgress, 5) + 2) + start;
}

function expEaseInOut(currentProgress, start, distance, steps) {
  currentProgress /= steps/2;
  if (currentProgress < 1) return distance/2 * Math.pow( 2, 10 * (currentProgress - 1) ) + start;
 currentProgress--;
  return distance/2 * ( -Math.pow( 2, -10 * currentProgress) + 2 ) + start;
};

Animaciones más sofisticadas con GSAP

Utilizar sus propias funciones de aceleración puede ser divertido, pero ¿y si desea más potencia y flexibilidad? Puede continuar escribiendo código personalizado o podría considerar una biblioteca más poderosa. Para eso, recurramos a GreenSock Animation Platform (GSAP).

La animación se vuelve mucho más fácil de implementar con GSAP. Tome este ejemplo, donde la carta rebota al final. Tenga en cuenta que la biblioteca GSAP está incluida en la demostración.

La función clave es moveCard:

function moveCard() {
  gsap.to(position, {
    duration: 2,
    ease: "bounce.out",
    x: position.xMax, 
    y: position.yMax, 
    onUpdate: function() {
      draw();
    },
    onComplete: function() {
      position.x = position.origX;
      position.y = position.origY;
    }
  });
}

El gsap.to El método es donde ocurre toda la magia. Durante los dos segundos de duración, el position El objeto se actualiza y, con cada actualización, se llama a onUpdate, lo que activa el lienzo para que se vuelva a dibujar.

Y no solo estamos hablando de rebotes. Hay toneladas de diferentes opciones de relajación para elegir.

Poniendolo todo junto

¿Aún no estás seguro de qué estilo y método de animación deberías usar en el lienzo cuando se trata de suavizado? Aquí hay un lápiz que muestra diferentes animaciones de aceleración, incluido lo que se ofrece en GSAP.

Echa un vistazo a mi juego de cartas Solitario para ver una demostración en vivo de las animaciones que no pertenecen a GSAP. En este caso, agregué animaciones para que las cartas en el juego salgan y entren cuando se muevan entre pilas.

Además de crear movimientos, las funciones de aceleración se pueden aplicar a cualquier otro atributo que tenga un estado desde y hasta, como cambios en la opacidad, rotaciones y escala. Espero que encuentre muchas formas de utilizar las funciones de aceleración para que su aplicación o juego se vea más fluido.