player-javascript
Animation
custom

Custom Animations

You can use TWEEN directly to create completely custom animation effects.

How to Use

After obtaining the player instance through ref or onReady, use getElementsByName or click events to get elements, then apply TWEEN animations directly to element properties (position, rotation, scale, color, etc.).

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0"
    />
    <title>ICraft Player TWEEN Custom Animation Demo</title>
    <style>
      body {
        margin: 0;
        padding: 0;
        font-family: Arial, sans-serif;
      }
      #container {
        width: 100%;
        height: 100vh;
        position: relative;
      }
      #controls {
        position: absolute;
        bottom: 10px;
        right: 10px;
        padding: 10px;
        font-size: 14px;
      }
    </style>
    <script src="https://unpkg.com/@icraft/player@latest/dist/umd/icraft-player.min.js"></script>
    <script src="https://unpkg.com/@tweenjs/tween.js@23.1.3/dist/tween.umd.js"></script>
  </head>
  <body>
    <div id="container"></div>
    <div id="controls">
      <div>
        <button id="pauseBtn">pause</button>
      </div>
    </div>
  </body>
  <script>
    let player = null;
    let isPaused = false;
    let allAnimations = [];
    let currentTween = null;
 
    const playerInstance = new ICraftPlayer({
      src: "/templates/tween.iplayer",
      container: document.getElementById("container"),
      onReady: (playerRef) => {
        player = playerRef;
        const car = player.getElementsByName("car")?.[0];
        if (car) {
          startSquarePathAnimation(car);
        }
      },
    });
 
    function startSquarePathAnimation(car) {
      const startPos = {
        x: car.position.x,
        y: car.position.y,
        z: car.position.z,
      };
      const startRot = car.rotation.y;
 
      const pathSegments = [
        {
          to: { x: startPos.x, y: startPos.y, z: startPos.z + 20 },
          rotation: startRot,
        },
        {
          to: { x: startPos.x + 20, y: startPos.y, z: startPos.z + 20 },
          rotation: startRot - Math.PI / 2,
        },
        {
          to: { x: startPos.x + 20, y: startPos.y, z: startPos.z },
          rotation: startRot - Math.PI,
        },
        {
          to: { x: startPos.x, y: startPos.y, z: startPos.z },
          rotation: startRot - (Math.PI * 3) / 2,
        },
      ];
 
      const animations = [];
 
      pathSegments.forEach((segment, index) => {
        const moveTween = new TWEEN.Tween(car.position)
          .to(segment.to, 2000)
          .easing(TWEEN.Easing.Quadratic.InOut);
 
        const rotateTween = new TWEEN.Tween(car.rotation)
          .to({ y: segment.rotation }, 300)
          .easing(TWEEN.Easing.Quadratic.InOut);
 
        moveTween.onStart(() => {
          rotateTween.start();
        });
 
        animations.push(moveTween);
      });
 
      for (let i = 0; i < animations.length; i++) {
        const nextIndex = (i + 1) % animations.length;
        animations[i].chain(animations[nextIndex]);
      }
 
      allAnimations = animations;
      animations[0].start();
      currentTween = animations[0];
 
      startTweenUpdateLoop();
    }
 
    function startTweenUpdateLoop() {
      function animate() {
        TWEEN.update();
        requestAnimationFrame(animate);
      }
      animate();
    }
 
    function togglePause() {
      const pauseBtn = document.getElementById("pauseBtn");
 
      if (isPaused) {
        allAnimations.forEach((animation) => {
          if (animation.isPaused && animation.isPaused()) {
            animation.resume();
          }
        });
        isPaused = false;
        pauseBtn.textContent = "pause";
        pauseBtn.className = "";
      } else {
        allAnimations.forEach((animation) => {
          if (animation.pause) {
            animation.pause();
          }
        });
        isPaused = true;
        pauseBtn.textContent = "play";
        pauseBtn.className = "pause";
      }
    }
 
    document.getElementById("pauseBtn").addEventListener("click", togglePause);
 
    window.addEventListener("beforeunload", () => {
      if (currentTween) {
        currentTween.stop();
      }
      TWEEN.removeAll();
    });
  </script>
</html>
 

Common Animation Examples

Position Animation

// Move to target position
const moveTween = new TWEEN.Tween(element.position)
  .to({ x: 10, y: 5, z: 0 }, 1000)
  .easing(TWEEN.Easing.Quadratic.InOut)
  .start();

Rotation Animation

// Rotate 360 degrees around Y axis
const rotateTween = new TWEEN.Tween(element.rotation)
  .to({ y: element.rotation.y + Math.PI * 2 }, 2000)
  .easing(TWEEN.Easing.Cubic.InOut)
  .start();

Scale Animation

// Scale to 1.5x
const scaleTween = new TWEEN.Tween(element.scale)
  .to({ x: 1.5, y: 1.5, z: 1.5 }, 1000)
  .easing(TWEEN.Easing.Back.Out)
  .start();

Combined Animation

// Execute multiple animations simultaneously
const moveTween = new TWEEN.Tween(element.position).to({ x: 5, y: 3, z: 2 }, 2000);
 
const rotateTween = new TWEEN.Tween(element.rotation).to({ y: Math.PI }, 2000);
 
const scaleTween = new TWEEN.Tween(element.scale).to({ x: 1.2, y: 1.2, z: 1.2 }, 2000);
 
// Start simultaneously
moveTween.start();
rotateTween.start();
scaleTween.start();

Chained Animation

// Execute animations sequentially
const firstTween = new TWEEN.Tween(element.position).to({ x: 5 }, 1000);
 
const secondTween = new TWEEN.Tween(element.position).to({ y: 5 }, 1000);
 
const thirdTween = new TWEEN.Tween(element.position).to({ z: 5 }, 1000);
 
// Chain animations
firstTween.chain(secondTween);
secondTween.chain(thirdTween);
firstTween.start();

API

TWEEN

TWEEN library provides powerful animation capabilities:

MethodDescriptionParameters
constructorCreate new tween instance(object: Object)
toSet animation target(properties: Object, duration: number)
easingSet easing function(easing: Function)
startStart animation() => Tween
stopStop animation() => Tween
chainChain next animation(...tweens: Tween[])
yoyoEnable yoyo animation(yoyo: boolean)
repeatSet repeat count(times: number)
onUpdateAnimation update callback(callback: Function)
onCompleteAnimation complete callback(callback: Function)

Easing Functions

TWEEN provides rich easing functions:

  • TWEEN.Easing.Linear.None - Linear
  • TWEEN.Easing.Quadratic.In/Out/InOut - Quadratic
  • TWEEN.Easing.Cubic.In/Out/InOut - Cubic
  • TWEEN.Easing.Quartic.In/Out/InOut - Quartic
  • TWEEN.Easing.Quintic.In/Out/InOut - Quintic
  • TWEEN.Easing.Sinusoidal.In/Out/InOut - Sinusoidal
  • TWEEN.Easing.Exponential.In/Out/InOut - Exponential
  • TWEEN.Easing.Circular.In/Out/InOut - Circular
  • TWEEN.Easing.Elastic.In/Out/InOut - Elastic
  • TWEEN.Easing.Back.In/Out/InOut - Back
  • TWEEN.Easing.Bounce.In/Out/InOut - Bounce

Element Properties

Common element properties that can be animated:

PropertyDescriptionType
positionPosition (x, y, z)Vector3
rotationRotation (x, y, z)Euler
scaleScale (x, y, z)Vector3

Best Practices

  1. Performance Considerations: Avoid running too many animations simultaneously, properly use stop() method to clean up animations
  2. Memory Management: Use ref to save animation instances for better management and cleanup
  3. User Experience: Provide ways to stop animations, such as clicking empty area to stop
  4. Animation Composition: Properly use chained and parallel animations to create complex effects
  5. Easing Functions: Choose appropriate easing functions to make animations more natural

TWEEN Documentation

https://tweenjs.github.io/tween.js (opens in a new tab)