Skip to content

Commit

Permalink
feat: Interpol accept inline props
Browse files Browse the repository at this point in the history
  • Loading branch information
willybrauner committed Jan 3, 2025
1 parent f530809 commit 47564ae
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 66 deletions.
97 changes: 42 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,7 @@ npm i @wbe/interpol
import { Interpol } from "@wbe/interpol"

new Interpol({
props: {
v: [0, 100],
},
v: [0, 100],
duration: 1000,
ease: (t) => t,
onUpdate: ({ v }, time, progress) => {
Expand Down Expand Up @@ -115,19 +113,15 @@ const tl = new Timeline({

// Set an Interpol instance object constructor directly
tl.add({
props: {
x: [0, 100],
},
x: [0, 100],
onComplete: ({ x }, time, progress) => {
// itp 1 is complete
},
})

// Or add interpol instance to the timeline
const itp = new Interpol({
props: {
x: [100, 50],
},
x: [100, 50],
duration: 500,
onComplete: ({ x }, time, progress) => {
// itp 2 is complete
Expand All @@ -150,19 +144,17 @@ For more flexibility, there is three ways to define a single `prop`:

```ts
new Interpol({
props: {
// 1. a simple number, implicite from is `0`
// to use only when `from` is `0`
x: 100,

// 2. an array
// [from, to, unit?]
x: [0, 100],

// 3. an object with explicite `from` and `to` properties
// { from?, to, unit?, ease?, reverseEase? }
x: { from: 0, to: 100 },
},
// 1. a simple number, implicite from is `0`
// to use only when `from` is `0`
x: 100,

// 2. an array
// [from, to, unit?]
x: [0, 100],

// 3. an object with explicite `from` and `to` properties
// { from?, to, unit?, ease?, reverseEase? }
x: { from: 0, to: 100 },
})
```

Expand All @@ -173,16 +165,14 @@ Three ways to define a `to` computed value on the same `prop` model:

```ts
new Interpol({
props: {
// 1. number
x: () => Math.random(),
// 1. number
x: () => Math.random(),

// 2. array
x: [0, () => Math.random()],
// 2. array
x: [0, () => Math.random()],

// 3. object
x: { from: 0, to: () => Math.random() },
},
// 3. object
x: { from: 0, to: () => Math.random() },
})
```

Expand Down Expand Up @@ -211,9 +201,7 @@ Without the unit:

```ts
new Interpol({
props: {
top: [-100, 0],
},
top: [-100, 0],
onUpdate: ({ top }) => {
// Set manually the unit each time
element.style.top = `${top}px`
Expand All @@ -225,10 +213,8 @@ With the unit as 3th value:

```ts
new Interpol({
props: {
// [from, to, unit]
top: [-100, 0, "px"],
},
// [from, to, unit]
top: [-100, 0, "px"],
onUpdate: ({ top }) => {
// top is value + "px" is already defined
element.style.top = top
Expand Down Expand Up @@ -256,10 +242,8 @@ Example:
import { Interpol, styles } from "./Interpol"

new Interpol({
props: {
x: [-100, 0, "%"],
opacity: [0, 1],
},
x: [-100, 0, "%"],
opacity: [0, 1],
onUpdate: ({ x, opacity }) => {
styles(element, { x, opacity })

Expand All @@ -278,10 +262,8 @@ But it might be redundant to set props on item styles every time we want to anim
new Interpol({
// can recieve HTMLElement or HTMLElement[]
el: document.querySelector("div"),
props: {
x: [-100, 0, "%"],
opacity: [0, 1],
},
x: [-100, 0, "%"],
opacity: [0, 1],
})
```

Expand Down Expand Up @@ -309,10 +291,8 @@ const tl = new Timeline({
// `add()` can recieve an Interpol object constructor
tl.add({
el: element,
props: {
x: [-100, 0, "%"],
y: [0, 100, "px"],
},
x: [-100, 0, "%"],
y: [0, 100, "px"],
duration: 1000,
ease: (t) => t * (2 - t),
onComplete: () => {
Expand All @@ -322,9 +302,7 @@ tl.add({

tl.add(
{
props: {
width: [10, 50],
},
width: [10, 50],
duration: 500,
ease: (t) => t * t,
onUpdate: ({ width }) => {
Expand Down Expand Up @@ -374,14 +352,23 @@ new Interpol({

```ts
import { EaseFn } from "./ease"
type Value = number | (() => number)

interface IInterpolConstruct<K extends keyof Props> {
// props are an interpol list object, 3 definition types
// the props value type can be a single number, an array or an object
export type PropsValues =
// 1. to
| Value
// 2. [from, to, unit]
| [Value, Value, (Units | null | undefined)?]
// 3. { from, to, unit, ease }
| Partial<{ from: Value; to: Value; unit: Units; ease: Ease; reverseEase: Ease }>

interface IInterpolConstruct<K extends keyof Props> {
// inline props are an interpol list object, 3 definition types
// default: /
props: Record<K, [number | (() => number), number | (() => number), string]>
[x: string]: PropsValues
// or props object wrapper (old way kept for backward compatibility)
props?: Record<string, PropsValues>

// Interpolation duration between `from` and `to` values (millisecond).
// ex: 1000 is 1 second
Expand Down
14 changes: 8 additions & 6 deletions examples/interpol-basic/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,25 @@ inputProgress!.onchange = () => itp.seek(parseFloat(inputProgress!.value) / 100,
inputSlider!.oninput = () => itp.seek(parseFloat(inputSlider!.value) / 100, false)

const itp = new Interpol({
// debug: true,
// declare a props object
props: {
x: { from: 0, to: 100, ease: "power3.out" },
y: { from: 0, to: 500, ease: "power1.in" },
opacity: [0.5, 1],
},
onUpdate: ({ x, y, opacity }) => {
// declare inline props outside the props object
top: [0, 100],
left: [-100, 100],

onUpdate: ({ x, y, opacity, top }) => {
ball!.style.transform = `translate3d(${x}px, ${y}px, 0px)`
ball!.style.opacity = opacity
},
onComplete: () => {
console.log("itp onComplete")
onComplete: (props) => {
console.log("itp onComplete", props)
},
})

console.log("itp", itp)
console.log("InterpolOptions.ticker", InterpolOptions.ticker)
InterpolOptions.ticker.disable()

const tick = (e: number) => {
Expand Down
3 changes: 2 additions & 1 deletion packages/interpol/src/Interpol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export class Interpol<K extends keyof Props = keyof Props> {
onComplete = noop,
debug = false,
el = null,
...inlineProps
}: InterpolConstruct<K>) {
this.ticker = InterpolOptions.ticker
this.#duration = duration
Expand All @@ -103,7 +104,7 @@ export class Interpol<K extends keyof Props = keyof Props> {
this.#reverseEase = reverseEase

// Prepare & compute props
this.#props = this.#prepareProps<K>(props)
this.#props = this.#prepareProps<K>({ ...(props || {}), ...(inlineProps as Props<K>) })
this.refreshComputedValues()
this.#propsValueRef = this.#createPropsParamObjRef<K>(this.#props)

Expand Down
6 changes: 3 additions & 3 deletions packages/interpol/src/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ export type CallBack<K extends keyof Props> = (
props: PropsValueObjectRef<K>,
time: number,
progress: number,
instance: Interpol<K>
instance: Interpol<K>,
) => void

export type El = HTMLElement | HTMLElement[] | Record<any, number> | null

export interface InterpolConstruct<K extends keyof Props> {
props: Props<K>
props?: Props<K>
duration?: Value
ease?: Ease
reverseEase?: Ease
Expand All @@ -61,14 +61,14 @@ export interface InterpolConstruct<K extends keyof Props> {
onUpdate?: CallBack<K>
onComplete?: CallBack<K>
el?: El
[key: string]: PropsValues | boolean | Function | undefined | El | Ease | CallBack<K> | Props<K>
}

/**
* Timeline
*
*
*/

export type TimelineCallback = (time: number, progress: number) => void

export interface TimelineConstruct {
Expand Down
42 changes: 41 additions & 1 deletion packages/interpol/tests/Interpol.props.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { it, expect, describe, vi } from "vitest"
import { it, expect, describe } from "vitest"
import { Interpol } from "../src"
import "./_setup"

Expand Down Expand Up @@ -78,4 +78,44 @@ describe.concurrent("Interpol props", () => {
test(null, 0, "number"),
])
})

it("should accept inline props", async () => {
return new Interpol({
duration: 100,
x: 100,
y: -100,
top: [0, 100],
left: [-100, 100, "px"],
onComplete: ({ x, y, top, left, right, marginRight }) => {
expect(x).toBe(100)
expect(y).toBe(-100)
expect(top).toBe(100)
expect(left).toBe("100px")
},
}).play()
})

it("should accept props object AND inline props together for backward compatibility", async () => {
return new Interpol({
duration: 100,
// object props
props: {
x: 100,
y: -100,
// top key will be overrided by inline props
top: -2000,
},
// inline props
top: [0, 100],
left: [-100, 100, "px"],

onComplete: ({ x, y, top, left, right }) => {
expect(x).toBe(100)
expect(y).toBe(-100)
expect(top).toBe(100)
expect(left).toBe("100px")
expect(right).toBe(undefined)
},
}).play()
})
})

0 comments on commit 47564ae

Please sign in to comment.