Skip to content

Commit e13433e

Browse files
committed
fix: remove velocity-animate uses
1 parent f071414 commit e13433e

File tree

10 files changed

+410
-351
lines changed

10 files changed

+410
-351
lines changed

packages/webui/src/client/ui/App.tsx

Lines changed: 82 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -148,90 +148,88 @@ export const App: React.FC = function App() {
148148
return (
149149
<UserPermissionsContext.Provider value={roles}>
150150
<Router getUserConfirmation={onNavigationUserConfirmation}>
151-
<div className="container-fluid header-clear">
152-
{/* Header switch - render the usual header for all pages but the rundown view */}
153-
<ErrorBoundary>
154-
<Switch>
155-
<Route path="/rundown/:playlistId" component={NullComponent} />
156-
<Route path="/countdowns/:studioId" component={NullComponent} />
157-
<Route path="/activeRundown" component={NullComponent} />
158-
<Route path="/prompter/:studioId" component={NullComponent} />
159-
<Route
160-
path="/"
161-
render={(props) => (
162-
<Header
163-
{...props}
164-
allowConfigure={roles.configure}
165-
allowTesting={roles.testing}
166-
allowDeveloper={roles.developer}
167-
/>
168-
)}
169-
/>
170-
</Switch>
171-
</ErrorBoundary>
172-
{/* Main app switch */}
173-
<ErrorBoundary>
174-
<Switch>
175-
<Route exact path="/" component={RundownList} />
176-
<Route path="/rundowns" render={() => <RundownList />} />
177-
<Route
178-
path="/rundown/:playlistId/shelf"
179-
exact
180-
render={(props) => (
181-
<RundownView
182-
playlistId={protectString(decodeURIComponent(props.match.params.playlistId))}
183-
onlyShelf={true}
184-
/>
185-
)}
186-
/>
187-
<Route
188-
path="/rundown/:playlistId"
189-
render={(props) => (
190-
<RundownView playlistId={protectString(decodeURIComponent(props.match.params.playlistId))} />
191-
)}
192-
/>
193-
<Route
194-
path="/activeRundown/:studioId"
195-
render={(props) => (
196-
<ActiveRundownView studioId={protectString(decodeURIComponent(props.match.params.studioId))} />
197-
)}
198-
/>
199-
<Route
200-
path="/prompter/:studioId"
201-
render={(props) => (
202-
<PrompterView studioId={protectString(decodeURIComponent(props.match.params.studioId))} />
203-
)}
204-
/>
205-
{/* We switch to the general ClockView component, and allow it to do the switch between various types of countdowns */}
206-
<Route
207-
path="/countdowns/:studioId"
208-
render={(props) => (
209-
<ClockView studioId={protectString(decodeURIComponent(props.match.params.studioId))} />
210-
)}
211-
/>
212-
<Route path="/status" render={() => <Status />} />
213-
<Route path="/settings" render={() => <SettingsView />} />
214-
<Route path="/testTools" render={() => <TestTools />} />
215-
<Route>
216-
<Redirect to="/" />
217-
</Route>
218-
</Switch>
219-
</ErrorBoundary>
220-
<ErrorBoundary>
221-
<Switch>
222-
{/* Put views that should NOT have the Notification center here: */}
223-
<Route path="/countdowns/:studioId" component={NullComponent} />
224-
<Route path="/prompter/:studioId" component={NullComponent} />
225-
<Route path="/" component={ConnectionStatusNotification} />
226-
</Switch>
227-
</ErrorBoundary>
228-
<ErrorBoundary>
229-
<DocumentTitleProvider />
230-
</ErrorBoundary>
231-
<ErrorBoundary>
232-
<ModalDialogGlobalContainer />
233-
</ErrorBoundary>
234-
</div>
151+
{/* Header switch - render the usual header for all pages but the rundown view */}
152+
<ErrorBoundary>
153+
<Switch>
154+
<Route path="/rundown/:playlistId" component={NullComponent} />
155+
<Route path="/countdowns/:studioId" component={NullComponent} />
156+
<Route path="/activeRundown" component={NullComponent} />
157+
<Route path="/prompter/:studioId" component={NullComponent} />
158+
<Route
159+
path="/"
160+
render={(props) => (
161+
<Header
162+
{...props}
163+
allowConfigure={roles.configure}
164+
allowTesting={roles.testing}
165+
allowDeveloper={roles.developer}
166+
/>
167+
)}
168+
/>
169+
</Switch>
170+
</ErrorBoundary>
171+
{/* Main app switch */}
172+
<ErrorBoundary>
173+
<Switch>
174+
<Route exact path="/" component={RundownList} />
175+
<Route path="/rundowns" render={() => <RundownList />} />
176+
<Route
177+
path="/rundown/:playlistId/shelf"
178+
exact
179+
render={(props) => (
180+
<RundownView
181+
playlistId={protectString(decodeURIComponent(props.match.params.playlistId))}
182+
onlyShelf={true}
183+
/>
184+
)}
185+
/>
186+
<Route
187+
path="/rundown/:playlistId"
188+
render={(props) => (
189+
<RundownView playlistId={protectString(decodeURIComponent(props.match.params.playlistId))} />
190+
)}
191+
/>
192+
<Route
193+
path="/activeRundown/:studioId"
194+
render={(props) => (
195+
<ActiveRundownView studioId={protectString(decodeURIComponent(props.match.params.studioId))} />
196+
)}
197+
/>
198+
<Route
199+
path="/prompter/:studioId"
200+
render={(props) => (
201+
<PrompterView studioId={protectString(decodeURIComponent(props.match.params.studioId))} />
202+
)}
203+
/>
204+
{/* We switch to the general ClockView component, and allow it to do the switch between various types of countdowns */}
205+
<Route
206+
path="/countdowns/:studioId"
207+
render={(props) => (
208+
<ClockView studioId={protectString(decodeURIComponent(props.match.params.studioId))} />
209+
)}
210+
/>
211+
<Route path="/status" render={() => <Status />} />
212+
<Route path="/settings" render={() => <SettingsView />} />
213+
<Route path="/testTools" render={() => <TestTools />} />
214+
<Route>
215+
<Redirect to="/" />
216+
</Route>
217+
</Switch>
218+
</ErrorBoundary>
219+
<ErrorBoundary>
220+
<Switch>
221+
{/* Put views that should NOT have the Notification center here: */}
222+
<Route path="/countdowns/:studioId" component={NullComponent} />
223+
<Route path="/prompter/:studioId" component={NullComponent} />
224+
<Route path="/" component={ConnectionStatusNotification} />
225+
</Switch>
226+
</ErrorBoundary>
227+
<ErrorBoundary>
228+
<DocumentTitleProvider />
229+
</ErrorBoundary>
230+
<ErrorBoundary>
231+
<ModalDialogGlobalContainer />
232+
</ErrorBoundary>
235233
</Router>
236234
</UserPermissionsContext.Provider>
237235
)

packages/webui/src/client/ui/ClockView/OverlayScreenSaver.tsx

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ import { Clock } from '../StudioScreenSaver/Clock'
33
import { useTracker, useSubscription } from '../../lib/ReactMeteorData/ReactMeteorData'
44
import { MeteorPubSub } from '@sofie-automation/meteor-lib/dist/api/pubsub'
55
import { findNextPlaylist } from '../StudioScreenSaver/StudioScreenSaver'
6-
import Velocity from 'velocity-animate'
76
import { StudioId } from '@sofie-automation/corelib/dist/dataModel/Ids'
87
import { useSetDocumentClass } from '../util/useSetDocumentClass'
8+
import { AnimationPlaybackControls, animate as motionAnimate } from 'motion'
99

1010
export function OverlayScreenSaver({ studioId }: Readonly<{ studioId: StudioId }>): JSX.Element {
1111
const studioNameRef = useRef<HTMLDivElement>(null)
12+
const animationControlsRef = useRef<AnimationPlaybackControls | null>(null)
1213

1314
useSetDocumentClass('transparent')
1415

@@ -46,27 +47,18 @@ export function OverlayScreenSaver({ studioId }: Readonly<{ studioId: StudioId }
4647
el.style.position = 'absolute'
4748
el.style.left = '0.2em'
4849

49-
Velocity(
50-
el,
51-
{
52-
opacity: 1,
53-
},
54-
{
55-
duration: 3000,
56-
delay: 1000,
57-
}
58-
)
59-
Velocity(
60-
el,
61-
{
62-
opacity: 0,
63-
},
64-
{
65-
duration: 3000,
66-
delay: 5000,
67-
complete: () => animate(),
68-
}
69-
)
50+
const animation = motionAnimate([
51+
[el, { opacity: 1 }, { duration: 3, delay: 1 }],
52+
[el, { opacity: 1 }, { duration: 3, delay: 5 }],
53+
])
54+
animation
55+
.then(() => {
56+
animate()
57+
})
58+
.catch(() => {
59+
console.error('Unlikely animation failure')
60+
})
61+
animationControlsRef.current = animation
7062
}
7163
}
7264

@@ -81,7 +73,7 @@ export function OverlayScreenSaver({ studioId }: Readonly<{ studioId: StudioId }
8173
el.style.opacity = ''
8274
el.style.position = ''
8375
el.style.left = ''
84-
Velocity(el, 'stop', true)
76+
animationControlsRef.current?.stop()
8577
}
8678
}
8779
}, [studioNameRef.current, data?.rundownPlaylist?.name, data?.studio?.name])

packages/webui/src/client/ui/Prompter/PrompterView.tsx

Lines changed: 89 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import ClassNames from 'classnames'
55
import { Meteor } from 'meteor/meteor'
66
import { parse as queryStringParse } from 'query-string'
77
import { Route } from 'react-router-dom'
8-
import Velocity from 'velocity-animate'
8+
import { animate, AnimationPlaybackControls } from 'motion'
99
import {
1010
Translated,
1111
useGlobalDelayedTrackerUpdateState,
@@ -122,6 +122,8 @@ export class PrompterViewContent extends React.Component<Translated<IProps & ITr
122122
// @ts-expect-error The manager inspects this instance
123123
private _controller: PrompterControlManager
124124

125+
private _lastAnimation: AnimationPlaybackControls | null = null
126+
125127
private checkWindowScroll: number | null = null
126128

127129
constructor(props: Translated<IProps & ITrackedProps>) {
@@ -316,29 +318,66 @@ export class PrompterViewContent extends React.Component<Translated<IProps & ITr
316318
const scrollMargin = this.calculateScrollPosition()
317319
const target = document.querySelector<HTMLElement>(`[data-part-instance-id="${partInstanceId}"]`)
318320

319-
if (target) {
320-
Velocity(document.body, 'finish')
321-
Velocity(target, 'scroll', { offset: -1 * scrollMargin, duration: 400, easing: 'ease-out' })
322-
}
321+
if (!target) return
322+
// Velocity(document.body, 'finish')
323+
// Velocity(target, 'scroll', { offset: -1 * scrollMargin, duration: 400, easing: 'ease-out' })
324+
const offsetTop = window.scrollY + target.offsetTop
325+
this._lastAnimation?.stop()
326+
this._lastAnimation = animate(
327+
window,
328+
{
329+
scrollY: offsetTop + -1 * scrollMargin,
330+
},
331+
{
332+
duration: 0.4,
333+
ease: 'easeOut',
334+
}
335+
)
323336
}
324337
scrollToLive(): void {
325338
const scrollMargin = this.calculateScrollPosition()
326339
const current =
327340
document.querySelector<HTMLElement>('.prompter .live') || document.querySelector<HTMLElement>('.prompter .next')
328341

329-
if (current) {
330-
Velocity(document.body, 'finish')
331-
Velocity(current, 'scroll', { offset: -1 * scrollMargin, duration: 400, easing: 'ease-out' })
332-
}
342+
if (!current) return
343+
// Velocity(document.body, 'finish')
344+
// Velocity(current, 'scroll', { offset: -1 * scrollMargin, duration: 400, easing: 'ease-out' })
345+
// }
346+
347+
const offsetTop = window.scrollY + current.offsetTop
348+
this._lastAnimation?.stop()
349+
this._lastAnimation = animate(
350+
window,
351+
{
352+
scrollY: offsetTop + -1 * scrollMargin,
353+
},
354+
{
355+
duration: 0.4,
356+
ease: 'easeOut',
357+
}
358+
)
333359
}
334360
scrollToNext(): void {
335361
const scrollMargin = this.calculateScrollPosition()
336362
const next = document.querySelector<HTMLElement>('.prompter .next')
337363

338-
if (next) {
339-
Velocity(document.body, 'finish')
340-
Velocity(next, 'scroll', { offset: -1 * scrollMargin, duration: 400, easing: 'ease-out' })
341-
}
364+
if (!next) return
365+
// Velocity(document.body, 'finish')
366+
// Velocity(next, 'scroll', { offset: -1 * scrollMargin, duration: 400, easing: 'ease-out' })
367+
// }
368+
369+
const offsetTop = window.scrollY + next.offsetTop
370+
this._lastAnimation?.stop()
371+
this._lastAnimation = animate(
372+
window,
373+
{
374+
scrollY: offsetTop + -1 * scrollMargin,
375+
},
376+
{
377+
duration: 0.4,
378+
ease: 'easeOut',
379+
}
380+
)
342381
}
343382
scrollToPrevious(): void {
344383
const scrollMargin = this.calculateScrollPosition()
@@ -347,12 +386,25 @@ export class PrompterViewContent extends React.Component<Translated<IProps & ITr
347386
const target = anchors[anchors.length - 2] || anchors[0]
348387
if (!target) return
349388

350-
Velocity(document.body, 'finish')
351-
Velocity(document.body, 'scroll', {
352-
offset: window.scrollY - scrollMargin + target[0],
353-
duration: 200,
354-
easing: 'ease-out',
355-
})
389+
// Velocity(document.body, 'finish')
390+
// Velocity(document.body, 'scroll', {
391+
// offset: window.scrollY - scrollMargin + target[0],
392+
// duration: 200,
393+
// easing: 'ease-out',
394+
// })
395+
396+
const offsetTop = window.scrollY + target[0]
397+
this._lastAnimation?.stop()
398+
this._lastAnimation = animate(
399+
window,
400+
{
401+
scrollY: offsetTop + -1 * scrollMargin,
402+
},
403+
{
404+
duration: 0.4,
405+
ease: 'easeOut',
406+
}
407+
)
356408
}
357409
scrollToFollowing(): void {
358410
const scrollMargin = this.calculateScrollPosition()
@@ -361,12 +413,24 @@ export class PrompterViewContent extends React.Component<Translated<IProps & ITr
361413
const target = anchors[0]
362414
if (!target) return
363415

364-
Velocity(document.body, 'finish')
365-
Velocity(document.body, 'scroll', {
366-
offset: window.scrollY - scrollMargin + target[0],
367-
duration: 200,
368-
easing: 'ease-out',
369-
})
416+
// Velocity(document.body, 'finish')
417+
// Velocity(document.body, 'scroll', {
418+
// offset: window.scrollY - scrollMargin + target[0],
419+
// duration: 200,
420+
// easing: 'ease-out',
421+
// })
422+
const offsetTop = window.scrollY + target[0]
423+
this._lastAnimation?.stop()
424+
this._lastAnimation = animate(
425+
window,
426+
{
427+
scrollY: offsetTop + -1 * scrollMargin,
428+
},
429+
{
430+
duration: 0.4,
431+
ease: 'easeOut',
432+
}
433+
)
370434
}
371435
listAnchorPositions(startY: number, endY: number, sortDirection = 1): [number, Element][] {
372436
let foundPositions: [number, Element][] = []

0 commit comments

Comments
 (0)