Skip to content

Commit 74e6de9

Browse files
authored
feat(useTransition): support for reactive durations (vueuse#264)
* support reactive transition duration * improve test readability * demo variable transition duration * fix typo
1 parent b4f9989 commit 74e6de9

File tree

5 files changed

+82
-39
lines changed

5 files changed

+82
-39
lines changed

.storybook/style.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
@apply bg-gray-400 border-gray-600 text-gray-600 opacity-50 pointer-events-none;
3939
}
4040

41-
#demo input {
41+
#demo input:not([type=radio]) {
4242
@apply bg-transparent appearance-none border border-gray-600 rounded w-full py-2 px-4 my-2 mx-1 text-white leading-tight block;
4343
}
4444

packages/core/useTransition/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,6 @@ const easeOutElastic = (n) => {
6161

6262
useTransition(baseNumber, {
6363
duration: 1000,
64-
transition: easeInOutElastic,
64+
transition: easeOutElastic,
6565
})
6666
```

packages/core/useTransition/index.stories.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ defineDemo(
3131
defineComponent({
3232
setup() {
3333
const baseNumber = ref(0)
34+
const duration = ref(1000)
3435

3536
const easeOutElastic = (n: number) => {
3637
return n === 0
@@ -41,12 +42,12 @@ defineDemo(
4142
}
4243

4344
const cubicBezierNumber = useTransition(baseNumber, {
44-
duration: 1500,
45+
duration,
4546
transition: [0.75, 0, 0.25, 1],
4647
})
4748

4849
const customFnNumber = useTransition(baseNumber, {
49-
duration: 1500,
50+
duration,
5051
transition: easeOutElastic,
5152
})
5253

@@ -57,6 +58,7 @@ defineDemo(
5758
baseNumber,
5859
cubicBezierNumber,
5960
customFnNumber,
61+
duration,
6062
track,
6163
sled,
6264
}
@@ -66,6 +68,19 @@ defineDemo(
6668
<div>
6769
<button @click="toggle">Transition</button>
6870
71+
<p class="mt-2">
72+
Duration:
73+
<label class="ml-2">
74+
<input v-model="duration" type="radio" :value="1000" /> 1000ms
75+
</label>
76+
<label class="ml-2">
77+
<input v-model="duration" type="radio" :value="5000" /> 5000ms
78+
</label>
79+
<label class="ml-2">
80+
<input v-model="duration" type="radio" :value="10000" /> 10000ms
81+
</label>
82+
</p>
83+
6984
<p class="mt-2">
7085
Base number: <b>{{ baseNumber }}</b>
7186
</p>

packages/core/useTransition/index.test.ts

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { ref } from 'vue-demi'
22
import { useSetup } from '../../_tests'
33
import { useTransition, TransitionPresets } from '.'
4+
import { promiseTimeout } from '../../shared/utils'
45

56
describe('useTransition', () => {
6-
it('transitions between values', (done) => {
7+
it('transitions between values', async() => {
78
const vm = useSetup(() => {
89
const baseValue = ref(0)
910

@@ -25,22 +26,19 @@ describe('useTransition', () => {
2526
// changing the base value should start the transition
2627
vm.baseValue = 1
2728

28-
setTimeout(() => {
29-
// half way through the transition the base value should be 1,
30-
// and the transitioned value should be approximately 0.5
31-
expect(vm.baseValue).toBe(1)
32-
expect(vm.transitionedValue > 0 && vm.transitionedValue < 1).toBe(true)
33-
34-
setTimeout(() => {
35-
// once the transition is complete, both values should be 1
36-
expect(vm.baseValue).toBe(1)
37-
expect(vm.transitionedValue).toBe(1)
38-
done()
39-
}, 100)
40-
}, 50)
29+
// half way through the transition the base value should be 1,
30+
// and the transitioned value should be approximately 0.5
31+
await promiseTimeout(50)
32+
expect(vm.baseValue).toBe(1)
33+
expect(vm.transitionedValue > 0 && vm.transitionedValue < 1).toBe(true)
34+
35+
// once the transition is complete, both values should be 1
36+
await promiseTimeout(100)
37+
expect(vm.baseValue).toBe(1)
38+
expect(vm.transitionedValue).toBe(1)
4139
})
4240

43-
it('exposes named presets', (done) => {
41+
it('exposes named presets', async() => {
4442
const vm = useSetup(() => {
4543
const baseValue = ref(0)
4644

@@ -57,17 +55,14 @@ describe('useTransition', () => {
5755

5856
vm.baseValue = 1
5957

60-
setTimeout(() => {
61-
expect(vm.transitionedValue > 0 && vm.transitionedValue < 1).toBe(true)
58+
await promiseTimeout(50)
59+
expect(vm.transitionedValue > 0 && vm.transitionedValue < 1).toBe(true)
6260

63-
setTimeout(() => {
64-
expect(vm.transitionedValue).toBe(1)
65-
done()
66-
}, 100)
67-
}, 50)
61+
await promiseTimeout(100)
62+
expect(vm.transitionedValue).toBe(1)
6863
})
6964

70-
it('supports custom function transitions', (done) => {
65+
it('supports custom function transitions', async() => {
7166
const vm = useSetup(() => {
7267
const baseValue = ref(0)
7368

@@ -84,13 +79,44 @@ describe('useTransition', () => {
8479

8580
vm.baseValue = 1
8681

87-
setTimeout(() => {
88-
expect(vm.transitionedValue > 0 && vm.transitionedValue < 1).toBe(true)
82+
await promiseTimeout(50)
83+
expect(vm.transitionedValue > 0 && vm.transitionedValue < 1).toBe(true)
84+
85+
await promiseTimeout(100)
86+
expect(vm.transitionedValue).toBe(1)
87+
})
88+
89+
it('support dynamic transition durations', async() => {
90+
const vm = useSetup(() => {
91+
const baseValue = ref(0)
92+
const duration = ref(100)
93+
94+
const transitionedValue = useTransition(baseValue, {
95+
duration,
96+
transition: n => n,
97+
})
98+
99+
return {
100+
baseValue,
101+
duration,
102+
transitionedValue,
103+
}
104+
})
105+
106+
// first transition should take 100ms
107+
vm.baseValue = 1
108+
109+
await promiseTimeout(150)
110+
expect(vm.transitionedValue).toBe(1)
111+
112+
// second transition should take 200ms
113+
vm.duration = 200
114+
vm.baseValue = 2
115+
116+
await promiseTimeout(150)
117+
expect(vm.transitionedValue < 2).toBe(true)
89118

90-
setTimeout(() => {
91-
expect(vm.transitionedValue).toBe(1)
92-
done()
93-
}, 100)
94-
}, 50)
119+
await promiseTimeout(100)
120+
expect(vm.transitionedValue).toBe(2)
95121
})
96122
})

packages/core/useTransition/index.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { useRafFn } from '../useRafFn'
2-
import { Ref, ref, watch } from 'vue-demi'
3-
import { clamp, isFunction } from '@vueuse/shared'
2+
import { Ref, ref, unref, watch } from 'vue-demi'
3+
import { clamp, isFunction, MaybeRef } from '@vueuse/shared'
44

55
type CubicBezierPoints = [number, number, number, number]
66

77
type EasingFunction = (x: number) => number
88

99
interface TransitionOptions {
10-
duration?: number
10+
duration?: MaybeRef<number>
1111
transition?: EasingFunction | CubicBezierPoints
1212
}
1313

@@ -90,14 +90,15 @@ export function useTransition(source: Ref<number>, options: TransitionOptions =
9090
? transition
9191
: createEasingFunction(transition)
9292

93+
let currentDuration = 0
9394
let diff = 0
9495
let endAt = 0
9596
let startAt = 0
9697
let startValue = 0
9798

9899
const { resume, pause } = useRafFn(() => {
99100
const now = Date.now()
100-
const progress = clamp(1 - ((endAt - now) / duration), 0, 1)
101+
const progress = clamp(1 - ((endAt - now) / currentDuration), 0, 1)
101102

102103
output.value = startValue + (diff * getValue(progress))
103104

@@ -108,10 +109,11 @@ export function useTransition(source: Ref<number>, options: TransitionOptions =
108109
watch(source, () => {
109110
pause()
110111

112+
currentDuration = unref(duration)
111113
diff = source.value - output.value
112114
startValue = output.value
113115
startAt = Date.now()
114-
endAt = startAt + duration
116+
endAt = startAt + currentDuration
115117

116118
resume()
117119
})

0 commit comments

Comments
 (0)