Skip to content

Commit

Permalink
Merge pull request tweenjs#640 from tweenjs/freeze-Easing
Browse files Browse the repository at this point in the history
Freeze the Easing API so it is not patchable
  • Loading branch information
trusktr authored Apr 2, 2023
2 parents c44e7ae + d88fefc commit 0848416
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 73 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ name: build and tests

on:
push:
branches: [master]
branches: [main]
pull_request:
branches: [master]
branches: [main]

jobs:
build:
Expand Down
151 changes: 86 additions & 65 deletions src/Easing.ts
Original file line number Diff line number Diff line change
@@ -1,92 +1,114 @@
export type EasingFunction = (amount: number) => number

export type EasingFunctionGroup = {
In: EasingFunction
Out: EasingFunction
InOut: EasingFunction
}

/**
* The Ease class provides a collection of easing functions for use with tween.js.
*/
const Easing = {
Linear: {
None: function (amount: number): number {

export const Easing = Object.freeze({
Linear: Object.freeze<EasingFunctionGroup & {None: EasingFunction}>({
None(amount: number): number {
return amount
},
},
Quadratic: {
In: function (amount: number): number {
In(amount: number): number {
return this.None(amount)
},
Out(amount: number): number {
return this.None(amount)
},
InOut(amount: number): number {
return this.None(amount)
},
}),

Quadratic: Object.freeze(<EasingFunctionGroup>{
In(amount: number): number {
return amount * amount
},
Out: function (amount: number): number {
Out(amount: number): number {
return amount * (2 - amount)
},
InOut: function (amount: number): number {
InOut(amount: number): number {
if ((amount *= 2) < 1) {
return 0.5 * amount * amount
}

return -0.5 * (--amount * (amount - 2) - 1)
},
},
Cubic: {
In: function (amount: number): number {
}),

Cubic: Object.freeze(<EasingFunctionGroup>{
In(amount: number): number {
return amount * amount * amount
},
Out: function (amount: number): number {
Out(amount: number): number {
return --amount * amount * amount + 1
},
InOut: function (amount: number): number {
InOut(amount: number): number {
if ((amount *= 2) < 1) {
return 0.5 * amount * amount * amount
}
return 0.5 * ((amount -= 2) * amount * amount + 2)
},
},
Quartic: {
In: function (amount: number): number {
}),

Quartic: Object.freeze(<EasingFunctionGroup>{
In(amount: number): number {
return amount * amount * amount * amount
},
Out: function (amount: number): number {
Out(amount: number): number {
return 1 - --amount * amount * amount * amount
},
InOut: function (amount: number): number {
InOut(amount: number): number {
if ((amount *= 2) < 1) {
return 0.5 * amount * amount * amount * amount
}

return -0.5 * ((amount -= 2) * amount * amount * amount - 2)
},
},
Quintic: {
In: function (amount: number): number {
}),

Quintic: Object.freeze(<EasingFunctionGroup>{
In(amount: number): number {
return amount * amount * amount * amount * amount
},
Out: function (amount: number): number {
Out(amount: number): number {
return --amount * amount * amount * amount * amount + 1
},
InOut: function (amount: number): number {
InOut(amount: number): number {
if ((amount *= 2) < 1) {
return 0.5 * amount * amount * amount * amount * amount
}

return 0.5 * ((amount -= 2) * amount * amount * amount * amount + 2)
},
},
Sinusoidal: {
In: function (amount: number): number {
}),

Sinusoidal: Object.freeze(<EasingFunctionGroup>{
In(amount: number): number {
return 1 - Math.sin(((1.0 - amount) * Math.PI) / 2)
},
Out: function (amount: number): number {
Out(amount: number): number {
return Math.sin((amount * Math.PI) / 2)
},
InOut: function (amount: number): number {
InOut(amount: number): number {
return 0.5 * (1 - Math.sin(Math.PI * (0.5 - amount)))
},
},
Exponential: {
In: function (amount: number): number {
}),

Exponential: Object.freeze(<EasingFunctionGroup>{
In(amount: number): number {
return amount === 0 ? 0 : Math.pow(1024, amount - 1)
},
Out: function (amount: number): number {
Out(amount: number): number {
return amount === 1 ? 1 : 1 - Math.pow(2, -10 * amount)
},
InOut: function (amount: number): number {
InOut(amount: number): number {
if (amount === 0) {
return 0
}
Expand All @@ -101,23 +123,25 @@ const Easing = {

return 0.5 * (-Math.pow(2, -10 * (amount - 1)) + 2)
},
},
Circular: {
In: function (amount: number): number {
}),

Circular: Object.freeze(<EasingFunctionGroup>{
In(amount: number): number {
return 1 - Math.sqrt(1 - amount * amount)
},
Out: function (amount: number): number {
Out(amount: number): number {
return Math.sqrt(1 - --amount * amount)
},
InOut: function (amount: number): number {
InOut(amount: number): number {
if ((amount *= 2) < 1) {
return -0.5 * (Math.sqrt(1 - amount * amount) - 1)
}
return 0.5 * (Math.sqrt(1 - (amount -= 2) * amount) + 1)
},
},
Elastic: {
In: function (amount: number): number {
}),

Elastic: Object.freeze(<EasingFunctionGroup>{
In(amount: number): number {
if (amount === 0) {
return 0
}
Expand All @@ -128,7 +152,7 @@ const Easing = {

return -Math.pow(2, 10 * (amount - 1)) * Math.sin((amount - 1.1) * 5 * Math.PI)
},
Out: function (amount: number): number {
Out(amount: number): number {
if (amount === 0) {
return 0
}
Expand All @@ -138,7 +162,7 @@ const Easing = {
}
return Math.pow(2, -10 * amount) * Math.sin((amount - 0.1) * 5 * Math.PI) + 1
},
InOut: function (amount: number): number {
InOut(amount: number): number {
if (amount === 0) {
return 0
}
Expand All @@ -155,29 +179,31 @@ const Easing = {

return 0.5 * Math.pow(2, -10 * (amount - 1)) * Math.sin((amount - 1.1) * 5 * Math.PI) + 1
},
},
Back: {
In: function (amount: number): number {
}),

Back: Object.freeze(<EasingFunctionGroup>{
In(amount: number): number {
const s = 1.70158
return amount === 1 ? 1 : amount * amount * ((s + 1) * amount - s)
},
Out: function (amount: number): number {
Out(amount: number): number {
const s = 1.70158
return amount === 0 ? 0 : --amount * amount * ((s + 1) * amount + s) + 1
},
InOut: function (amount: number): number {
InOut(amount: number): number {
const s = 1.70158 * 1.525
if ((amount *= 2) < 1) {
return 0.5 * (amount * amount * ((s + 1) * amount - s))
}
return 0.5 * ((amount -= 2) * amount * ((s + 1) * amount + s) + 2)
},
},
Bounce: {
In: function (amount: number): number {
}),

Bounce: Object.freeze(<EasingFunctionGroup>{
In(amount: number): number {
return 1 - Easing.Bounce.Out(1 - amount)
},
Out: function (amount: number): number {
Out(amount: number): number {
if (amount < 1 / 2.75) {
return 7.5625 * amount * amount
} else if (amount < 2 / 2.75) {
Expand All @@ -188,37 +214,32 @@ const Easing = {
return 7.5625 * (amount -= 2.625 / 2.75) * amount + 0.984375
}
},
InOut: function (amount: number): number {
InOut(amount: number): number {
if (amount < 0.5) {
return Easing.Bounce.In(amount * 2) * 0.5
}
return Easing.Bounce.Out(amount * 2 - 1) * 0.5 + 0.5
},
},
generatePow: function (
power = 4,
): {
In(amount: number): number
Out(amount: number): number
InOut(amount: number): number
} {
}),

generatePow(power = 4): EasingFunctionGroup {
power = power < Number.EPSILON ? Number.EPSILON : power
power = power > 10000 ? 10000 : power
return {
In: function (amount: number): number {
In(amount: number): number {
return amount ** power
},
Out: function (amount: number): number {
Out(amount: number): number {
return 1 - (1 - amount) ** power
},
InOut: function (amount: number): number {
InOut(amount: number): number {
if (amount < 0.5) {
return (amount * 2) ** power / 2
}
return (1 - (2 - amount * 2) ** power) / 2 + 0.5
},
}
},
}
})

export default Easing
48 changes: 42 additions & 6 deletions src/tests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as TWEEN from './Index'
import * as FakeTimers from '@sinonjs/fake-timers'
import type {EasingFunctionGroup} from './Easing'

export const tests = {
hello(test: Test): void {
Expand Down Expand Up @@ -633,6 +634,46 @@ export const tests = {
test.done()
},

'Test TWEEN.Tween.EasingFunctionGroup should be frozen'(test: Test): void {
const replaceEasingFunction = (easingGroup: EasingFunctionGroup) => {
const throwsWithReassigned = () => {
easingGroup.In = (amount: number) => {
return 1.0 + amount
}
easingGroup.Out = (amount: number) => {
return 1.0 + amount
}
easingGroup.InOut = (amount: number) => {
return 1.0 + amount
}
}
test.throws(throwsWithReassigned)
test.equal(easingGroup.In(0.0), 0.0)
test.equal(easingGroup.Out(0.0), 0.0)
test.equal(easingGroup.InOut(0.0), 0.0)
test.equal(easingGroup.In(1.0), 1.0)
test.equal(easingGroup.Out(1.0), 1.0)
test.equal(easingGroup.InOut(1.0), 1.0)
}

const Easing = TWEEN.Easing
const easingGroups = [
Easing.Quadratic,
Easing.Cubic,
Easing.Quartic,
Easing.Quintic,
Easing.Sinusoidal,
Easing.Exponential,
Easing.Circular,
Easing.Elastic,
Easing.Back,
Easing.Bounce,
]
easingGroups.forEach(replaceEasingFunction)

test.done()
},

'Test TWEEN.Easing should starts at 0.0, ends at 1.0. TWEEN.Easing.InOut() should be 0.5 at midpoint'(
test: Test,
): void {
Expand Down Expand Up @@ -2193,15 +2234,10 @@ type Test = {
equal(a: unknown, b: unknown, failMessage?: string): void
deepEqual(a: unknown, b: unknown, failMessage?: string): void
expect(n: number): void
throws(block: unknown, error?: unknown, message?: string): void
done(): void
}

type EasingFunctionGroup = {
In(amount: number): number
Out(amount: number): number
InOut(amount: number): number
}

function toBeCloseTo(test: Test, numberA: number, numberB: number, numDigits = 2): void {
const diff = Math.abs(numberA - numberB)
test.ok(
Expand Down

0 comments on commit 0848416

Please sign in to comment.