<select id="animationType">
    <option value="static">Choose animation style</option>
    <optgroup label="Standard animations">
      <option value="quadratic">Quadratic</option>
      <option value="cubic">Cubic</option>
      <option value="quartic">Quartic</option>
      <option value="quintic">Quintic</option>
      <option value="sinusoidal">Sinusoidal</option>
      <option value="exponential">Exponential</option>
    </optgroup>
    <optgroup label="GSAP animations">
        <option value="power2.inOut">Quadratic</option>
        <option value="power3.inOut">Cubic</option>
        <option value="power4.inOut">Quartic</option>
        <option value="sine.inOut">Sinusoidal</option>
        <option value="expo.inOut">Exponential</option>
        <option value="bounce.out">Bounce</option>

  </select>
  <button onclick="javascript:playAnimation()">Play</button>
  <br>

  <canvas id="canvas" width="300" height="300"></canvas>
  <img src="https://solitaired.com/images/style-classic-2/heart_13.png" id="heart_13" style="display:none;">
const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    const canvasWidth = canvas.width;
    const canvasHeight = canvas.height;
    const cardName = 'heart_13';
    const cardWidth = 100;
    const cardHeight = 145;
    const cardPadding = 10;
    let imagesCanvas = {};

    let position = {
      x: cardPadding,
      origX: cardPadding,
      y: cardPadding,
      origY: cardPadding,
      xMax: canvas.width - cardPadding - cardWidth,
      yMax: canvas.height - cardPadding - cardHeight
    };

    function getEase(currentProgress, start, distance, steps, animationFunction) {
      switch(animationFunction) {
        case 'static':
          return cardPadding; break;
        case 'quadratic':
          return getQuadraticEase(currentProgress, start, distance, steps); break;
        case 'cubic':
          return getCubicEase(currentProgress, start, distance, steps); break;
        case 'quartic':
          return getQuarticEase(currentProgress, start, distance, steps); break;
        case 'quintic':
          return getQuinticEase(currentProgress, start, distance, steps); break;
        case 'sinusoidal':
          return getSinusoidalEase(currentProgress, start, distance, steps); break;
        case 'exponential':
          return getExponentialEase(currentProgress, start, distance, steps); break;
      }
    }

    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 getCubicEase(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;
    }

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

    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 getSinusoidalEase(currentProgress, start, distance, steps) {
      return -distance/2 * (Math.cos(Math.PI*currentProgress/steps) - 1) + start;
    }

    function getExponentialEase(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;
    }

    function getX(params) {
      let distance = params.xTo - params.xFrom;
      let steps = params.frames;
      let currentProgress = params.frame;
      return getEase(currentProgress, params.xFrom, distance, steps, params.type);
    }

    function getY(params) {
      let distance = params.yTo - params.yFrom;
      let steps = params.frames;
      let currentProgress = params.frame;
      return getEase(currentProgress, params.yFrom, distance, steps, params.type);
    }

    function drawCanvas() {
      ctx.fillStyle = 'rgb(0,80,0)';
      ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    function addImage(params) {
      let name = params.name;
      if (imagesCanvas[name] === undefined) {
        imagesCanvas[name] = document.createElement('canvas');
      }
      imagesCanvas[name].width = cardWidth;
      imagesCanvas[name].height = cardHeight;

      let image = document.getElementById(name);

      let imageCtx = imagesCanvas[name].getContext('2d');
      imageCtx.clearRect(0, 0, cardWidth, cardHeight);
      imageCtx.drawImage(image, 0, 0, cardWidth, cardHeight);

      if (params.library == 'gsap') {
        ctx.drawImage(imagesCanvas[name], params.x, params.y);
      } else {
        ctx.drawImage(imagesCanvas[name], getX(params), getY(params));
        if (params.frame < params.frames) {
          params.frame = params.frame + 1;
          window.requestAnimationFrame(drawCanvas);
          window.requestAnimationFrame(addImage.bind(null, params))
        }
      }
    }

    function draw() {
      drawCanvas();
      addImage({
        name: cardName,
        frame: 0,
        frames: 100,
        xFrom: cardPadding,
        xTo: canvasWidth - cardWidth - cardPadding,
        yFrom: cardPadding,
        yTo: canvasHeight - cardHeight - cardPadding,
        type: $('#animationType').val(),
        library: 'none'
      });
    }

    //gsap section
    function drawGsap() {
      drawCanvas();
      addImage({
        name: cardName,
        library: 'gsap',
        x: position.x,
        y: position.y
      });
    }

    function runGsap() {
      gsap.to(position, {
        duration: 2,
        ease: $('#animationType').val(),
        x: position.xMax,
        y: position.yMax,
        onUpdate: function() {
          drawGsap();
        },
        onComplete: function() {
          position.x = position.origX;
          position.y = position.origY;
        }
      });
    }

    function playAnimation() {
      if (['quadratic', 'cubic', 'quartic', 'quintic', 'sinusoidal', 'exponential'].includes($('#animationType').val())) {
        draw();
      } else {
        runGsap();
      }
    }

    window.onload = draw;

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.6/gsap.min.js