Bläddra i källkod

Adds zoom helpers, improves drag selection

main
Steve Ruiz 4 år sedan
förälder
incheckning
f4e429af0e

+ 2
- 0
components/canvas/misc.tsx Visa fil

2
 
2
 
3
 const DotCircle = styled("circle", {
3
 const DotCircle = styled("circle", {
4
   transform: "scale(var(--scale))",
4
   transform: "scale(var(--scale))",
5
+  strokeWidth: "2",
6
+  fill: "#000",
5
 })
7
 })
6
 
8
 
7
 export { DotCircle }
9
 export { DotCircle }

+ 0
- 1
components/canvas/shape.tsx Visa fil

95
 const HoverIndicator = styled("path", {
95
 const HoverIndicator = styled("path", {
96
   fill: "none",
96
   fill: "none",
97
   stroke: "transparent",
97
   stroke: "transparent",
98
-  zStrokeWidth: [8, 4],
99
   pointerEvents: "all",
98
   pointerEvents: "all",
100
   strokeLinecap: "round",
99
   strokeLinecap: "round",
101
   strokeLinejoin: "round",
100
   strokeLinejoin: "round",

+ 21
- 0
hooks/useKeyboardEvents.ts Visa fil

10
       }
10
       }
11
 
11
 
12
       switch (e.key) {
12
       switch (e.key) {
13
+        case "!": {
14
+          // Shift + 1
15
+          if (e.shiftKey) {
16
+            state.send("ZOOMED_TO_FIT")
17
+          }
18
+          break
19
+        }
20
+        case "@": {
21
+          // Shift + 2
22
+          if (e.shiftKey) {
23
+            state.send("ZOOMED_TO_SELECTION")
24
+          }
25
+          break
26
+        }
27
+        case ")": {
28
+          // Shift + 0
29
+          if (e.shiftKey) {
30
+            state.send("ZOOMED_TO_ACTUAL")
31
+          }
32
+          break
33
+        }
13
         case "Escape": {
34
         case "Escape": {
14
           state.send("CANCELLED")
35
           state.send("CANCELLED")
15
           break
36
           break

+ 1
- 2
lib/shape-utils/dot.tsx Visa fil

4
 import { registerShapeUtils } from "./index"
4
 import { registerShapeUtils } from "./index"
5
 import { boundsContained } from "utils/bounds"
5
 import { boundsContained } from "utils/bounds"
6
 import { intersectCircleBounds } from "utils/intersections"
6
 import { intersectCircleBounds } from "utils/intersections"
7
-import styled from "styles"
8
 import { DotCircle } from "components/canvas/misc"
7
 import { DotCircle } from "components/canvas/misc"
9
 import { translateBounds } from "utils/utils"
8
 import { translateBounds } from "utils/utils"
10
 
9
 
23
       rotation: 0,
22
       rotation: 0,
24
       style: {
23
       style: {
25
         fill: "#c6cacb",
24
         fill: "#c6cacb",
26
-        stroke: "#000",
25
+        strokeWidth: "0",
27
       },
26
       },
28
       ...props,
27
       ...props,
29
     }
28
     }

+ 1
- 1
lib/shape-utils/line.tsx Visa fil

36
     return (
36
     return (
37
       <g id={id}>
37
       <g id={id}>
38
         <line x1={x1} y1={y1} x2={x2} y2={y2} />
38
         <line x1={x1} y1={y1} x2={x2} y2={y2} />
39
-        <DotCircle cx={0} cy={0} r={4} />
39
+        <DotCircle cx={0} cy={0} r={3} />
40
       </g>
40
       </g>
41
     )
41
     )
42
   },
42
   },

+ 1
- 1
lib/shape-utils/ray.tsx Visa fil

36
     return (
36
     return (
37
       <g id={id}>
37
       <g id={id}>
38
         <line x1={0} y1={0} x2={x2} y2={y2} />
38
         <line x1={0} y1={0} x2={x2} y2={y2} />
39
-        <DotCircle cx={0} cy={0} r={4} />
39
+        <DotCircle cx={0} cy={0} r={3} />
40
       </g>
40
       </g>
41
     )
41
     )
42
   },
42
   },

+ 93
- 12
state/state.ts Visa fil

1
 import { createSelectorHook, createState } from "@state-designer/react"
1
 import { createSelectorHook, createState } from "@state-designer/react"
2
 import {
2
 import {
3
   clamp,
3
   clamp,
4
+  getBoundsCenter,
4
   getChildren,
5
   getChildren,
5
   getCommonBounds,
6
   getCommonBounds,
6
   getPage,
7
   getPage,
8
+  getSelectedBounds,
9
+  getSelectedShapes,
7
   getShape,
10
   getShape,
8
   getSiblings,
11
   getSiblings,
9
   screenToWorld,
12
   screenToWorld,
13
+  setZoomCSS,
10
 } from "utils/utils"
14
 } from "utils/utils"
11
 import * as vec from "utils/vec"
15
 import * as vec from "utils/vec"
12
 import {
16
 import {
68
     SELECTED_RECTANGLE_TOOL: { unless: "isReadOnly", to: "rectangle" },
72
     SELECTED_RECTANGLE_TOOL: { unless: "isReadOnly", to: "rectangle" },
69
     TOGGLED_CODE_PANEL_OPEN: "toggleCodePanel",
73
     TOGGLED_CODE_PANEL_OPEN: "toggleCodePanel",
70
     RESET_CAMERA: "resetCamera",
74
     RESET_CAMERA: "resetCamera",
75
+    ZOOMED_TO_FIT: "zoomCameraToFit",
76
+    ZOOMED_TO_SELECTION: { if: "hasSelection", do: "zoomCameraToSelection" },
77
+    ZOOMED_TO_ACTUAL: {
78
+      if: "hasSelection",
79
+      do: "zoomCameraToSelectionActual",
80
+      else: "zoomCameraToActual",
81
+    },
71
   },
82
   },
72
   initial: "loading",
83
   initial: "loading",
73
   states: {
84
   states: {
480
       return data.isReadOnly
491
       return data.isReadOnly
481
     },
492
     },
482
     distanceImpliesDrag(data, payload: PointerInfo) {
493
     distanceImpliesDrag(data, payload: PointerInfo) {
483
-      return vec.dist2(payload.origin, payload.point) > 16
494
+      return vec.dist2(payload.origin, payload.point) > 8
484
     },
495
     },
485
     isPointedShapeSelected(data) {
496
     isPointedShapeSelected(data) {
486
       return data.selectedIds.has(data.pointedId)
497
       return data.selectedIds.has(data.pointedId)
508
     ) {
519
     ) {
509
       return payload.target === "rotate"
520
       return payload.target === "rotate"
510
     },
521
     },
522
+    hasSelection(data) {
523
+      return data.selectedIds.size > 0
524
+    },
511
   },
525
   },
512
   actions: {
526
   actions: {
513
     /* --------------------- Shapes --------------------- */
527
     /* --------------------- Shapes --------------------- */
565
     startTranslateSession(data, payload: PointerInfo) {
579
     startTranslateSession(data, payload: PointerInfo) {
566
       session = new Sessions.TranslateSession(
580
       session = new Sessions.TranslateSession(
567
         data,
581
         data,
568
-        screenToWorld(payload.point, data),
582
+        screenToWorld(inputs.pointer.origin, data),
569
         payload.altKey
583
         payload.altKey
570
       )
584
       )
571
     },
585
     },
697
 
711
 
698
       document.documentElement.style.setProperty("--camera-zoom", "1")
712
       document.documentElement.style.setProperty("--camera-zoom", "1")
699
     },
713
     },
700
-    centerCamera(data) {
714
+    zoomCameraToSelection(data) {
715
+      const { camera } = data
716
+
717
+      const bounds = getSelectedBounds(data)
718
+
719
+      const zoom =
720
+        bounds.width > bounds.height
721
+          ? (window.innerWidth - 128) / bounds.width
722
+          : (window.innerHeight - 128) / bounds.height
723
+
724
+      const mx = window.innerWidth - bounds.width * zoom
725
+      const my = window.innerHeight - bounds.height * zoom
726
+
727
+      camera.zoom = zoom
728
+
729
+      camera.point = vec.add(
730
+        [-bounds.minX, -bounds.minY],
731
+        [mx / 2 / zoom, my / 2 / zoom]
732
+      )
733
+
734
+      setZoomCSS(camera.zoom)
735
+    },
736
+    zoomCameraToSelectionActual(data) {
737
+      const { camera } = data
738
+
739
+      const bounds = getSelectedBounds(data)
740
+
741
+      const zoom = 1
742
+
743
+      const mx = window.innerWidth - 128 - bounds.width * zoom
744
+      const my = window.innerHeight - 128 - bounds.height * zoom
745
+
746
+      camera.zoom = zoom
747
+      camera.point = vec.add(
748
+        [-bounds.minX, -bounds.minY],
749
+        [mx / 2 / zoom, my / 2 / zoom]
750
+      )
751
+
752
+      setZoomCSS(camera.zoom)
753
+    },
754
+    zoomCameraToActual(data) {
755
+      const { camera } = data
756
+
757
+      const center = [window.innerWidth / 2, window.innerHeight / 2]
758
+
759
+      const p0 = screenToWorld(center, data)
760
+      camera.zoom = 1
761
+      const p1 = screenToWorld(center, data)
762
+      camera.point = vec.add(camera.point, vec.sub(p1, p0))
763
+
764
+      setZoomCSS(camera.zoom)
765
+    },
766
+    zoomCameraToFit(data) {
767
+      const { camera } = data
701
       const { shapes } = getPage(data)
768
       const { shapes } = getPage(data)
702
-      getCommonBounds()
703
-      data.camera.zoom = 1
704
-      data.camera.point = [window.innerWidth / 2, window.innerHeight / 2]
705
 
769
 
706
-      document.documentElement.style.setProperty("--camera-zoom", "1")
770
+      const bounds = getCommonBounds(
771
+        ...Object.values(shapes).map((shape) =>
772
+          getShapeUtils(shape).getBounds(shape)
773
+        )
774
+      )
775
+
776
+      const zoom =
777
+        bounds.width > bounds.height
778
+          ? (window.innerWidth - 104) / bounds.width
779
+          : (window.innerHeight - 104) / bounds.height
780
+
781
+      const mx = window.innerWidth - bounds.width * zoom
782
+      const my = window.innerHeight - bounds.height * zoom
783
+
784
+      camera.zoom = zoom
785
+      camera.point = vec.add(
786
+        [-bounds.minX, -bounds.minY],
787
+        [mx / 2 / zoom, my / 2 / zoom]
788
+      )
789
+
790
+      setZoomCSS(camera.zoom)
707
     },
791
     },
708
     zoomCamera(data, payload: { delta: number; point: number[] }) {
792
     zoomCamera(data, payload: { delta: number; point: number[] }) {
709
       const { camera } = data
793
       const { camera } = data
710
       const p0 = screenToWorld(payload.point, data)
794
       const p0 = screenToWorld(payload.point, data)
711
       camera.zoom = clamp(
795
       camera.zoom = clamp(
712
         camera.zoom - (payload.delta / 100) * camera.zoom,
796
         camera.zoom - (payload.delta / 100) * camera.zoom,
713
-        0.5,
797
+        0.1,
714
         3
798
         3
715
       )
799
       )
716
       const p1 = screenToWorld(payload.point, data)
800
       const p1 = screenToWorld(payload.point, data)
717
       camera.point = vec.add(camera.point, vec.sub(p1, p0))
801
       camera.point = vec.add(camera.point, vec.sub(p1, p0))
718
 
802
 
719
-      document.documentElement.style.setProperty(
720
-        "--camera-zoom",
721
-        camera.zoom.toString()
722
-      )
803
+      setZoomCSS(camera.zoom)
723
     },
804
     },
724
     panCamera(data, payload: { delta: number[]; point: number[] }) {
805
     panCamera(data, payload: { delta: number[]; point: number[] }) {
725
       const { camera } = data
806
       const { camera } = data

+ 12
- 7
styles/stitches.config.ts Visa fil

43
   utils: {
43
   utils: {
44
     zStrokeWidth: () => (value: number | number[]) => {
44
     zStrokeWidth: () => (value: number | number[]) => {
45
       if (Array.isArray(value)) {
45
       if (Array.isArray(value)) {
46
-        const [val, min, max] = value
47
         return {
46
         return {
48
-          strokeWidth:
49
-            min !== undefined && max !== undefined
50
-              ? `clamp(${min}, ${val} / var(--camera-zoom), ${max})`
51
-              : min !== undefined
52
-              ? `min(${min}, ${val} / var(--camera-zoom))`
53
-              : `calc(${val} / var(--camera-zoom))`,
47
+          strokeWidth: `calc(${value[0]} / var(--camera-zoom))`,
54
         }
48
         }
55
       }
49
       }
56
 
50
 
51
+      // const [val, min, max] = value
52
+      // return {
53
+      //   strokeWidth:
54
+      //     min !== undefined && max !== undefined
55
+      //       ? `clamp(${min}, ${val} / var(--camera-zoom), ${max})`
56
+      //       : min !== undefined
57
+      //       ? `min(${min}, ${val} / var(--camera-zoom))`
58
+      //       : `calc(${val} / var(--camera-zoom))`,
59
+      // }
60
+      // }
61
+
57
       return {
62
       return {
58
         strokeWidth: `calc(${value} / var(--camera-zoom))`,
63
         strokeWidth: `calc(${value} / var(--camera-zoom))`,
59
       }
64
       }

+ 11
- 0
utils/utils.ts Visa fil

1355
   return ids.map((id) => page.shapes[id])
1355
   return ids.map((id) => page.shapes[id])
1356
 }
1356
 }
1357
 
1357
 
1358
+export function getSelectedBounds(data: Data) {
1359
+  return getCommonBounds(
1360
+    ...getSelectedShapes(data).map((shape) =>
1361
+      getShapeUtils(shape).getBounds(shape)
1362
+    )
1363
+  )
1364
+}
1365
+
1358
 export function isMobile() {
1366
 export function isMobile() {
1359
   return _isMobile()
1367
   return _isMobile()
1360
 }
1368
 }
1474
     shapes[i].childIndex = i + 1
1482
     shapes[i].childIndex = i + 1
1475
   }
1483
   }
1476
 }
1484
 }
1485
+export function setZoomCSS(zoom: number) {
1486
+  document.documentElement.style.setProperty("--camera-zoom", zoom.toString())
1487
+}

Laddar…
Avbryt
Spara