Giter Site home page Giter Site logo

pofulu / sparkar-pftween Goto Github PK

View Code? Open in Web Editor NEW
64.0 5.0 10.0 6.09 MB

Tween library for Meta Spark Studio (Spark AR).

Home Page: https://www.npmjs.com/package/sparkar-pftween

License: MIT License

JavaScript 40.40% TypeScript 59.60%
sparkar tween animation spark-ar spark-ar-studio spark-ar-creators ease

sparkar-pftween's Introduction

PFTween

index

PFTween is a Spark AR library for tweening animation.

You can use the similar syntax to DOTween to create animation with JavaScript/TypeScript in Spark AR.

Table of Contents

Install

NPM

You can download script and import it into your Spark AR project, or use this with npm.

  1. Download PFTween.ts

  2. Drag/Import it into your project. (Spark AR support TypeScript since v105)

  3. Import Ease and PFTween module at the top of your script.

    import { Ease, PFTween } from './PFTween';
  4. You can also Click Here to Download Sample Project (v118).

Usage

There are four ways to create animation with PFTween.

1. Basic - Simple and Easy

Create and use animation at once. Learn more

plane0.transform.x = new PFTween(-0.2, 0.2, 1000).scalar;

2. Reusable - Better Performance

Create and reuse/control it latter. Learn more

const animation = new PFTween(-0.2, 0.2, 1000)
  .onStart(v => plane0.transform.x = v.scalar)
  .build(false);

animation.replay();

3. Clip - Awaitable Animation

Create animation and you can await the them to complete. Learn more

const clip = new PFTween(-0.2, 0.2, 1000).clip;

Diagnostics.log('start');
await clip();
Diagnostics.log('complete');

4. Progress - Control Animation with Progress 0-1

Create then play tweens with progress you like. Learn more

const animation = new PFTween(0, 6, 1000).progress;
progress.setProgress(0)   // 0
progress.setProgress(0.5) // 3
progress.setProgress(1)   // 6

Getting Started

Let's create an animation, the value is from 0 to 1 in 1000 milliseconds, and output type is ScalarSignal.

new PFTween(0, 1, 1000).scalar;

You can set it to other ScalarSignal. E.g. position x, material's opacity, send to PatchEditor, etc.

const plane0 = await Scene.root.findFirst('plane0');
plane0.transform.x = new PFTween(0, 1, 1000).scalar;

You can also set the output to more value type as needed: .scalar, .pack2, .pack3, .pack4, .deg2rad, .swizzle(), .rgba, .patch().

plane0.transform.scale = new PFTween(0, 1, 1000).pack3;
plane0.transform.rotationZ = new PFTween(0, 360, 1000).deg2rad;
plane0.transform.position = new PFTween(-1, 1, 1000).swizzle('xx0');

The default movement is linear, you can change it by chain setEase() function.

new PFTween(0, 1, 1000)
  .setEase(Ease.easeInOutSine)  // Remeber to import Ease
  .scalar;

And you can add more function to modify this animation. E.g. Make it mirror loop 10 times.

new PFTween(0, 1, 1000)
  .setLoops(10)
  .setMirror()
  .setEase(Ease.easeInOutSine)
  .scalar;

Events

There are some events in animation, you can add callback to them using the function named onXXX.

new PFTween(0, 1, 1000)
  .onStart(tweener => {})   // When start, with tweener
  .onComplete(() => {)      // When animation stop
  .onLoop(iteration => {})  // When loop, with iteration
  .onUpdate(value => {})    // When tween value changed, with number or number[] 

There are also some useful function that can save you time.

const plane0 = await Scene.root.findFirst('plane0');
const material0 = await Materials.findFirst('material0');

new PFTween(0, 1, 1000)
  .setDelay(1000)  // Delay 1000 milliseconds to start
  .onStartVisible(plane0)
  .onStartHidden(plane0)
  .onCompleteVisible(plane0)
  .onCompleteHidden(plane0)
  .onCompleteResetPosition(plane0)
  .onCompleteResetRotation(plane0)
  .onCompleteResetScale(plane0)
  .onCompleteResetOpacity(material0)
  .onAnimatingVisibleOnly(plane0)
  .build()

Array of numbers

The from and to can be number or number[]. When you use number[] make sure the two array have the same length.

new PFTween([0, 0], [1, 2], 1000);    // O
new PFTween([0, 0, 0], [1, 2], 1000); // X

Notice that the output of number and number[] are somewhat different.

new PFTween([0, 0], [1, 2], 1000).scalar; // final: 1
new PFTween([0, 0], [1, 2], 1000).pack2;  // final: {x:1 ,y:2}
new PFTween([0, 0], [1, 2], 1000).pack3;  // final: {x:1 ,y:2, z:0}

new PFTween(0, 1, 1000).scalar; // final: 1
new PFTween(0, 1, 1000).pack2;  // final: {x:1 ,y:1}
new PFTween(0, 1, 1000).pack3;  // final: {x:1 ,y:1, z:1}

You can also pass the ScalarSignal, Point2DSignal, PointSignal, Point4DSignal. These values will be converted to number or number[] when you create animation.

new PFTween(plane0.transform.x, 1, 1000);
new PFTween(plane0.transform.scale, [0, 0, 0], 1000);

Reuse the Animation

Everytime you call new PFTween() will create a new animation object. Sometimes, it's not neccesary to create a new animation, you can reuse it for better performance. (However, in generally, user don't notice the performance impact as well)

E.g., you need to punch a image every time user open their mouth:

const onMouthOpen = FaceTracking.face(0).mouth.openness.gt(0.2).onOn();
onMouthOpen.subscribe(play_punch_animation);

function play_punch_animation(){
  plane0.transform.scale = new PFTween(1, 0.3, 1000).setEase(Ease.punch).pack3;
}

It works, but you don't need to create a new animation every time you play.

Use onStart() to set the value and call build() at the end of PFTween chain. It will return a PFTweener, a controller for PFTween object. You can call replay, reverse, start, stop or get isRunning.

const onMouthOpen = FaceTracking.face(0).mouth.openness.gt(0.2).onOn();
const play_punch_animation = new PFTween(1, 0.3, 1000)
  .setEase(Ease.punch)
  .onStart(tweener => plane0.transform.scale = tweener.pack3)
  .build(false); // The 'false' means don't play animation when build. Default is 'true'.
    
onMouthOpen.subscribe(() => play_punch_animation.replay());

PFTweener is actually a wrapped AnimationModule.TimeDriver, so you can find the similar APIs from the official document.

Play Animations in Sequence

.clip is an asynchronous way to reuse animation based on Promise. With clip, you can play tween animation in sequence.

E.g., jump().then(scale).then(rotate).then(fadeout).then(......

In order to use clip, you must set the value with onStart(), and get clip instead of call build() at the end of PFTween chain.

When you get clip, it returns a Promise. If you want to play the clip, just call clip().

const clip1 = new PFTween(0, 1, 500).clip;
const clip2 = new PFTween(1, 2, 500).clip;
const clip3 = new PFTween(2, 3, 500).clip;

clip1().then(clip2).then(clip3);

In addition to manually play multiple clips using then(), you can also use PFTween.concat() to concatenate them into one clip.

const clip1 = new PFTween(0, 1, 500).clip;
const clip2 = new PFTween(1, 2, 500).clip;
const clip3 = new PFTween(2, 3, 500).clip;

const animations = PFTween.concat(clip1, clip2, clip3);
animations();

If you want to start multiple clips at the same time, you can use PFTween.combine() to combine multiple clips in to one clip.

const clip1 = new PFTween(0, 1, 500).clip;
const clip2 = new PFTween(1, 2, 500).clip;
const clip3 = new PFTween(2, 3, 500).clip;

const animations = PFTween.combine(clip1, clip2, clip3);
animations();

Play Animation with Progress

.progress is based on Animation.ValueDriver, you can control it with progress you like. The progress value is clamped in 0-1.

The onComplete, onStart, onLoop and their related won't work, so you have to use onUpdate() to set values.

const animation = new PFTween(-0.1, 0.1, 500).onUpdate(v => plane0.transform.x = v).progress;
animation.setProgress(0);   // plane0.transform.x = -0.1
animation.setProgress(0.5); // plane0.transform.x = 0
animation.setProgress(1);   // plane0.transform.x = 0.1 

// or you can pass a ScalarSignal
animation.setProgress(new PFTween(0, 1, 1000).scalar);   

You can use combineProgress and concatProgress to merge multiple progress.

import { PFTween } from './PFTween';
import Scene from 'Scene';
import Diagnostics from 'Diagnostics';

(async () => {
 const plane0 = await Scene.root.findFirst('plane0');
 const p1 = new PFTween(0, 0.2, 500).onUpdate(v => plane0.transform.x = v).progress;
 const p2 = new PFTween(0, 0.1, 500).onUpdate(v => plane0.transform.y = v).progress;
 const p3 = new PFTween(0.2, 0, 500).onUpdate(v => plane0.transform.x = v).progress;

 // The "combineProgress" and "concatProgress" are static functions
 const combine = PFTween.combineProgress(p1, p2);
 const animation = PFTween.concatProgress(combine, p3);
})();

Stop Animation

There are three ways to create animation with PFTween.

1. With Reusable Tween

If your animation is made with .build(), it's will return a controller. You can stop the animation with controller's stop() function.

import { PFTween } from './PFTween';
import Scene from 'Scene';
import TouchGestures from 'TouchGestures';

(async () => {
  const plane0 = await Scene.root.findFirst('plane0');
 
  const controller = new PFTween(0, 1, 1000)
    .setLoops(true)
    .setId('foo')
    .onStart(v => plane0.transform.x = v.scalar)
    .build();
  
  TouchGestures.onTap().subscribe(() => {
    controller.stop();
  });
})();

2. Set ID

You can add .setId("id") to any of your tween, and then use the static function PFTween.kill("id") to kill and stop the animation. Please note that if you kill the animation, all of the events will be removed. (i.e. The animation you killed can't be reused)

import { PFTween } from './PFTween';
import Scene from 'Scene';
import TouchGestures from 'TouchGestures';

(async () => {
  const plane0 = await Scene.root.findFirst('plane0');
  
  plane0.transform.x = new PFTween(0, 1, 1000).setLoops(true).setId('foo').scalar;
  
  TouchGestures.onTap().subscribe(() => PFTween.kill('foo'));
})();

If your animation is created with basic way such .scalar, .pack2, .pack3...... The animation will be auto killed after complete.

3. Clip Cancellation

If you animation is made with .clip, you can create a cancellationa and pass it when you play the clip.

import { PFTween } from './PFTween';
import Scene from 'Scene';
import TouchGestures from 'TouchGestures';

(async () => {
  const plane0 = await Scene.root.findFirst('plane0');
  
  // PFTween.newCancellation is static function
  const cancellation = PFTween.newClipCancellation();
  
  new PFTween(0, 1, 1000)
    .setLoops(true)
    .onStart(v => plane0.transform.x = v.scalar)
    .clip(cancellation);
  
  TouchGestures.onTap().subscribe(() => {
    cancellation.cancel();
  });
})();

Unlike setId/kill, canceled clips can be played again, and all events you added will remain.

Donations

If this is useful for you, please consider a donation🙏🏼. One-time donations can be made with PayPal.

sparkar-pftween's People

Contributors

pofulu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

sparkar-pftween's Issues

Todolist

  • export PFTweener type
  • export IPFTweenClip type
  • Fix typo in README
  • Fix typo: PFTween.concatProgerss
  • Fix typo: tweener.scale
  • export IPFTweenProgress type

How to add a concat list of animations to a transform property

How do you add a concatenated list of animations to a simple object transform property?

I tried this

var animWalkerStart = new PFTween(walker.transform.x,endX,walkSpeed).scalar.clip;

var animWalkerLoop = new PFTween(startX,endX,walkSpeed).setLoops().scalar.clip;

const concat = PFTween.concat([animWalkerStart, animWalkerLoop]);
walker.transform.x=concat();

But I get this error:

Error:undefined is not a function
   {
     "line": 112,
     "column": 79,
     "sourceURL": "PFTween.js"
   }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.