Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1.0.0 add Action.follow(...), add .easeInOut() helpers #2

Merged
merged 12 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 84 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ Most actions implement specific predefined animations that are ready to use. If
const razzleDazzle = Action.sequence([
Action.unhide(),
Action.fadeIn(0.3),
Action.scaleTo(2, 0.3).withTimingMode(TimingMode.easeInSine),
Action.scaleTo(1, 0.3).withTimingMode(TimingMode.easeOutSine),
Action.scaleTo(2, 0.3).easeIn(),
Action.scaleTo(1, 0.3).easeOut(),
]);

// ✨ Show mySprite with some flair!
Expand All @@ -89,10 +89,12 @@ mySprite.run(razzleDazzle);

| Action | Description | Reversible? |
| :----- | :---------- | :---------- |
|***Chaining Actions***|||
| `Action.group(actions)` | Run multiple actions in parallel. | Yes |
| `Action.sequence(actions)` | Run multiple actions sequentially. | Yes |
| `Action.repeat(action, count)` | Repeat an action a specified number of times. | Yes |
| `Action.repeatForever(action)` | Repeat an action indefinitely. | Yes |
|***Animating a Node's Position in a Linear Path***|||
| `Action.moveBy(dx, dy, duration)` | Move a node by a relative amount. | Yes |
| `Action.moveByVector(vector, duration)` | Move a node by a relative vector (e.g. `PIXI.Point`). | Yes |
| `Action.moveByX(dx, duration)` | Move a node horizontally by a relative amount. | Yes |
Expand All @@ -101,60 +103,97 @@ mySprite.run(razzleDazzle);
| `Action.moveToPoint(point, duration)` | Move a node to a specified position (e.g. `PIXI.Point`). | _*No_ |
| `Action.moveToX(x, duration)` | Move a node to a specified horizontal position. | _*No_ |
| `Action.moveToY(y, duration)` | Move a node to a specified vertical position. | _*No_ |
| `Action.scaleBy(delta, duration)` | Scale a node by a relative amount. | Yes |
| `Action.scaleBy(dx, dy, duration)` | Scale a node by a relative amount. | Yes |
|***Animating a Node's Position Along a Custom Path***|||
| `Action.follow(path, duration)` | Move a node along a path, optionally orienting the node to the path. | Yes | Yes |
| `Action.followAtSpeed(path, speed)` | Move a node along a path at a specified speed, optionally orienting the node to the path. | Yes |
|***Animating the Rotation of a Node***|||
| `Action.rotateBy(delta, duration)` | Rotate a node by a relative value (in radians). | Yes |
| `Action.rotateByDegrees(delta, duration)` | Rotate a node by a relative value (in degrees). | Yes |
| `Action.rotateTo(radians, duration)` | Rotate a node to a specified value (in radians). | _*No_ |
| `Action.rotateToDegrees(degrees, duration)` | Rotate a node to a specified value (in degrees). | _*No_ |
|***Animating the Scaling of a Node***|||
| `Action.scaleBy(delta, duration)` | Scale a node by a relative value. | Yes |
| `Action.scaleBy(dx, dy, duration)` | Scale a node by a relative value. | Yes |
| `Action.scaleByVector(vector, duration)` | Scale a node by a given vector (e.g. `PIXI.Point`). | Yes |
| `Action.scaleXBy(dx, duration)` | Scale a node by a relative amount. | Yes |
| `Action.scaleYBy(dy, duration)` | Scale a node by a relative amount. | Yes |
| `Action.scaleByX(dx, duration)` | Scale a node by a relative value. | Yes |
| `Action.scaleByY(dy, duration)` | Scale a node by a relative value. | Yes |
| `Action.scaleTo(scale, duration)` | Scale a node to a specified value. | _*No_ |
| `Action.scaleTo(x, y, duration)` | Scale a node to a specified value. | _*No_ |
| `Action.scaleToSize(vector, duration)` | Scale a node to a specified size (e.g. `PIXI.Point`). | _*No_ |
| `Action.scaleXTo(x, duration)` | Scale a node to a specified value in the X-axis. | _*No_ |
| `Action.scaleYTo(y, duration)` | Scale a node to a specified value in the Y-axis. | _*No_ |
| `Action.scaleToX(x, duration)` | Scale a node to a specified value in the X-axis. | _*No_ |
| `Action.scaleToY(y, duration)` | Scale a node to a specified value in the Y-axis. | _*No_ |
|***Animating the Transparency of a Node***|||
| `Action.fadeIn(duration)` | Fade the alpha to `1.0`. | Yes |
| `Action.fadeOut(duration)` | Fade the alpha to `0.0`. | Yes |
| `Action.fadeAlphaBy(delta, duration)` | Fade the alpha by a relative value. | Yes |
| `Action.fadeAlphaTo(alpha, duration)` | Fade the alpha to a specified value. | _*No_ |
| `Action.rotateBy(delta, duration)` | Rotate a node by a relative value (in radians). | Yes |
| `Action.rotateByDegrees(delta, duration)` | Rotate a node by a relative value (in degrees). | Yes |
| `Action.rotateTo(radians, duration)` | Rotate a node to a specified value (in radians). | _*No_ |
| `Action.rotateToDegrees(degrees, duration)` | Rotate a node to a specified value (in degrees). | _*No_ |
| `Action.speedBy(delta, duration)` | Change how fast a node executes actions by a relative value. | Yes |
| `Action.speedTo(speed, duration)` | Set how fast a node executes actions. | _*No_ |
| `Action.hide()` | Set a node's `visible` property to `false`. | Yes |
| `Action.unhide()` | Set a node's `visible` property to `true`. | Yes |
| `Action.removeFromParent()` | Remove a node from its parent. | _†Identical_ |
| `Action.waitForDuration(duration)` | Idle for a specified interval. | _†Identical_ |
|***Controlling Node Visibility***|||
| `Action.unhide()` | Instant. Set a node's `visible` property to `true`. | Yes |
| `Action.hide()` | Instant. Set a node's `visible` property to `false`. | Yes |
|***Removing a Node from the Canvas***|||
| `Action.removeFromParent()` | Instant. Remove a node from its parent. | _†Identical_ |
|***Delaying Actions***|||
| `Action.waitForDuration(duration)` | Idle for a specified period of time. | _†Identical_ |
| `Action.waitForDurationWithRange(duration, range)` | Idle for a randomized period of time. | _†Identical_ |
| `Action.run(block)` | Execute a block of code immediately. | _†Identical_ |
| `Action.customAction(duration, stepHandler)` | Execute a custom stepping function over the duration. | _†Identical_ |

#### Reversing Actions
|***Triggers and Custom Actions***|||
| `Action.run(callback)` | Instant. Execute a block. | _†Identical_ |
| `Action.customAction(duration, stepHandler)` | Execute a custom stepping function over the action duration. | _†Identical_ |
|***Controlling the Node's Action Speed***|||
| `Action.speedBy(delta, duration)` | Change how fast a node executes its actions by a relative value. | Yes |
| `Action.speedTo(speed, duration)` | Set how fast a node executes actions to a specified value. | _*No_ |

### About Reversing Actions
All actions have a `.reversed()` method which will return an action with the reverse action on it. Some actions are **not reversible**, and these cases are noted in the table above:
- _**†Identical:**_ The reverse action will be identical to the action.
- _**\*No:**_ The reverse action will idle for the equivalent duration.
- _**†Identical:**_ The reversed action will be identical to the original action.
- _**\*No:**_ The reversed action will only idle for the equivalent duration.

## Timing Modes

All actions have a `timingMode` which controls the speed curve of its execution.

### TimingMode
The default timingMode for all actions is `TimingMode.linear`, which causes an animation to occur evenly over its duration.

The default timing mode for actions is `TimingMode.linear`.
> Note: The `timingMode` property of `.group()`, `.sequence()`, `.repeat()`, and `.repeatForever()` has no effect on the underlying action(s).

You can set a custom `TimingMode` (see options below), or you can provide a custom timing mode function.
You can customize the speed curve of actions like so:

| | InOut | In | Out | Description |
```ts
// Use the defaults
Action.fadeIn(0.3).easeInOut();
Action.fadeIn(0.3).easeIn();
Action.fadeIn(0.3).easeOut();

// Set a TimingMode
Action.fadeIn(0.3).setTimingMode(TimingMode.easeInOutCubic);

// Provide a custom function
Action.fadeIn(0.3).setTimingMode(x => x * x);
```

### Global Defaults

The `.easeInOut()`, `.easeIn()`, and `.easeOut()` methods set the timing mode of the action to the global default timing for that curve type.

You can set any global defaults for these by updating `Action.DefaultTimingModeEaseInOut`, `Action.DefaultTimingModeEaseIn`, and `Action.DefaultTimingModeEaseOut`. The default timing modes for these easings are `TimingMode.easeInOutSine`, `TimingMode.easeInSine`, and `TimingMode.easeOutSine` respectively.

### Built-in TimingMode Options

See the following table for default `TimingMode` options.

| Pattern | Ease In, Ease Out | Ease In | Ease Out | Description |
| --------------- | ----- | -- | --- | ----------- |
| **Linear** | `linear` | - | - | Constant motion with no acceleration or deceleration. |
| **Sine** | `easeInOutSine` | `easeInSine` | `easeOutSine` | Gentle start and end, with accelerated motion in the middle. |
| **Circ** | `easeInOutCirc` | `easeInCirc` | `easeOutCirc` | Smooth start and end, faster acceleration in the middle, circular motion. |
| **Circular** | `easeInOutCirc` | `easeInCirc` | `easeOutCirc` | Smooth start and end, faster acceleration in the middle, circular motion. |
| **Cubic** | `easeInOutCubic` | `easeInCubic` | `easeOutCubic` | Gradual acceleration and deceleration, smooth motion throughout. |
| **Quad** | `easeInOutQuad` | `easeInQuad` | `easeOutQuad` | Smooth acceleration and deceleration, starts and ends slowly, faster in the middle. |
| **Quadratic** | `easeInOutQuad` | `easeInQuad` | `easeOutQuad` | Smooth acceleration and deceleration, starts and ends slowly, faster in the middle. |
| **Quartic** | `easeInOutQuart` | `easeInQuart` | `easeOutQuart` | Slower start and end, increased acceleration in the middle. |
| **Quintic** | `easeInOutQuint` | `easeInQuint` | `easeOutQuint` | Very gradual start and end, smoother acceleration in the middle. |
| **Expo** | `easeInOutExpo` | `easeInExpo` | `easeOutExpo` | Very slow start, exponential acceleration, slow end. |
| **Exponential** | `easeInOutExpo` | `easeInExpo` | `easeOutExpo` | Very slow start, exponential acceleration, slow end. |
| **Back** | `easeInOutBack` | `easeInBack` | `easeOutBack` | Starts slowly, overshoots slightly, settles into final position. |
| **Bounce** | `easeInOutBounce` | `easeInBounce` | `easeOutBounce` | Bouncy effect at the start or end, with multiple rebounds. |
| **Elastic** | `easeInOutElastic` | `easeInElastic` | `easeOutElastic` | Stretchy motion with overshoot and multiple oscillations. |


### Custom actions

Actions are reusable, so you can create complex animations once, and then run them on many display objects.
Expand All @@ -164,17 +203,17 @@ Actions are reusable, so you can create complex animations once, and then run th
const rockBackAndForth = Action.repeatForever(
Action.sequence([
Action.group([
Action.moveXBy(5, 0.33),
Action.rotateByDegrees(-2, 0.33),
]).setTimingMode(TimingMode.easeOutQuad),
Action.moveXBy(5, 0.33).easeOut(),
Action.rotateByDegrees(-2, 0.33).easeOut(),
]),
Action.group([
Action.moveXBy(-10, 0.34),
Action.rotateByDegrees(4, 0.34),
]).setTimingMode(TimingMode.easeInOutQuad),
Action.moveXBy(-10, 0.34).easeInOut(),
Action.rotateByDegrees(4, 0.34).easeInOut(),
]),
Action.group([
Action.moveXBy(5, 0.33),
Action.rotateByDegrees(-2, 0.33),
]).setTimingMode(TimingMode.easeInQuad),
Action.moveXBy(5, 0.33).easeIn(),
Action.rotateByDegrees(-2, 0.33).easeIn(),
]),
])
);

Expand All @@ -190,12 +229,12 @@ You can combine these with dynamic actions for more variety:
```ts
const MyActions = {
squash: (amount: number, duration: number = 0.3) => Action.sequence([
Action.scaleTo(amount, 1 / amount, duration / 2).setTimingMode(TimingMode.easeOutSine),
Action.scaleTo(1, duration / 2).setTimingMode(TimingMode.easeInSine)
Action.scaleTo(amount, 1 / amount, duration / 2).easeOut(),
Action.scaleTo(1, duration / 2).easeIn()
]),
stretch: (amount: number, duration: number = 0.3) => Action.sequence([
Action.scaleTo(1 / amount, amount, duration / 2).setTimingMode(TimingMode.easeOutSine),
Action.scaleTo(1, duration / 2).setTimingMode(TimingMode.easeInSine)
Action.scaleTo(1 / amount, amount, duration / 2).easeOut(),
Action.scaleTo(1, duration / 2).easeIn()
]),
squashAndStretch: (amount: number, duration: number = 0.3) => Action.sequence([
MyActions.squash(amount, duration / 2),
Expand Down
Loading
Loading