Merge pull request #6602 from logseq/fix/whiteboards-scroll-zoom

This commit is contained in:
Peng Xiao
2022-09-05 16:48:38 +08:00
committed by GitHub
4 changed files with 52 additions and 42 deletions

View File

@@ -1,6 +1,5 @@
import { TLApp, TLEvents, TLTool } from '@tldraw/core'
import type { TLReactEventMap } from '@tldraw/react'
import Vec from '@tldraw/vec'
import { type Shape, LogseqPortalShape } from '../../shapes'
import { CreatingState, IdleState } from './states'
@@ -16,15 +15,7 @@ export class LogseqPortalTool extends TLTool<
Shape = LogseqPortalShape
private pinchCamera(point: number[], delta: number[], zoom: number) {
const { camera } = this.app.viewport
const nextPoint = Vec.sub(camera.point, Vec.div(delta, camera.zoom))
const p0 = Vec.sub(Vec.div(point, camera.zoom), nextPoint)
const p1 = Vec.sub(Vec.div(point, zoom), nextPoint)
this.app.setCamera(Vec.toFixed(Vec.add(nextPoint, Vec.sub(p1, p0))), zoom)
}
onPinch: TLEvents<Shape>['pinch'] = info => {
this.pinchCamera(info.point, [0, 0], info.offset[0])
this.app.viewport.pinchCamera(info.point, [0, 0], info.offset[0])
}
}

View File

@@ -82,22 +82,30 @@ export class TLViewport {
return Vec.mul(Vec.add(point, camera.point), camera.zoom)
}
zoomIn = (): this => {
const { camera, bounds } = this
const zoom: number = Math.min(TLViewport.maxZoom, camera.zoom / ZOOM_UPDATE_FACTOR)
const center = [bounds.width / 2, bounds.height / 2]
const p0 = Vec.sub(Vec.div(center, camera.zoom), center)
const p1 = Vec.sub(Vec.div(center, zoom), center)
return this.update({ point: Vec.toFixed(Vec.add(camera.point, Vec.sub(p1, p0))), zoom })
pinchCamera = (point: number[], delta: number[], zoom: number): this => {
const { camera } = this
zoom = Math.max(TLViewport.minZoom, Math.min(TLViewport.maxZoom, zoom));
const nextPoint = Vec.sub(camera.point, Vec.div(delta, camera.zoom))
const p0 = Vec.sub(Vec.div(point, camera.zoom), nextPoint)
const p1 = Vec.sub(Vec.div(point, zoom), nextPoint)
return this.update({ point: Vec.toFixed(Vec.add(nextPoint, Vec.sub(p1, p0))), zoom })
}
zoomOut = (): this => {
const { camera, bounds } = this
const zoom: number = Math.max(TLViewport.minZoom, camera.zoom * ZOOM_UPDATE_FACTOR)
setZoom = (zoom: number) => {
const { bounds } = this
const center = [bounds.width / 2, bounds.height / 2]
const p0 = Vec.sub(Vec.div(center, camera.zoom), center)
const p1 = Vec.sub(Vec.div(center, zoom), center)
return this.update({ point: Vec.toFixed(Vec.add(camera.point, Vec.sub(p1, p0))), zoom })
this.pinchCamera(center, [0, 0], zoom)
}
zoomIn = () => {
const { camera } = this
this.setZoom(camera.zoom / ZOOM_UPDATE_FACTOR)
}
zoomOut = () => {
const { camera, bounds } = this
this.setZoom(camera.zoom * ZOOM_UPDATE_FACTOR)
}
resetZoom = (): this => {

View File

@@ -1,4 +1,3 @@
import { Vec } from '@tldraw/vec'
import type { TLEventMap, TLEventInfo, TLEvents } from '../../../../types'
import type { TLShape } from '../../../shapes'
import type { TLApp } from '../../../TLApp'
@@ -26,21 +25,13 @@ export class PinchingState<
private prevDelta: number[] = [0, 0]
private pinchCamera(point: number[], delta: number[], zoom: number) {
const { camera } = this.app.viewport
const nextPoint = Vec.sub(camera.point, Vec.div(delta, camera.zoom))
const p0 = Vec.sub(Vec.div(point, camera.zoom), nextPoint)
const p1 = Vec.sub(Vec.div(point, zoom), nextPoint)
this.app.setCamera(Vec.toFixed(Vec.add(nextPoint, Vec.sub(p1, p0))), zoom)
}
onEnter = (info: GestureInfo<S, K>) => {
this.prevDelta = info.info.delta
this.origin = info.info.point
}
onPinch: TLEvents<S>['pinch'] = info => {
this.pinchCamera(info.point, [0, 0], info.offset[0])
this.app.viewport.pinchCamera(info.point, [0, 0], info.offset[0])
}
onPinchEnd: TLEvents<S>['pinch'] = () => {

View File

@@ -1,4 +1,3 @@
import { Vec } from '@tldraw/vec'
import type { TLEventMap, TLEventInfo, TLEvents } from '../../../../types'
import type { TLShape } from '../../../shapes'
import type { TLApp } from '../../../TLApp'
@@ -22,16 +21,14 @@ export class PinchingState<
> extends TLToolState<S, K, R, P> {
static id = 'pinching'
private pinchCamera(point: number[], delta: number[], zoom: number) {
onPinch: TLEvents<S>['pinch'] = (info, event: any) => {
const { camera } = this.app.viewport
const nextPoint = Vec.sub(camera.point, Vec.div(delta, camera.zoom))
const p0 = Vec.sub(Vec.div(point, camera.zoom), nextPoint)
const p1 = Vec.sub(Vec.div(point, zoom), nextPoint)
this.app.setCamera(Vec.toFixed(Vec.add(nextPoint, Vec.sub(p1, p0))), zoom)
}
onPinch: TLEvents<S>['pinch'] = info => {
this.pinchCamera(info.point, [0, 0], info.offset[0])
// Normalize the value of deltaZ from raw WheelEvent
const deltaZ = normalizeWheel(event)[2] * 0.01
if (deltaZ === 0) return;
const zoom = camera.zoom - deltaZ * camera.zoom;
this.app.viewport.pinchCamera(info.point, [0, 0], zoom)
}
onPinchEnd: TLEvents<S>['pinch'] = () => {
@@ -42,3 +39,26 @@ export class PinchingState<
this.tool.transition('idle')
}
}
// Adapted from https://stackoverflow.com/a/13650579
function normalizeWheel(event: WheelEvent) {
const MAX_ZOOM_STEP = 10
const { deltaY, deltaX } = event
let deltaZ = 0
if (event.ctrlKey || event.metaKey) {
const signY = Math.sign(event.deltaY)
const absDeltaY = Math.abs(event.deltaY)
let dy = deltaY
if (absDeltaY > MAX_ZOOM_STEP) {
dy = MAX_ZOOM_STEP * signY
}
deltaZ = dy
}
return [deltaX, deltaY, deltaZ]
}