Skip to content
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
46 changes: 46 additions & 0 deletions packages/core/src/Controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,52 @@ describe('Controller', () => {
// Since we call `update` twice, frames are generated!
expect(global.getFrames(ctrl)).toMatchSnapshot()
})

describe('when skipAnimations is true', () => {
it('should not run at all', async () => {
const ctrl = new Controller({ from: { x: 0 } })
let n = 0

global.setSkipAnimation(true)

ctrl.start({
to: async next => {
while (true) {
n += 1
await next({ x: 1, reset: true })
}
},
})

await flushMicroTasks()
expect(n).toBe(0)
})

it('should stop running and push the animation to the finished state when called mid animation', async () => {
const ctrl = new Controller({ from: { x: 0 } })
let n = 0

ctrl.start({
to: async next => {
while (n < 5) {
n++
await next({ x: 10, reset: true })
}
},
})

await global.advance()
expect(n).toBe(1)

global.setSkipAnimation(true)

await global.advanceUntilIdle()

const { x } = ctrl.springs
expect(n).toBe(2)
expect(x.get()).toEqual(10)
})
})
})

describe('when the "onStart" prop is defined', () => {
Expand Down
44 changes: 43 additions & 1 deletion packages/core/src/runAsync.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { is, raf, flush, eachProp, Timeout } from '@react-spring/shared'
import {
is,
raf,
flush,
eachProp,
Timeout,
Globals as G,
} from '@react-spring/shared'
import { Falsy } from '@react-spring/types'

import { getDefaultProps } from './helpers'
Expand Down Expand Up @@ -88,8 +95,23 @@ export function runAsync<T extends AnimationTarget>(
// Create the bail signal outside the returned promise,
// so the generated stack trace is relevant.
const bailSignal = new BailSignal()
const skipAnimationSignal = new SkipAniamtionSignal()

return (async () => {
if (G.skipAnimation) {
/**
* We need to stop animations if `skipAnimation`
* is set in the Globals
*
*/
stopAsync(state)

// create the rejection error that's handled gracefully
skipAnimationSignal.result = getFinishedResult(target, false)
bail(skipAnimationSignal)
throw skipAnimationSignal
}

bailIfEnded(bailSignal)

const props: any = is.obj(arg1) ? { ...arg1 } : { ...arg2, to: arg1 }
Expand All @@ -115,6 +137,16 @@ export function runAsync<T extends AnimationTarget>(
}

let result!: AnimationResult<T>

if (G.skipAnimation) {
/**
* We need to stop animations if `skipAnimation`
* is set in the Globals
*/
stopAsync(state)
return getFinishedResult(target, false)
}

try {
let animating!: Promise<void>

Expand All @@ -139,6 +171,8 @@ export function runAsync<T extends AnimationTarget>(
} catch (err) {
if (err instanceof BailSignal) {
result = err.result
} else if (err instanceof SkipAniamtionSignal) {
result = err.result
} else {
throw err
}
Expand Down Expand Up @@ -181,3 +215,11 @@ export class BailSignal extends Error {
)
}
}

export class SkipAniamtionSignal extends Error {
result!: AnimationResult

constructor() {
super('SkipAnimationSignal')
}
}
9 changes: 9 additions & 0 deletions packages/core/test/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ declare global {

// @ts-ignore
setTimeout: (handler: Function, ms: number) => number

setSkipAnimation: (skip: boolean) => void
}
}
}
Expand All @@ -60,6 +62,7 @@ beforeEach(() => {
now: global.mockRaf.now,
requestAnimationFrame: global.mockRaf.raf,
colors,
skipAnimation: false,
// This lets our useTransition hook force its component
// to update from within an "onRest" handler.
batchedUpdates: act,
Expand Down Expand Up @@ -184,3 +187,9 @@ global.advanceUntilValue = (spring, value) => {
return stop
})
}

global.setSkipAnimation = skip => {
Globals.assign({
skipAnimation: skip,
})
}