-
Notifications
You must be signed in to change notification settings - Fork 186
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
Tweens #112
Comments
You can add Tweens by hooking into the new Plugin system with something like this
|
Very cool, I am definitely planning on adding something like this either as a plugin or directly to the engine. Will use yours to help me get started when I do it. |
Had some extra time so I did some more work, might be a bit more helpful now. class Tween {
static active = [];
constructor(fn, start, end, duration) {
// These two lines are to reverse the order of optional params
// Otherwise just use constructor(fn, duration, end=1, start=0)
end ?? ([start, end, duration] = [0, 1, start]);
duration ?? ([start, end, duration] = [0, start, end]);
// Properties for the Tween to function
this.duration = duration;
this.life = this.duration;
this.start = start;
this.end = end;
this.delta = this.end - this.start;
this.fn = fn;
// Callback for when Tween is completed
this.then = (f) => ((this.then = f), this);
this.setEase = (f) => ((this.ease = f), this);
Tween.active.push(this);
this.fn(this.interp(this.duration));
}
ease = (t) => t;
interp(life) {
const y = this.ease((this.duration - life) / this.duration);
return y * this.delta + this.start;
}
static update() {
// for(let t,i=0;i<Tween.living.length;i++)
// --(t=Tween.living[i]).life?t.fn(t.curr):(t.fn(t.end),Tween.living.splice(i--,1),t.then());
for (let i = 0; i < Tween.active.length; i++) {
const twn = Tween.active[i];
if (--twn.life) twn.fn(twn.interp(twn.life));
else {
twn.fn(twn.interp(0));
Tween.active.splice(i--, 1);
twn.then();
}
}
}
static Loop = function (n) {
function repeat() {
new Tween(this.fn, this.start, this.end, this.duration)
.setEase(this.ease)
.then(Tween.Loop(n));
}
if (--n == 0) return () => {};
else if (n) return repeat;
else Tween.Loop(Infinity).call(this);
};
static PingPong = function (n) {
function repeat() {
new Tween(this.fn, this.end, this.start, this.duration)
.setEase(this.ease)
.then(Tween.PingPong(n));
}
if (--n == 0) return () => {};
else if (n) return repeat;
else Tween.PingPong(Infinity).call(this);
};
}
class Ease {
static LINEAR = (x) => x;
static POWER = (n) => (x) => x ** n;
static SINE = (x) => 1 - Math.cos(x * (Math.PI / 2));
static CIRC = (x) => 1 - Math.sqrt(1 - x * x);
static EXPO = (x) => 2 ** (10 * x - 10);
static BACK = (x) => x * x * (2.70158 * x - 1.70158);
static ELASTIC = (x) =>
-(2 ** (10 * x - 10)) * Math.sin(((37 - 40 * x) * Math.PI) / 6);
static SPRING = (x) =>
1 -
(Math.sin(Math.PI * (1 - x) * (0.2 + 2.5 * (1 - x) ** 3)) *
Math.pow(x, 2.2) +
(1 - x)) *
(1.0 + 1.2 * x);
static BOUNCE = (x) => {
const bounceOut = (x) => {
if (x < 4 / 11) return 7.5625 * x * x;
if (x < 8 / 11) return bounceOut(x - 6 / 11) + 0.75;
if (x < 10 / 11) return bounceOut(x - 9 / 11) + 0.9375;
return bounceOut(x - 10.5 / 11) + 0.984375;
};
return Ease.OUT(bounceOut)(x);
};
static BEZIER = (x1, y1, x2, y2) => {
const curve = (t) => {
const u = 1 - t;
const c1 = 3 * u * u * t;
const c2 = 3 * u * t * t;
const t3 = t ** 3;
return [c1 * x1 + c2 * x2 + t3, c1 * y1 + c2 * y2 + t3];
};
return (x) => {
for (let i = 0, t0 = 0, t1 = 1; i < 128; i++) {
const tMid = (t0 + t1) / 2;
const [bx, by] = curve(tMid);
if (Math.abs(bx - x) < 1e-5) return by;
else if (bx < x) t0 = tMid;
else t1 = tMid;
}
return curve((t0 + t1) / 2)[1];
};
};
static IN = (x) => x;
static OUT = (f) => (x) => 1 - f(1 - x);
static IN_OUT = (f) => Piecewise(f, Ease.OUT(f));
static PIECEWISE = (...fns) => {
const n = fns.length;
return (x) => {
let i = (x * n - 1e-9) >> 0;
return (fns[i]((x - i / n) * n) + i) / n;
};
};
} And some Examples to demo them a bit better: Example 1: Countdown Timerconst txt = (t) => drawTextScreen(t, mainCanvasSize.scale(0.5), 80);
new Tween((t) => txt(t >> 0), 10, 1, 600).then(() => txt("BOOM!")); Example 2: Chaining Tweensconst obj = new EngineObject();
new Tween((t) => (obj.pos.x = t), -10, 10, 600).then = () =>
new Tween((t) => (obj.size = new Vector2(t, t)), 1, 0, 60); Example 3: Loopingconst obj = new EngineObject(new Vector2(0, -1));
const obj2 = new EngineObject(new Vector2(0, 1));
new Tween((t) => (obj.pos.x = t), -10, 10, 60).then(Tween.PingPong);
new Tween((t) => (obj2.pos.x = t), -10, 10, 60).then(Tween.Loop(5)); Example 4: Easingconst obj = new EngineObject(new Vector2(3, 3));
new Tween((t) => (obj.pos.y = t), 12, -10, 120)
.then(Tween.Loop)
.setEase(Ease.OUT(Ease.BOUNCE)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Is there something like a tween system planned? Something that could modify values in time, with easings etc.?
The text was updated successfully, but these errors were encountered: