motion-canvas / examples

A set of examples for Motion Canvas
MIT License
1.02k stars 60 forks source link

Is there a better way of writing this? #12

Closed aaronjolson closed 1 year ago

aaronjolson commented 1 year ago

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.

aarthificial commented 1 year ago

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),
);
aaronjolson commented 1 year ago

Thank you! This was very helpful!