motion-canvas/examples

Is there a better way of writing this?

Closed this issue · 2 comments

Hello, I am trying to wrap my head around the action chaining behaviors in this library. Using the default example from the docs, I expanded it a bit to have the circle move around in a square pattern.

import {makeScene2D, Circle} from '@motion-canvas/2d';
import {all, createRef} from '@motion-canvas/core';

export default makeScene2D(function* (view) {
  const myCircle = createRef<Circle>();

  view.add(
    <Circle
      ref={myCircle}
      // try changing these properties:
      x={-300}
      width={140}
      height={140}
      fill="#eeeeee"
    />,
  );

  yield* all(
      myCircle().position.x(300, 1),
      myCircle().fill('#550000', 1),
  );
  
  yield* all(
      myCircle().position.y(300, 1),
      myCircle().fill('#000055', 1),
  );

  yield* all(
    myCircle().position.x(-300, 1),
    myCircle().fill('#005500', 1),
);

yield* all(
  myCircle().position.y(-300, 1),
  myCircle().fill('#555555', 1),
);

});

My question is, is there a better, or more concise way of doing this?
I had originally tried chaining .to() statements inside of a single yield* all(), eg,

yield* all(
    myCircle().position.x(300, 1).to(-300, 3),
    myCircle().fill('#550000', 1).to('#000055', 2).to('#005500', 3).to('#555555', 4),
    myCircle().position.y(300, 2).to(-300, 4),
  );

But this does not work how I was thinking it might. I was thinking that the .to() method would chain, and use the second parameter to break up the steps, but it does not.

The second parameter of to() is the duration of the tween.
You can use wait() to synchronize chains together:

yield* all(
  myCircle().position.x(300, 1).wait(1).to(-300, 1),
  myCircle().fill('#550000', 1).to('#000055', 1).to('#005500', 1).to('#555555', 1),
  delay(1, myCircle().position.y(300, 1).wait(1).to(-300, 1)),
);

// the same code reformatted to highlight synchronization:
yield* all(
  myCircle().position.x(300, 1) .wait(         1) .to(-300,      1),
  myCircle().fill('#550000', 1) .to('#000055', 1) .to('#005500', 1) .to('#555555', 1),
  delay(1, myCircle().position  .y(300,        1) .wait(         1) .to(-300,      1)),
);

Or, alternatively, you can animate the entire position instead of individual components.
But in this case, you'll have to repeat certain values in multiple palces:

yield* all(
  myCircle().position([300, 0], 1).to([300, 300], 1).to([-300, 300], 1).to([-300, -300], 1),
  myCircle().fill('#550000', 1).to('#000055', 1).to('#005500', 1).to('#555555', 1),
);

// the same code reformatted to highlight synchronization:
yield* all(
  myCircle().position([300, 0], 1) .to([300, 300], 1) .to([-300, 300], 1) .to([-300, -300], 1),
  myCircle().fill('#550000',    1) .to('#000055',  1) .to('#005500',   1) .to('#555555',    1),
);

Thank you! This was very helpful!