[fix] setCamera animates to constrained viewport (#3828)
Before this PR, calling `setCamera` with an `animation` option would calculate the target viewport based on the camera passed in without first applying constraints. That meant that if the camera did indeed need to be constrained you'd end up with some funky animations. ### Change Type <!-- ❗ Please select a 'Scope' label ❗️ --> - [x] `sdk` — Changes the tldraw SDK - [ ] `dotcom` — Changes the tldraw.com web app - [ ] `docs` — Changes to the documentation, examples, or templates. - [ ] `vs code` — Changes to the vscode plugin - [ ] `internal` — Does not affect user-facing stuff <!-- ❗ Please select a 'Type' label ❗️ --> - [x] `bugfix` — Bug fix - [ ] `feature` — New feature - [ ] `improvement` — Improving existing features - [ ] `chore` — Updating dependencies, other boring stuff - [ ] `galaxy brain` — Architectural changes - [ ] `tests` — Changes to any test code - [ ] `tools` — Changes to infrastructure, CI, internal scripts, debugging tools, etc. - [ ] `dunno` — I don't know ### Test Plan 1. Add a step-by-step description of how to test your PR here. 2. - [ ] Unit Tests - [ ] End to end tests ### Release Notes - Add a brief release note for your PR here.
This commit is contained in:
parent
e474f51745
commit
5f7032a553
2 changed files with 51 additions and 3 deletions
|
@ -2235,7 +2235,14 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
}
|
||||
|
||||
/** @internal */
|
||||
private _setCamera(point: VecLike, opts?: TLCameraMoveOptions): this {
|
||||
private getConstrainedCamera(
|
||||
point: VecLike,
|
||||
opts?: TLCameraMoveOptions
|
||||
): {
|
||||
x: number
|
||||
y: number
|
||||
z: number
|
||||
} {
|
||||
const currentCamera = this.getCamera()
|
||||
|
||||
let { x, y, z = currentCamera.z } = point
|
||||
|
@ -2391,6 +2398,15 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
}
|
||||
}
|
||||
|
||||
return { x, y, z }
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
private _setCamera(point: VecLike, opts?: TLCameraMoveOptions): this {
|
||||
const currentCamera = this.getCamera()
|
||||
|
||||
const { x, y, z } = this.getConstrainedCamera(point, opts)
|
||||
|
||||
if (currentCamera.x === x && currentCamera.y === y && currentCamera.z === z) {
|
||||
return this
|
||||
}
|
||||
|
@ -2472,14 +2488,20 @@ export class Editor extends EventEmitter<TLEventMap> {
|
|||
if (!Number.isFinite(_point.y)) _point.y = 0
|
||||
if (_point.z === undefined || !Number.isFinite(_point.z)) point.z = this.getZoomLevel()
|
||||
|
||||
const camera = this.getConstrainedCamera(_point, opts)
|
||||
|
||||
if (opts?.animation) {
|
||||
const { width, height } = this.getViewportScreenBounds()
|
||||
this._animateToViewport(
|
||||
new Box(-point.x, -point.y, width / _point.z, height / _point.z),
|
||||
new Box(-camera.x, -camera.y, width / camera.z, height / camera.z),
|
||||
opts
|
||||
)
|
||||
} else {
|
||||
this._setCamera(_point, opts)
|
||||
this._setCamera(camera, {
|
||||
...opts,
|
||||
// we already did the constraining, so we don't need to do it again
|
||||
force: true,
|
||||
})
|
||||
}
|
||||
|
||||
return this
|
||||
|
|
|
@ -757,3 +757,29 @@ describe('Outside behavior', () => {
|
|||
describe('Allows mixed values for x and y', () => {
|
||||
it.todo('Allows different values to be set for x and y axes')
|
||||
})
|
||||
|
||||
test('it animated towards the constrained viewport rather than the given viewport', () => {
|
||||
// @ts-expect-error
|
||||
const mockAnimateToViewport = (editor._animateToViewport = jest.fn())
|
||||
editor.setCameraOptions({
|
||||
...DEFAULT_CAMERA_OPTIONS,
|
||||
constraints: {
|
||||
...DEFAULT_CONSTRAINTS,
|
||||
behavior: 'contain',
|
||||
origin: { x: 0.5, y: 0.5 },
|
||||
padding: { x: 100, y: 100 },
|
||||
initialZoom: 'fit-max',
|
||||
},
|
||||
})
|
||||
|
||||
editor.setCamera(new Vec(-1000000, -1000000), { animation: { duration: 4000 } })
|
||||
expect(mockAnimateToViewport).toHaveBeenCalledTimes(1)
|
||||
expect(mockAnimateToViewport.mock.calls[0][0]).toMatchInlineSnapshot(`
|
||||
Box {
|
||||
"h": 900,
|
||||
"w": 1600,
|
||||
"x": -200,
|
||||
"y": -0,
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue