소스 검색

Improves performance for certain actions

main
Steve Ruiz 4 년 전
부모
커밋
704e92faa4
15개의 변경된 파일846개의 추가작업 그리고 530개의 파일을 삭제
  1. 9
    35
      components/canvas/canvas.tsx
  2. 40
    8
      components/canvas/page.tsx
  3. 1
    1
      components/canvas/selected.tsx
  4. 6
    6
      components/canvas/shape.tsx
  5. 53
    0
      hooks/useCanvasEvents.ts
  6. 1
    0
      hooks/useShapeEvents.ts
  7. 29
    9
      hooks/useZoomEvents.ts
  8. 1
    1
      package.json
  9. 94
    0
      state/hacks.ts
  10. 27
    0
      state/session.ts
  11. 2
    1
      state/sessions/brush-session.ts
  12. 66
    56
      state/state.ts
  13. 8
    9
      types.ts
  14. 4
    0
      utils/utils.ts
  15. 505
    404
      yarn.lock

+ 9
- 35
components/canvas/canvas.tsx 파일 보기

@@ -11,52 +11,26 @@ import Bounds from './bounds/bounding-box'
11 11
 import BoundsBg from './bounds/bounds-bg'
12 12
 import Selected from './selected'
13 13
 import Handles from './bounds/handles'
14
-import { isMobile, throttle } from 'utils/utils'
14
+import { isMobile, screenToWorld, throttle } from 'utils/utils'
15
+import session from 'state/session'
16
+import { PointerInfo } from 'types'
17
+import { fastDrawUpdate } from 'state/hacks'
18
+import useCanvasEvents from 'hooks/useCanvasEvents'
15 19
 
16 20
 export default function Canvas() {
17 21
   const rCanvas = useRef<SVGSVGElement>(null)
18 22
   const rGroup = useRef<SVGGElement>(null)
19 23
 
20 24
   useCamera(rGroup)
21
-  useZoomEvents()
22
-
23
-  const isReady = useSelector((s) => s.isIn('ready'))
24 25
 
25
-  const handlePointerDown = useCallback((e: React.PointerEvent) => {
26
-    if (!inputs.canAccept(e.pointerId)) return
27
-    rCanvas.current.setPointerCapture(e.pointerId)
28
-    state.send('POINTED_CANVAS', inputs.pointerDown(e, 'canvas'))
29
-  }, [])
30
-
31
-  const handleTouchStart = useCallback((e: React.TouchEvent) => {
32
-    if (e.touches.length === 2) {
33
-      state.send('TOUCH_UNDO')
34
-    } else {
35
-      if (isMobile()) {
36
-        state.send('TOUCHED_CANVAS')
37
-      }
38
-    }
39
-  }, [])
26
+  useZoomEvents()
40 27
 
41
-  const handlePointerMove = useCallback((e: React.PointerEvent) => {
42
-    if (!inputs.canAccept(e.pointerId)) return
43
-    throttledPointerMove(inputs.pointerMove(e))
44
-  }, [])
28
+  const events = useCanvasEvents(rCanvas)
45 29
 
46
-  const handlePointerUp = useCallback((e: React.PointerEvent) => {
47
-    if (!inputs.canAccept(e.pointerId)) return
48
-    rCanvas.current.releasePointerCapture(e.pointerId)
49
-    state.send('STOPPED_POINTING', { id: 'canvas', ...inputs.pointerUp(e) })
50
-  }, [])
30
+  const isReady = useSelector((s) => s.isIn('ready'))
51 31
 
52 32
   return (
53
-    <MainSVG
54
-      ref={rCanvas}
55
-      onPointerDown={handlePointerDown}
56
-      onPointerMove={handlePointerMove}
57
-      onPointerUp={handlePointerUp}
58
-      onTouchStart={handleTouchStart}
59
-    >
33
+    <MainSVG ref={rCanvas} {...events}>
60 34
       <Defs />
61 35
       {isReady && (
62 36
         <g ref={rGroup}>

+ 40
- 8
components/canvas/page.tsx 파일 보기

@@ -1,6 +1,8 @@
1
-import { useSelector } from 'state'
2
-import { GroupShape } from 'types'
3
-import { deepCompareArrays, getPage } from 'utils/utils'
1
+import { getShapeUtils } from 'lib/shape-utils'
2
+import state, { useSelector } from 'state'
3
+import { Bounds, GroupShape, PageState } from 'types'
4
+import { boundsContain } from 'utils/bounds'
5
+import { deepCompareArrays, getPage, screenToWorld } from 'utils/utils'
4 6
 import Shape from './shape'
5 7
 
6 8
 /* 
@@ -11,12 +13,42 @@ here; and still cheaper than any other pattern I've found.
11 13
 
12 14
 const noOffset = [0, 0]
13 15
 
16
+const viewportCache = new WeakMap<PageState, Bounds>()
17
+
14 18
 export default function Page() {
15
-  const currentPageShapeIds = useSelector(({ data }) => {
16
-    return Object.values(getPage(data).shapes)
17
-      .filter((shape) => shape.parentId === data.currentPageId)
18
-      .sort((a, b) => a.childIndex - b.childIndex)
19
-      .map((shape) => shape.id)
19
+  const currentPageShapeIds = useSelector((s) => {
20
+    const page = getPage(s.data)
21
+    const pageState = s.data.pageStates[page.id]
22
+
23
+    // if (!viewportCache.has(pageState)) {
24
+    //   const [minX, minY] = screenToWorld([0, 0], s.data)
25
+    //   const [maxX, maxY] = screenToWorld(
26
+    //     [window.innerWidth, window.innerHeight],
27
+    //     s.data
28
+    //   )
29
+    //   viewportCache.set(pageState, {
30
+    //     minX,
31
+    //     minY,
32
+    //     maxX,
33
+    //     maxY,
34
+    //     height: maxX - minX,
35
+    //     width: maxY - minY,
36
+    //   })
37
+    // }
38
+
39
+    // const viewport = viewportCache.get(pageState)
40
+
41
+    return (
42
+      Object.values(page.shapes)
43
+        .filter((shape) => shape.parentId === page.id)
44
+        // .filter((shape) => {
45
+        //   const shapeBounds = getShapeUtils(shape).getBounds(shape)
46
+        //   console.log(shapeBounds, viewport)
47
+        //   return boundsContain(viewport, shapeBounds)
48
+        // })
49
+        .sort((a, b) => a.childIndex - b.childIndex)
50
+        .map((shape) => shape.id)
51
+    )
20 52
   }, deepCompareArrays)
21 53
 
22 54
   const isSelecting = useSelector((s) => s.isIn('selecting'))

+ 1
- 1
components/canvas/selected.tsx 파일 보기

@@ -12,7 +12,7 @@ export default function Selected() {
12 12
     return Array.from(data.selectedIds.values())
13 13
   }, deepCompareArrays)
14 14
 
15
-  const isSelecting = useSelector((s) => s.isInAny('notPointing', 'pinching'))
15
+  const isSelecting = useSelector((s) => s.isIn('selecting'))
16 16
 
17 17
   if (!isSelecting) return null
18 18
 

+ 6
- 6
components/canvas/shape.tsx 파일 보기

@@ -49,7 +49,7 @@ function Shape({ id, isSelecting, parentPoint }: ShapeProps) {
49 49
           {...events}
50 50
         />
51 51
       )}
52
-      {!shape.isHidden && <ReadShape isGroup={isGroup} id={id} style={style} />}
52
+      {!shape.isHidden && <RealShape isGroup={isGroup} id={id} style={style} />}
53 53
       {isGroup &&
54 54
         shape.children.map((shapeId) => (
55 55
           <Shape
@@ -69,7 +69,7 @@ interface RealShapeProps {
69 69
   style: Partial<React.SVGProps<SVGUseElement>>
70 70
 }
71 71
 
72
-const ReadShape = memo(function RealShape({
72
+const RealShape = memo(function RealShape({
73 73
   isGroup,
74 74
   id,
75 75
   style,
@@ -157,10 +157,10 @@ function Label({ children }: { children: React.ReactNode }) {
157 157
   )
158 158
 }
159 159
 
160
-export { HoverIndicator }
161
-
162
-export default memo(Shape)
163
-
164 160
 function pp(n: number[]) {
165 161
   return '[' + n.map((v) => v.toFixed(1)).join(', ') + ']'
166 162
 }
163
+
164
+export { HoverIndicator }
165
+
166
+export default memo(Shape)

+ 53
- 0
hooks/useCanvasEvents.ts 파일 보기

@@ -0,0 +1,53 @@
1
+import { MutableRefObject, useCallback } from 'react'
2
+import state from 'state'
3
+import { fastBrushSelect, fastDrawUpdate } from 'state/hacks'
4
+import inputs from 'state/inputs'
5
+import { isMobile } from 'utils/utils'
6
+
7
+export default function useCanvasEvents(
8
+  rCanvas: MutableRefObject<SVGGElement>
9
+) {
10
+  const handlePointerDown = useCallback((e: React.PointerEvent) => {
11
+    if (!inputs.canAccept(e.pointerId)) return
12
+    rCanvas.current.setPointerCapture(e.pointerId)
13
+    state.send('POINTED_CANVAS', inputs.pointerDown(e, 'canvas'))
14
+  }, [])
15
+
16
+  const handleTouchStart = useCallback((e: React.TouchEvent) => {
17
+    if (isMobile()) {
18
+      if (e.touches.length === 2) {
19
+        state.send('TOUCH_UNDO')
20
+      } else state.send('TOUCHED_CANVAS')
21
+    }
22
+  }, [])
23
+
24
+  const handlePointerMove = useCallback((e: React.PointerEvent) => {
25
+    if (!inputs.canAccept(e.pointerId)) return
26
+
27
+    if (state.isIn('draw.editing')) {
28
+      fastDrawUpdate(inputs.pointerMove(e))
29
+      return
30
+    }
31
+
32
+    if (state.isIn('brushSelecting')) {
33
+      const info = inputs.pointerMove(e)
34
+      fastBrushSelect(info.point)
35
+      return
36
+    }
37
+
38
+    state.send('MOVED_POINTER', inputs.pointerMove(e))
39
+  }, [])
40
+
41
+  const handlePointerUp = useCallback((e: React.PointerEvent) => {
42
+    if (!inputs.canAccept(e.pointerId)) return
43
+    rCanvas.current.releasePointerCapture(e.pointerId)
44
+    state.send('STOPPED_POINTING', { id: 'canvas', ...inputs.pointerUp(e) })
45
+  }, [])
46
+
47
+  return {
48
+    onPointerDown: handlePointerDown,
49
+    onTouchStart: handleTouchStart,
50
+    onPointerMove: handlePointerMove,
51
+    onPointerUp: handlePointerUp,
52
+  }
53
+}

+ 1
- 0
hooks/useShapeEvents.ts 파일 보기

@@ -47,6 +47,7 @@ export default function useShapeEvents(
47 47
   const handlePointerMove = useCallback(
48 48
     (e: React.PointerEvent) => {
49 49
       if (!inputs.canAccept(e.pointerId)) return
50
+
50 51
       if (isGroup) {
51 52
         state.send('MOVED_OVER_GROUP', inputs.pointerEnter(e, id))
52 53
       } else {

+ 29
- 9
hooks/useZoomEvents.ts 파일 보기

@@ -3,6 +3,12 @@ import state from 'state'
3 3
 import inputs from 'state/inputs'
4 4
 import * as vec from 'utils/vec'
5 5
 import { useGesture } from 'react-use-gesture'
6
+import {
7
+  fastBrushSelect,
8
+  fastPanUpdate,
9
+  fastPinchCamera,
10
+  fastZoomUpdate,
11
+} from 'state/hacks'
6 12
 
7 13
 /**
8 14
  * Capture zoom gestures (pinches, wheels and pans) and send to the state.
@@ -17,10 +23,17 @@ export default function useZoomEvents() {
17 23
     {
18 24
       onWheel: ({ event, delta }) => {
19 25
         if (event.ctrlKey) {
20
-          state.send('ZOOMED_CAMERA', {
21
-            delta: delta[1],
22
-            ...inputs.wheel(event as WheelEvent),
23
-          })
26
+          const { point } = inputs.wheel(event as WheelEvent)
27
+          fastZoomUpdate(point, delta[1])
28
+          // state.send('ZOOMED_CAMERA', {
29
+          //   delta: delta[1],
30
+          //   ...inputs.wheel(event as WheelEvent),
31
+          // })
32
+          return
33
+        }
34
+
35
+        if (state.isIn('pointing')) {
36
+          fastPanUpdate(delta)
24 37
           return
25 38
         }
26 39
 
@@ -45,12 +58,19 @@ export default function useZoomEvents() {
45 58
 
46 59
         const [distanceDelta, angleDelta] = vec.sub(rPinchDa.current, da)
47 60
 
48
-        state.send('PINCHED', {
49
-          delta: vec.sub(rPinchPoint.current, origin),
50
-          point: origin,
61
+        fastPinchCamera(
62
+          origin,
63
+          vec.sub(rPinchPoint.current, origin),
51 64
           distanceDelta,
52
-          angleDelta,
53
-        })
65
+          angleDelta
66
+        )
67
+
68
+        // state.send('PINCHED', {
69
+        //   delta: vec.sub(rPinchPoint.current, origin),
70
+        //   point: origin,
71
+        //   distanceDelta,
72
+        //   angleDelta,
73
+        // })
54 74
 
55 75
         rPinchDa.current = da
56 76
         rPinchPoint.current = origin

+ 1
- 1
package.json 파일 보기

@@ -16,7 +16,7 @@
16 16
     "@radix-ui/react-icons": "^1.0.3",
17 17
     "@radix-ui/react-radio-group": "^0.0.16",
18 18
     "@radix-ui/react-tooltip": "^0.0.18",
19
-    "@state-designer/react": "^1.7.1",
19
+    "@state-designer/react": "^1.7.3",
20 20
     "@stitches/react": "^0.1.9",
21 21
     "framer-motion": "^4.1.16",
22 22
     "ismobilejs": "^1.1.1",

+ 94
- 0
state/hacks.ts 파일 보기

@@ -0,0 +1,94 @@
1
+import { PointerInfo } from 'types'
2
+import {
3
+  getCameraZoom,
4
+  getCurrentCamera,
5
+  screenToWorld,
6
+  setZoomCSS,
7
+} from 'utils/utils'
8
+import session from './session'
9
+import state from './state'
10
+import * as vec from 'utils/vec'
11
+
12
+/**
13
+ * While a user is drawing with the draw tool, we want to update the shape without
14
+ * going through the trouble of updating the entire state machine. Speciifcally, we
15
+ * do not want to push the change through immer. Instead, we'll push the change
16
+ * directly to the state using `forceData`.
17
+ * @param info
18
+ */
19
+export function fastDrawUpdate(info: PointerInfo) {
20
+  const data = { ...state.data }
21
+
22
+  session.current.update(
23
+    data,
24
+    screenToWorld(info.point, data),
25
+    info.pressure,
26
+    info.shiftKey
27
+  )
28
+
29
+  const selectedId = Array.from(data.selectedIds.values())[0]
30
+
31
+  const shape = data.document.pages[data.currentPageId].shapes[selectedId]
32
+
33
+  data.document.pages[data.currentPageId].shapes[selectedId] = { ...shape }
34
+
35
+  state.forceData(Object.freeze(data))
36
+}
37
+
38
+export function fastPanUpdate(delta: number[]) {
39
+  const data = { ...state.data }
40
+  const camera = getCurrentCamera(data)
41
+  camera.point = vec.sub(camera.point, vec.div(delta, camera.zoom))
42
+
43
+  data.pageStates[data.currentPageId].camera = { ...camera }
44
+
45
+  state.forceData(Object.freeze(data))
46
+}
47
+
48
+export function fastZoomUpdate(point: number[], delta: number) {
49
+  const data = { ...state.data }
50
+  const camera = getCurrentCamera(data)
51
+
52
+  const next = camera.zoom - (delta / 100) * camera.zoom
53
+
54
+  const p0 = screenToWorld(point, data)
55
+  camera.zoom = getCameraZoom(next)
56
+  const p1 = screenToWorld(point, data)
57
+  camera.point = vec.add(camera.point, vec.sub(p1, p0))
58
+
59
+  data.pageStates[data.currentPageId].camera = { ...camera }
60
+
61
+  state.forceData(Object.freeze(data))
62
+}
63
+
64
+export function fastPinchCamera(
65
+  point: number[],
66
+  delta: number[],
67
+  distanceDelta: number,
68
+  angleDelta: number
69
+) {
70
+  const data = { ...state.data }
71
+  const camera = getCurrentCamera(data)
72
+
73
+  camera.point = vec.sub(camera.point, vec.div(delta, camera.zoom))
74
+
75
+  const next = camera.zoom - (distanceDelta / 300) * camera.zoom
76
+
77
+  const p0 = screenToWorld(point, data)
78
+  camera.zoom = getCameraZoom(next)
79
+  const p1 = screenToWorld(point, data)
80
+  camera.point = vec.add(camera.point, vec.sub(p1, p0))
81
+
82
+  data.pageStates[data.currentPageId].camera = { ...camera }
83
+
84
+  state.forceData(Object.freeze(data))
85
+}
86
+
87
+export function fastBrushSelect(point: number[]) {
88
+  const data = { ...state.data }
89
+  session.current.update(data, screenToWorld(point, data))
90
+
91
+  data.selectedIds = new Set(data.selectedIds)
92
+
93
+  state.forceData(Object.freeze(data))
94
+}

+ 27
- 0
state/session.ts 파일 보기

@@ -0,0 +1,27 @@
1
+import { BaseSession } from './sessions'
2
+
3
+class SessionManager {
4
+  private _current?: BaseSession
5
+
6
+  clear() {
7
+    this._current = undefined
8
+    return this
9
+  }
10
+
11
+  setCurrent(session: BaseSession) {
12
+    this._current = session
13
+    return this
14
+  }
15
+
16
+  get current() {
17
+    return this._current
18
+  }
19
+
20
+  set current(session: BaseSession) {
21
+    this._current = session
22
+  }
23
+}
24
+
25
+const session = new SessionManager()
26
+
27
+export default session

+ 2
- 1
state/sessions/brush-session.ts 파일 보기

@@ -4,6 +4,7 @@ import BaseSession from './base-session'
4 4
 import { getShapeUtils } from 'lib/shape-utils'
5 5
 import { getBoundsFromPoints, getPage, getShapes } from 'utils/utils'
6 6
 import * as vec from 'utils/vec'
7
+import state from 'state/state'
7 8
 
8 9
 export default class BrushSession extends BaseSession {
9 10
   origin: number[]
@@ -62,7 +63,7 @@ export function getBrushSnapshot(data: Data) {
62 63
   return {
63 64
     selectedIds: new Set(data.selectedIds),
64 65
     shapeHitTests: Object.fromEntries(
65
-      getShapes(current(data))
66
+      getShapes(state.data)
66 67
         .filter((shape) => shape.type !== ShapeType.Group)
67 68
         .map((shape) => {
68 69
           return [

+ 66
- 56
state/state.ts 파일 보기

@@ -25,6 +25,7 @@ import {
25 25
   rotateBounds,
26 26
   getBoundsCenter,
27 27
   getDocumentBranch,
28
+  getCameraZoom,
28 29
 } from 'utils/utils'
29 30
 import {
30 31
   Data,
@@ -43,6 +44,7 @@ import {
43 44
   SizeStyle,
44 45
   ColorStyle,
45 46
 } from 'types'
47
+import session from './session'
46 48
 
47 49
 const initialData: Data = {
48 50
   isReadOnly: false,
@@ -324,28 +326,31 @@ const state = createState({
324 326
               ],
325 327
               on: {
326 328
                 STARTED_PINCHING: { do: 'completeSession', to: 'pinching' },
327
-                MOVED_POINTER: 'updateBrushSession',
329
+                // Currently using hacks.fastBrushSelect
330
+                // MOVED_POINTER: 'updateBrushSession',
328 331
                 PANNED_CAMERA: 'updateBrushSession',
329 332
                 STOPPED_POINTING: { do: 'completeSession', to: 'selecting' },
330 333
                 CANCELLED: { do: 'cancelSession', to: 'selecting' },
331 334
               },
332 335
             },
333
-            pinching: {
336
+          },
337
+        },
338
+        pinching: {
339
+          on: {
340
+            // Pinching uses hacks.fastPinchCamera
341
+            // PINCHED: { do: 'pinchCamera' },
342
+          },
343
+          initial: 'selectPinching',
344
+          onExit: { secretlyDo: 'updateZoomCSS' },
345
+          states: {
346
+            selectPinching: {
334 347
               on: {
335
-                PINCHED: { do: 'pinchCamera' },
348
+                STOPPED_PINCHING: { to: 'selecting' },
336 349
               },
337
-              initial: 'selectPinching',
338
-              states: {
339
-                selectPinching: {
340
-                  on: {
341
-                    STOPPED_PINCHING: { to: 'selecting' },
342
-                  },
343
-                },
344
-                toolPinching: {
345
-                  on: {
346
-                    STOPPED_PINCHING: { to: 'usingTool.previous' },
347
-                  },
348
-                },
350
+            },
351
+            toolPinching: {
352
+              on: {
353
+                STOPPED_PINCHING: { to: 'usingTool.previous' },
349 354
               },
350 355
             },
351 356
           },
@@ -385,17 +390,17 @@ const state = createState({
385 390
                 editing: {
386 391
                   onEnter: 'startDrawSession',
387 392
                   on: {
388
-                    STOPPED_POINTING: {
389
-                      do: 'completeSession',
390
-                      to: 'draw.creating',
391
-                    },
392 393
                     CANCELLED: {
393 394
                       do: 'breakSession',
394 395
                       to: 'selecting',
395 396
                     },
397
+                    STOPPED_POINTING: {
398
+                      do: 'completeSession',
399
+                      to: 'draw.creating',
400
+                    },
396 401
                     PRESSED_SHIFT: 'keyUpdateDrawSession',
397 402
                     RELEASED_SHIFT: 'keyUpdateDrawSession',
398
-                    MOVED_POINTER: 'updateDrawSession',
403
+                    // MOVED_POINTER: 'updateDrawSession',
399 404
                     PANNED_CAMERA: 'updateDrawSession',
400 405
                   },
401 406
                 },
@@ -816,53 +821,57 @@ const state = createState({
816 821
 
817 822
     // Shared
818 823
     breakSession(data) {
819
-      session?.cancel(data)
820
-      session = undefined
824
+      session.current?.cancel(data)
825
+      session.clear()
821 826
       history.disable()
822 827
       commands.deleteSelected(data)
823 828
       history.enable()
824 829
     },
825 830
     cancelSession(data) {
826
-      session?.cancel(data)
827
-      session = undefined
831
+      session.current?.cancel(data)
832
+      session.clear()
828 833
     },
829 834
     completeSession(data) {
830
-      session?.complete(data)
831
-      session = undefined
835
+      session.current?.complete(data)
836
+      session.clear()
832 837
     },
833 838
 
834 839
     // Brushing
835 840
     startBrushSession(data, payload: PointerInfo) {
836
-      session = new Sessions.BrushSession(
841
+      session.current = new Sessions.BrushSession(
837 842
         data,
838 843
         screenToWorld(payload.point, data)
839 844
       )
840 845
     },
841 846
     updateBrushSession(data, payload: PointerInfo) {
842
-      session.update(data, screenToWorld(payload.point, data))
847
+      session.current.update(data, screenToWorld(payload.point, data))
843 848
     },
844 849
 
845 850
     // Rotating
846 851
     startRotateSession(data, payload: PointerInfo) {
847
-      session = new Sessions.RotateSession(
852
+      session.current = new Sessions.RotateSession(
848 853
         data,
849 854
         screenToWorld(payload.point, data)
850 855
       )
851 856
     },
852 857
     keyUpdateRotateSession(data, payload: PointerInfo) {
853
-      session.update(
858
+      session.current.update(
854 859
         data,
855 860
         screenToWorld(inputs.pointer.point, data),
856 861
         payload.shiftKey
857 862
       )
858 863
     },
859 864
     updateRotateSession(data, payload: PointerInfo) {
860
-      session.update(data, screenToWorld(payload.point, data), payload.shiftKey)
865
+      session.current.update(
866
+        data,
867
+        screenToWorld(payload.point, data),
868
+        payload.shiftKey
869
+      )
861 870
     },
862 871
 
863 872
     // Dragging / Translating
864 873
     startTranslateSession(data) {
865
-      session = new Sessions.TranslateSession(
874
+      session.current = new Sessions.TranslateSession(
866 875
         data,
867 876
         screenToWorld(inputs.pointer.origin, data)
868 877
       )
@@ -871,7 +880,7 @@ const state = createState({
871 880
       data,
872 881
       payload: { shiftKey: boolean; altKey: boolean }
873 882
     ) {
874
-      session.update(
883
+      session.current.update(
875 884
         data,
876 885
         screenToWorld(inputs.pointer.point, data),
877 886
         payload.shiftKey,
@@ -879,7 +888,7 @@ const state = createState({
879 888
       )
880 889
     },
881 890
     updateTranslateSession(data, payload: PointerInfo) {
882
-      session.update(
891
+      session.current.update(
883 892
         data,
884 893
         screenToWorld(payload.point, data),
885 894
         payload.shiftKey,
@@ -892,7 +901,7 @@ const state = createState({
892 901
       const shapeId = Array.from(data.selectedIds.values())[0]
893 902
       const handleId = payload.target
894 903
 
895
-      session = new Sessions.HandleSession(
904
+      session.current = new Sessions.HandleSession(
896 905
         data,
897 906
         shapeId,
898 907
         handleId,
@@ -903,7 +912,7 @@ const state = createState({
903 912
       data,
904 913
       payload: { shiftKey: boolean; altKey: boolean }
905 914
     ) {
906
-      session.update(
915
+      session.current.update(
907 916
         data,
908 917
         screenToWorld(inputs.pointer.point, data),
909 918
         payload.shiftKey,
@@ -911,7 +920,7 @@ const state = createState({
911 920
       )
912 921
     },
913 922
     updateHandleSession(data, payload: PointerInfo) {
914
-      session.update(
923
+      session.current.update(
915 924
         data,
916 925
         screenToWorld(payload.point, data),
917 926
         payload.shiftKey,
@@ -925,14 +934,13 @@ const state = createState({
925 934
       payload: PointerInfo & { target: Corner | Edge }
926 935
     ) {
927 936
       const point = screenToWorld(inputs.pointer.origin, data)
928
-      session = new Sessions.TransformSession(data, payload.target, point)
929
-      session =
937
+      session.current =
930 938
         data.selectedIds.size === 1
931 939
           ? new Sessions.TransformSingleSession(data, payload.target, point)
932 940
           : new Sessions.TransformSession(data, payload.target, point)
933 941
     },
934 942
     startDrawTransformSession(data, payload: PointerInfo) {
935
-      session = new Sessions.TransformSingleSession(
943
+      session.current = new Sessions.TransformSingleSession(
936 944
         data,
937 945
         Corner.BottomRight,
938 946
         screenToWorld(payload.point, data),
@@ -940,7 +948,7 @@ const state = createState({
940 948
       )
941 949
     },
942 950
     keyUpdateTransformSession(data, payload: PointerInfo) {
943
-      session.update(
951
+      session.current.update(
944 952
         data,
945 953
         screenToWorld(inputs.pointer.point, data),
946 954
         payload.shiftKey,
@@ -948,7 +956,7 @@ const state = createState({
948 956
       )
949 957
     },
950 958
     updateTransformSession(data, payload: PointerInfo) {
951
-      session.update(
959
+      session.current.update(
952 960
         data,
953 961
         screenToWorld(payload.point, data),
954 962
         payload.shiftKey,
@@ -958,19 +966,19 @@ const state = createState({
958 966
 
959 967
     // Direction
960 968
     startDirectionSession(data, payload: PointerInfo) {
961
-      session = new Sessions.DirectionSession(
969
+      session.current = new Sessions.DirectionSession(
962 970
         data,
963 971
         screenToWorld(inputs.pointer.origin, data)
964 972
       )
965 973
     },
966 974
     updateDirectionSession(data, payload: PointerInfo) {
967
-      session.update(data, screenToWorld(payload.point, data))
975
+      session.current.update(data, screenToWorld(payload.point, data))
968 976
     },
969 977
 
970 978
     // Drawing
971 979
     startDrawSession(data, payload: PointerInfo) {
972 980
       const id = Array.from(data.selectedIds.values())[0]
973
-      session = new Sessions.DrawSession(
981
+      session.current = new Sessions.DrawSession(
974 982
         data,
975 983
         id,
976 984
         screenToWorld(inputs.pointer.origin, data),
@@ -978,7 +986,7 @@ const state = createState({
978 986
       )
979 987
     },
980 988
     keyUpdateDrawSession(data, payload: PointerInfo) {
981
-      session.update(
989
+      session.current.update(
982 990
         data,
983 991
         screenToWorld(inputs.pointer.point, data),
984 992
         payload.pressure,
@@ -986,7 +994,7 @@ const state = createState({
986 994
       )
987 995
     },
988 996
     updateDrawSession(data, payload: PointerInfo) {
989
-      session.update(
997
+      session.current.update(
990 998
         data,
991 999
         screenToWorld(payload.point, data),
992 1000
         payload.pressure,
@@ -997,7 +1005,7 @@ const state = createState({
997 1005
     // Arrow
998 1006
     startArrowSession(data, payload: PointerInfo) {
999 1007
       const id = Array.from(data.selectedIds.values())[0]
1000
-      session = new Sessions.ArrowSession(
1008
+      session.current = new Sessions.ArrowSession(
1001 1009
         data,
1002 1010
         id,
1003 1011
         screenToWorld(inputs.pointer.origin, data),
@@ -1005,14 +1013,18 @@ const state = createState({
1005 1013
       )
1006 1014
     },
1007 1015
     keyUpdateArrowSession(data, payload: PointerInfo) {
1008
-      session.update(
1016
+      session.current.update(
1009 1017
         data,
1010 1018
         screenToWorld(inputs.pointer.point, data),
1011 1019
         payload.shiftKey
1012 1020
       )
1013 1021
     },
1014 1022
     updateArrowSession(data, payload: PointerInfo) {
1015
-      session.update(data, screenToWorld(payload.point, data), payload.shiftKey)
1023
+      session.current.update(
1024
+        data,
1025
+        screenToWorld(payload.point, data),
1026
+        payload.shiftKey
1027
+      )
1016 1028
     },
1017 1029
 
1018 1030
     // Nudges
@@ -1257,6 +1269,10 @@ const state = createState({
1257 1269
       const camera = getCurrentCamera(data)
1258 1270
       camera.point = vec.sub(camera.point, vec.div(payload.delta, camera.zoom))
1259 1271
     },
1272
+    updateZoomCSS(data) {
1273
+      const camera = getCurrentCamera(data)
1274
+      setZoomCSS(camera.zoom)
1275
+    },
1260 1276
     pinchCamera(
1261 1277
       data,
1262 1278
       payload: {
@@ -1509,16 +1525,10 @@ const state = createState({
1509 1525
   },
1510 1526
 })
1511 1527
 
1512
-let session: Sessions.BaseSession
1513
-
1514 1528
 export default state
1515 1529
 
1516 1530
 export const useSelector = createSelectorHook(state)
1517 1531
 
1518
-function getCameraZoom(zoom: number) {
1519
-  return clamp(zoom, 0.1, 5)
1520
-}
1521
-
1522 1532
 function getParentId(data: Data, id: string) {
1523 1533
   const shape = getPage(data).shapes[id]
1524 1534
   return shape.parentId

+ 8
- 9
types.ts 파일 보기

@@ -33,15 +33,7 @@ export interface Data {
33 33
     pages: Record<string, Page>
34 34
     code: Record<string, CodeFile>
35 35
   }
36
-  pageStates: Record<
37
-    string,
38
-    {
39
-      camera: {
40
-        point: number[]
41
-        zoom: number
42
-      }
43
-    }
44
-  >
36
+  pageStates: Record<string, PageState>
45 37
 }
46 38
 
47 39
 /* -------------------------------------------------- */
@@ -56,6 +48,13 @@ export interface Page {
56 48
   shapes: Record<string, Shape>
57 49
 }
58 50
 
51
+export interface PageState {
52
+  camera: {
53
+    point: number[]
54
+    zoom: number
55
+  }
56
+}
57
+
59 58
 export enum ShapeType {
60 59
   Dot = 'dot',
61 60
   Circle = 'circle',

+ 4
- 0
utils/utils.ts 파일 보기

@@ -834,6 +834,10 @@ export function throttle<P extends any[], T extends (...args: P) => any>(
834 834
   }
835 835
 }
836 836
 
837
+export function getCameraZoom(zoom: number) {
838
+  return clamp(zoom, 0.1, 5)
839
+}
840
+
837 841
 export function pointInRect(
838 842
   point: number[],
839 843
   minX: number,

+ 505
- 404
yarn.lock
파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
파일 보기


Loading…
취소
저장