SOURCE

console 命令行工具 X clear

                    
>
console
function animate ({timing, draw, duration}) {

    let start = performance.now();
    
    return new Promise(resolve => {
        requestAnimationFrame(function animate(time) {
            //timeFraction为时间段,值为0-1之间 (timeFraction goes from 0 to 1)
            let timeFraction = (time - start) / duration;
            if (timeFraction > 1) timeFraction = 1;

            // 计算当前的动画状态(calculate the current animation state)
            let progress = timing(timeFraction)

            draw(progress); // draw it

            if (timeFraction < 1) {
            requestAnimationFrame(animate);
            } else {
            resolve(timing);
            }
        });
    });

}

const train = document.querySelector('.ball');

// 沿着x轴匀速直线运动
const linear = () => {
  const draw = (progress) => {
    train.style.transform = `translate(${progress}px, 0)`;
  }

  // 沿着x轴匀速运动
  animate({
    duration: 1000,
    timing(timeFraction) {
      return timeFraction * 100;
    },
    draw,
  });
};

// 重力
const gravity = () => {
  const draw = (progress) => {
    train.style.transform = `translate(0, ${500 * (progress - 1)}px)`;
  };

  // 沿着x轴匀速运动
  animate({
    duration: 1000,
    timing(timeFraction) {
      return timeFraction ** 2;
    },
    draw,
  });
};

// 摩擦力(匀减速运动)
const friction = () => {
  // 初始高度500px
  const draw = (progress) => {
    train.style.transform = `translate(0, ${500 * (progress - 1)}px)`;
  };

  // 沿着x轴匀速运动
  animate({
    duration: 1000,
    timing(timeFraction) {
      // 初始速度系数为2
      return timeFraction * (2 - timeFraction);
    },
    draw,
  });
};

// 平抛(x轴匀速,y轴加速)
const horizontalMotion = () => {
  const draw = (progress) => {
    train.style.transform = `translate(${500 * progress.x}px, ${500 * (progress.y - 1)}px)`;
  };

  // 有两个方向,沿着x轴匀速运动,沿着y轴加速运动
  animate({
    duration: 1000,
    timing(timeFraction) {
      return {
        x: timeFraction,
        y: timeFraction ** 2,
      }
    },
    draw,
  });
};

// 旋转 + 平抛(x轴匀速,y轴加速,旋转匀速)
const horizontalMotionWidthRotate = () => {
  const draw = (progress) => {
    train.style.transform = `translate(${500 * progress.x}px, ${500 * (progress.y - 1)}px) rotate(${2000 * progress.rotate}deg)`;
  };

  // 有两个方向,沿着x轴匀速运动,沿着y轴加速运动
  animate({
    duration: 2000,
    timing(timeFraction) {
      return {
        x: timeFraction,
        y: timeFraction ** 2,
        rotate: timeFraction,
      }
    },
    draw,
  });
};
// 拉弓(x轴匀速 + y轴初始速度为负的匀加速)
const arrowMove = () => {
  const back = (x, timeFraction) => {
    return Math.pow(timeFraction, 2) * ((x + 1) * timeFraction - x);
  }
  const draw = (progress) => {
    train.style.transform = `translate(${200*progress.x}px, ${ - 500 * progress.y}px)`;
  };
  animate({
    duration: 1000,
    timing(timeFraction) {
      return {
        x: timeFraction,
        y: back(2, timeFraction),
      };
    },
    draw,
  });
};
// 贝塞尔
const bezier = () => {
  const bezierPath = (x1, y1, x2, y2, timeFraction) => {
    const x = 3 * x1 * timeFraction * (1 - timeFraction) ** 2 + 3 * x2 * timeFraction ** 2 * (1 - timeFraction) + timeFraction ** 3;
    const y = 3 * y1 * timeFraction * (1 - timeFraction) ** 2 + 3 * y2 * timeFraction ** 2 * (1 - timeFraction) + timeFraction ** 3;
    return [x, y];
  }

  const draw = (progress) => {
    const [px, py] = bezierPath(0.2, 0.6, 0.8, 0.2, progress);
    train.style.transform = `translate(${300 * px}px, ${-300 * py}px)`;
  }

  animate({
    duration: 2000,
    timing(timeFraction) {
      return timeFraction * (2 - timeFraction);
    },
    draw,
  });
}
// 缓动函数实现弹球
const bounceMove = () => {
  const bounce = (timeFraction) => {
    if (timeFraction < 1 / 2.75) {
      return 7.5625 * timeFraction * timeFraction
    } else if (timeFraction < 2 / 2.75) {
      return 7.5625 * (timeFraction -= 1.5 / 2.75) * timeFraction + 0.75
    } else if (amount < 2.5 / 2.75) {
      return 7.5625 * (timeFraction -= 2.25 / 2.75) * timeFraction + 0.9375
    } else {
      return 7.5625 * (timeFraction -= 2.625 / 2.75) * timeFraction + 0.984375
    }
  }
  const draw = (progress) => {
    train.style.transform = `translate(${200*progress.x}px, ${200 * (progress.y - 1)}px)`;
  };
  animate({
    duration: 1000,
    timing(timeFraction) {
      return {
        x: timeFraction,
        y: bounce(timeFraction),
      };
    },
    draw,
  });
};
// 弹球
const asyncBounce = () => {
  (async function() {
    let damping = 0.7, // 衰变系数
        duration = 1000,
        height = 300;
    
    while(height >= 1) {
      const down = (progress) => {
        train.style.transform = `translate(0, ${height * (progress - 1)}px)`;
      };
      await animate({
        duration: duration,
        timing(timeFraction) {
          return timeFraction ** 2;
        },
        draw: down,
      });
      height *= damping ** 2;
      duration *= damping;
      const up = (progress) => {
        train.style.transform = `translate(0, ${-height * progress}px)`;
      }
      await animate({
        duration: duration,
        timing(timeFraction) {
          return timeFraction * (2 - timeFraction);
        },
        draw: up,
      });
    }
  }());
};
// 椭圆
const ellipsis = () => {
  const draw = (progress) => {
    const x = 150 * Math.cos(Math.PI * 2 * progress);
    const y = 100 * Math.sin(Math.PI * 2 * progress);
    train.style.transform = `translate(${x}px, ${y}px)`;
  }
  
  animate({
    duration: 2000,
    timing(timeFraction) {
      return timeFraction * (2 - timeFraction);
    },
    draw,
  });
};

// 延时函数
// await animate({
//   duration: 1000,
//   timing: () => {},
//   draw: () => {},
// });
<div id="container">
  <div class="axisX"></div>
  <div class="axisY"></div>
  <div class="ball"></div>
  <div class="button-container">
    <div class="button" onClick="linear()">匀速直线</div>
    <div class="button" onClick="gravity()">重力</div>
    <div class="button" onClick="friction()">摩擦力</div>
    <div class="button" onClick="horizontalMotion()">平抛</div>
    <div class="button" onClick="horizontalMotionWidthRotate()">旋转+平抛</div>
    <div class="button" onClick="arrowMove()">拉弓</div>
    <div class="button" onClick="bezier()">贝塞尔曲线</div>
    <div class="button" onClick="bounceMove()">缓动函数实现弹球</div>
    <div class="button" onClick="asyncBounce()">弹球</div>
    <div class="button" onClick="ellipsis()">椭圆</div>
  </div>
</div>
body {
  margin: 0;
  padding: 0;
}

#container {
  position: absolute;
  width: 100%;
  height: 100vh;
}

.axisX {
  height: 2px;
  background: black;
  width: 100%;
  position: absolute;
  top: 60%;
  transform: translate(0, -1px);
}

.axisY {
  width: 2px;
  background: black;
  height: 100vh;
  position: absolute;
  left: 50%;
  transform: translate(-1px, 0);
}

.ball {
  position: absolute;
  width: 40px;
  height: 40px;
  border-radius: 20px;
  background: red;
  top: calc(60% - 20px);
  left: calc(50% - 20px);
}

.ball::before {
  content: "";
  position: absolute;
  left: 10px;
  top: 10px;
  background: yellow;
  width: 10px;
  height: 10px;
  border-radius: 5px;
}

.button-container {
  position: absolute;
  bottom: 50px;
  left: 100px;
}

.button {
  cursor: pointer;
  border: 2px solid black;
  padding: 10px 20px;
  font-size: 20px;
  display: inline-block;
}
.button:hover {
  background: gray;
}