Browse Source

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

Peng Xiao 3 years ago
parent
commit
f28df1d7c6

+ 1 - 10
tldraw/apps/tldraw-logseq/src/lib/tools/LogseqPortalTool/LogseqPortalTool.tsx

@@ -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])
   }
 }

+ 20 - 12
tldraw/packages/core/src/lib/TLViewport.ts

@@ -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)
+  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 })
+  }
+
+  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)
   }
 
-  zoomOut = (): this => {
+  zoomIn = () => {
+    const { camera } = this
+    this.setZoom(camera.zoom / ZOOM_UPDATE_FACTOR)
+  }
+
+  zoomOut = () => {
     const { camera, bounds } = this
-    const zoom: number = Math.max(TLViewport.minZoom, 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 })
+    this.setZoom(camera.zoom * ZOOM_UPDATE_FACTOR)
+
   }
 
   resetZoom = (): this => {

+ 1 - 10
tldraw/packages/core/src/lib/tools/TLMoveTool/states/PinchingState.ts

@@ -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'] = () => {

+ 29 - 9
tldraw/packages/core/src/lib/tools/TLSelectTool/states/PinchingState.ts

@@ -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]
+}