lerp viewports, make page switches instant always

pull/3695/head
David Sheldrick 2024-05-08 14:05:25 +01:00
rodzic 2a01720c8d
commit bfc3991091
1 zmienionych plików z 23 dodań i 12 usunięć

Wyświetl plik

@ -70,6 +70,7 @@ import {
getOwnProperty, getOwnProperty,
hasOwnProperty, hasOwnProperty,
last, last,
lerp,
sortById, sortById,
sortByIndex, sortByIndex,
structuredClone, structuredClone,
@ -3126,6 +3127,7 @@ export class Editor extends EventEmitter<TLEventMap> {
// Following // Following
// When we are 'locked on' to a user, our camera is derived from their camera.
private _isLockedOnFollowingUser = atom('isLockedOnFollowingUser', false) private _isLockedOnFollowingUser = atom('isLockedOnFollowingUser', false)
/** /**
@ -3158,6 +3160,7 @@ export class Editor extends EventEmitter<TLEventMap> {
if (!thisUserId) { if (!thisUserId) {
console.warn('You should set the userId for the current instance before following a user') console.warn('You should set the userId for the current instance before following a user')
// allow to continue since it's probably fine most of the time.
} }
// If the leader is following us, then we can't follow them // If the leader is following us, then we can't follow them
@ -3172,7 +3175,8 @@ export class Editor extends EventEmitter<TLEventMap> {
transact(() => { transact(() => {
this.updateInstanceState({ followingUserId: userId }) this.updateInstanceState({ followingUserId: userId })
const stopUpdatingPage = react('update current page', () => { // we listen for page changes separately from the 'moveTowardsUser' tick
const dispose = react('update current page', () => {
const leaderPresence = latestLeaderPresence.get() const leaderPresence = latestLeaderPresence.get()
if (!leaderPresence) { if (!leaderPresence) {
this.stopFollowingUser() this.stopFollowingUser()
@ -3182,17 +3186,19 @@ export class Editor extends EventEmitter<TLEventMap> {
leaderPresence.currentPageId !== this.getCurrentPageId() && leaderPresence.currentPageId !== this.getCurrentPageId() &&
this.getPage(leaderPresence.currentPageId) this.getPage(leaderPresence.currentPageId)
) { ) {
// if the page changed, switch page
this.history.ignore(() => { this.history.ignore(() => {
// sneaky store.put here, we can't go through setCurrentPage because it calls stopFollowingUser // sneaky store.put here, we can't go through setCurrentPage because it calls stopFollowingUser
this.store.put([ this.store.put([
{ ...this.getInstanceState(), currentPageId: leaderPresence.currentPageId }, { ...this.getInstanceState(), currentPageId: leaderPresence.currentPageId },
]) ])
this._isLockedOnFollowingUser.set(true)
}) })
} }
}) })
const cancel = () => { const cancel = () => {
stopUpdatingPage() dispose()
this._isLockedOnFollowingUser.set(false) this._isLockedOnFollowingUser.set(false)
this.off('frame', moveTowardsUser) this.off('frame', moveTowardsUser)
this.off('stop-following', cancel) this.off('stop-following', cancel)
@ -3235,22 +3241,27 @@ export class Editor extends EventEmitter<TLEventMap> {
return return
} }
const midpointViewport = new Box( // Chase the user's viewport!
(currentViewport.minX + targetViewport.minX) / 2, // Interpolate between the current viewport and the target viewport based on animation speed.
(currentViewport.minY + targetViewport.minY) / 2, // This will produce an 'ease-out' effect.
(currentViewport.width + targetViewport.width) / 2, const t = clamp(animationSpeed * 0.5, 0.1, 0.8)
(currentViewport.height + targetViewport.height) / 2
const nextViewport = new Box(
lerp(currentViewport.minX, targetViewport.minX, t),
lerp(currentViewport.minY, targetViewport.minY, t),
lerp(currentViewport.width, targetViewport.width, t),
lerp(currentViewport.height, targetViewport.height, t)
) )
const midpointCamera = new Vec( const nextCamera = new Vec(
-midpointViewport.x, -nextViewport.x,
-midpointViewport.y, -nextViewport.y,
this.getViewportScreenBounds().width / midpointViewport.width this.getViewportScreenBounds().width / nextViewport.width
) )
// Update the camera! // Update the camera!
this.stopCameraAnimation() this.stopCameraAnimation()
this._setCamera(midpointCamera) this._setCamera(nextCamera)
} }
this.once('stop-following', cancel) this.once('stop-following', cancel)