瀏覽代碼

[fix] rotate center (#213)

* fixes rotate center after translating / transforming

* Adds test, fixes issue on undo/redo

* Update tsconfig.base.json
main
Steve Ruiz 3 年之前
父節點
當前提交
12e425ddc4
沒有連結到貢獻者的電子郵件帳戶。

+ 1
- 1
packages/tldraw/package.json 查看文件

@@ -66,7 +66,7 @@
66 66
     "@radix-ui/react-tooltip": "^0.1.1",
67 67
     "@stitches/core": "^1.2.5",
68 68
     "@stitches/react": "^1.0.0",
69
-    "@tldraw/core": "^0.1.9",
69
+    "@tldraw/core": "^0.1.10",
70 70
     "@tldraw/intersect": "^0.0.132",
71 71
     "@tldraw/vec": "^0.0.132",
72 72
     "perfect-freehand": "^1.0.16",

+ 4
- 1
packages/tldraw/src/state/command/translate/translate.command.ts 查看文件

@@ -1,10 +1,13 @@
1 1
 import { Vec } from '@tldraw/vec'
2
-import type { Data, TLDrawCommand, PagePartial } from '~types'
2
+import { Data, TLDrawCommand, PagePartial, Session } from '~types'
3 3
 import { TLDR } from '~state/tldr'
4 4
 
5 5
 export function translate(data: Data, ids: string[], delta: number[]): TLDrawCommand {
6 6
   const { currentPageId } = data.appState
7 7
 
8
+  // Clear session cache
9
+  Session.cache.selectedIds = TLDR.getSelectedIds(data, data.appState.currentPageId)
10
+
8 11
   const before: PagePartial = {
9 12
     shapes: {},
10 13
     bindings: {},

+ 51
- 3
packages/tldraw/src/state/session/sessions/rotate/rotate.session.spec.ts 查看文件

@@ -119,16 +119,64 @@ describe('Rotate session', () => {
119 119
         )
120 120
       )
121 121
 
122
-      tlstate.startSession(SessionType.Rotate, [50, 0]).updateSession([100, 50])
122
+      tlstate.startSession(SessionType.Rotate, [50, 0]).updateSession([100, 50]).completeSession()
123 123
 
124
-      const centerAfter = Vec.round(
124
+      const centerAfterA = Vec.round(
125
+        Utils.getBoundsCenter(
126
+          Utils.getCommonBounds(tlstate.selectedIds.map((id) => tlstate.getShapeBounds(id)))
127
+        )
128
+      )
129
+
130
+      tlstate.startSession(SessionType.Rotate, [100, 0]).updateSession([50, 0]).completeSession()
131
+
132
+      const centerAfterB = Vec.round(
125 133
         Utils.getBoundsCenter(
126 134
           Utils.getCommonBounds(tlstate.selectedIds.map((id) => tlstate.getShapeBounds(id)))
127 135
         )
128 136
       )
129 137
 
130 138
       expect(tlstate.getShape('rect1').rotation)
131
-      expect(centerBefore).toStrictEqual(centerAfter)
139
+      expect(centerBefore).toStrictEqual(centerAfterA)
140
+      expect(centerAfterA).toStrictEqual(centerAfterB)
141
+    })
142
+
143
+    it.todo('clears the cached center after transforming')
144
+    it.todo('clears the cached center after translating')
145
+    it.todo('clears the cached center after undoing')
146
+    it.todo('clears the cached center after redoing')
147
+    it.todo('clears the cached center after any command other than a rotate command, tbh')
148
+
149
+    it('changes the center after nudging', () => {
150
+      const tlstate = new TLDrawState().loadDocument(mockDocument).select('rect1', 'rect2')
151
+
152
+      const centerBefore = Vec.round(
153
+        Utils.getBoundsCenter(
154
+          Utils.getCommonBounds(tlstate.selectedIds.map((id) => tlstate.getShapeBounds(id)))
155
+        )
156
+      )
157
+
158
+      tlstate.startSession(SessionType.Rotate, [50, 0]).updateSession([100, 50]).completeSession()
159
+
160
+      const centerAfterA = Vec.round(
161
+        Utils.getBoundsCenter(
162
+          Utils.getCommonBounds(tlstate.selectedIds.map((id) => tlstate.getShapeBounds(id)))
163
+        )
164
+      )
165
+
166
+      expect(tlstate.getShape('rect1').rotation)
167
+      expect(centerBefore).toStrictEqual(centerAfterA)
168
+
169
+      tlstate.selectAll().nudge([10, 10])
170
+
171
+      tlstate.startSession(SessionType.Rotate, [50, 0]).updateSession([100, 50]).completeSession()
172
+
173
+      const centerAfterB = Vec.round(
174
+        Utils.getBoundsCenter(
175
+          Utils.getCommonBounds(tlstate.selectedIds.map((id) => tlstate.getShapeBounds(id)))
176
+        )
177
+      )
178
+
179
+      expect(centerAfterB).not.toStrictEqual(centerAfterA)
132 180
     })
133 181
   })
134 182
 })

+ 17
- 12
packages/tldraw/src/state/session/sessions/rotate/rotate.session.ts 查看文件

@@ -4,8 +4,6 @@ import { Session, SessionType, TLDrawShape, TLDrawStatus } from '~types'
4 4
 import type { Data } from '~types'
5 5
 import { TLDR } from '~state/tldr'
6 6
 
7
-const centerCache = new WeakMap<string[], number[]>()
8
-
9 7
 export class RotateSession extends Session {
10 8
   static type = SessionType.Rotate
11 9
   status = TLDrawStatus.Transforming
@@ -17,6 +15,7 @@ export class RotateSession extends Session {
17 15
 
18 16
   constructor(data: Data, viewport: TLBounds, point: number[]) {
19 17
     super(viewport)
18
+
20 19
     this.origin = point
21 20
     this.snapshot = getRotateSnapshot(data)
22 21
     this.initialAngle = Vec.angle(this.snapshot.commonBoundsCenter, this.origin)
@@ -134,19 +133,25 @@ export function getRotateSnapshot(data: Data) {
134 133
   const pageState = TLDR.getPageState(data, currentPageId)
135 134
   const initialShapes = TLDR.getSelectedBranchSnapshot(data, currentPageId)
136 135
 
137
-  const commonBoundsCenter = Utils.getFromCache(centerCache, pageState.selectedIds, () => {
138
-    if (initialShapes.length === 0) {
139
-      throw Error('No selected shapes!')
140
-    }
136
+  if (initialShapes.length === 0) {
137
+    throw Error('No selected shapes!')
138
+  }
141 139
 
142
-    const shapesBounds = Object.fromEntries(
143
-      initialShapes.map((shape) => [shape.id, TLDR.getBounds(shape)])
144
-    )
140
+  let commonBoundsCenter: number[]
145 141
 
146
-    const bounds = Utils.getCommonBounds(Object.values(shapesBounds))
142
+  if (Session.cache.selectedIds === pageState.selectedIds) {
143
+    if (Session.cache.center === undefined) {
144
+      throw Error('The center was not added to the cache!')
145
+    }
147 146
 
148
-    return Utils.getBoundsCenter(bounds)
149
-  })
147
+    commonBoundsCenter = Session.cache.center
148
+  } else {
149
+    commonBoundsCenter = Utils.getBoundsCenter(
150
+      Utils.getCommonBounds(initialShapes.map(TLDR.getBounds))
151
+    )
152
+    Session.cache.selectedIds = pageState.selectedIds
153
+    Session.cache.center = commonBoundsCenter
154
+  }
150 155
 
151 156
   return {
152 157
     commonBoundsCenter,

+ 1
- 0
packages/tldraw/src/state/session/sessions/transform-single/transform-single.session.ts 查看文件

@@ -47,6 +47,7 @@ export class TransformSingleSession extends Session {
47 47
     this.transformType = transformType
48 48
     this.snapshot = getTransformSingleSnapshot(data, transformType)
49 49
     this.isCreate = isCreate
50
+    Session.cache.selectedIds = [...this.snapshot.initialShape.id]
50 51
   }
51 52
 
52 53
   start = (data: Data) => {

+ 1
- 2
packages/tldraw/src/state/session/sessions/transform/transform.session.ts 查看文件

@@ -43,6 +43,7 @@ export class TransformSession extends Session {
43 43
     this.snapshot = getTransformSnapshot(data, transformType)
44 44
     this.isCreate = isCreate
45 45
     this.initialSelectedIds = TLDR.getSelectedIds(data, data.appState.currentPageId)
46
+    Session.cache.selectedIds = [...this.initialSelectedIds]
46 47
   }
47 48
 
48 49
   start = (data: Data) => {
@@ -59,8 +60,6 @@ export class TransformSession extends Session {
59 60
 
60 61
     const shapes = {} as Record<string, TLDrawShape>
61 62
 
62
-    const pageState = TLDR.getPageState(data, data.appState.currentPageId)
63
-
64 63
     const delta = Vec.sub(point, this.origin)
65 64
 
66 65
     let newBounds = Utils.getTransformedBoundingBox(

+ 2
- 9
packages/tldraw/src/state/session/sessions/translate/translate.session.ts 查看文件

@@ -12,7 +12,6 @@ import {
12 12
   GroupShape,
13 13
   SessionType,
14 14
   ArrowBinding,
15
-  TLDrawShapeType,
16 15
 } from '~types'
17 16
 import { SLOW_SPEED, SNAP_DISTANCE } from '~constants'
18 17
 import { TLDR } from '~state/tldr'
@@ -70,6 +69,7 @@ export class TranslateSession extends Session {
70 69
     this.snapshot = getTranslateSnapshot(data, isLinked)
71 70
     this.isCreate = isCreate
72 71
     this.isLinked = isLinked
72
+    Session.cache.selectedIds = [...TLDR.getSelectedIds(data, data.appState.currentPageId)]
73 73
   }
74 74
 
75 75
   start = (data: Data) => {
@@ -94,14 +94,7 @@ export class TranslateSession extends Session {
94 94
     }
95 95
   }
96 96
 
97
-  update = (
98
-    data: Data,
99
-    point: number[],
100
-    shiftKey = false,
101
-    altKey = false,
102
-    metaKey = false,
103
-    viewPort = {} as TLBounds
104
-  ) => {
97
+  update = (data: Data, point: number[], shiftKey = false, altKey = false, metaKey = false) => {
105 98
     const { selectedIds, initialParentChildren, initialShapes, bindingsToDelete } = this.snapshot
106 99
 
107 100
     const { currentPageId } = data.appState

+ 5
- 0
packages/tldraw/src/state/tlstate.ts 查看文件

@@ -347,6 +347,10 @@ export class TLDrawState extends StateManager<Data> {
347 347
       this.clearSelectHistory()
348 348
     }
349 349
 
350
+    if (id.startsWith('undo') || id.startsWith('redo')) {
351
+      Session.cache.selectedIds = [...this.selectedIds]
352
+    }
353
+
350 354
     this._onChange?.(this, state, id)
351 355
   }
352 356
 
@@ -1670,6 +1674,7 @@ export class TLDrawState extends StateManager<Data> {
1670 1674
     }
1671 1675
 
1672 1676
     const Session = getSession(type)
1677
+
1673 1678
     // eslint-disable-next-line @typescript-eslint/ban-ts-comment
1674 1679
     // @ts-ignore
1675 1680
     this.session = new Session(this.state, this.viewport, ...args)

+ 8
- 0
packages/tldraw/src/types.ts 查看文件

@@ -155,6 +155,14 @@ export abstract class Session {
155 155
   updateViewport = (viewport: TLBounds) => {
156 156
     this.viewport = viewport
157 157
   }
158
+
159
+  static cache: {
160
+    selectedIds: string[]
161
+    center: number[]
162
+  } = {
163
+    selectedIds: [],
164
+    center: [0, 0],
165
+  }
158 166
 }
159 167
 
160 168
 export enum TLDrawStatus {

+ 0
- 3
packages/vec/scripts/dev.js 查看文件

@@ -11,10 +11,7 @@ async function main() {
11 11
     bundle: true,
12 12
     format: 'cjs',
13 13
     target: 'es6',
14
-    jsxFactory: 'React.createElement',
15
-    jsxFragment: 'React.Fragment',
16 14
     tsconfig: './tsconfig.json',
17
-    external: ['react', 'react-dom'],
18 15
     incremental: true,
19 16
     watch: {
20 17
       onRebuild(error) {

+ 1
- 0
tsconfig.base.json 查看文件

@@ -11,6 +11,7 @@
11 11
     "forceConsistentCasingInFileNames": true,
12 12
     "importHelpers": true,
13 13
     "importsNotUsedAsValues": "error",
14
+    "resolveJsonModule": true,
14 15
     "incremental": true,
15 16
     "jsx": "preserve",
16 17
     "lib": ["dom", "esnext"],

+ 4
- 4
yarn.lock 查看文件

@@ -3722,10 +3722,10 @@
3722 3722
     "@babel/runtime" "^7.12.5"
3723 3723
     "@testing-library/dom" "^8.0.0"
3724 3724
 
3725
-"@tldraw/core@^0.1.9":
3726
-  version "0.1.9"
3727
-  resolved "https://registry.yarnpkg.com/@tldraw/core/-/core-0.1.9.tgz#006748ee0c395ef15756ee1aa3c93b7eef109432"
3728
-  integrity sha512-3vzFceettMZVkVM+ASaAFyFy2YibwnEkIcIiWTmKER+5R0HIEKnwDuH+zXiv+C1dcbagUvArHRw0AiuzA5ft8g==
3725
+"@tldraw/core@^0.1.10":
3726
+  version "0.1.10"
3727
+  resolved "https://registry.yarnpkg.com/@tldraw/core/-/core-0.1.10.tgz#fe6aa880b619361473ebccb5c932082efb535518"
3728
+  integrity sha512-kLqCDQHRykWfskMvcbSn3Ll8Ssd46hMmMdTpqw1O3i3AV2zuWDYkdgGD3PO6PRSkkMZyGNr5/zpkU+Yrk5DYZQ==
3729 3729
   dependencies:
3730 3730
     "@use-gesture/react" "^10.0.2"
3731 3731
 

Loading…
取消
儲存