improves appearance on safari
This commit is contained in:
parent
8dfef5c302
commit
708223fffa
8 changed files with 103 additions and 14 deletions
|
@ -20,7 +20,7 @@ export default function Bounds() {
|
||||||
if (!bounds) return null
|
if (!bounds) return null
|
||||||
if (!isSelecting) return null
|
if (!isSelecting) return null
|
||||||
|
|
||||||
const size = (isMobile().any ? 16 : 8) / zoom // Touch target size
|
const size = (isMobile().any ? 12 : 8) / zoom // Touch target size
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g
|
<g
|
||||||
|
|
25
components/canvas/defs.tsx
Normal file
25
components/canvas/defs.tsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { getShapeUtils } from "lib/shape-utils"
|
||||||
|
import { useSelector } from "state"
|
||||||
|
import { deepCompareArrays, getPage } from "utils/utils"
|
||||||
|
|
||||||
|
export default function Defs() {
|
||||||
|
const currentPageShapeIds = useSelector(({ data }) => {
|
||||||
|
return Object.values(getPage(data).shapes)
|
||||||
|
.sort((a, b) => a.childIndex - b.childIndex)
|
||||||
|
.map((shape) => shape.id)
|
||||||
|
}, deepCompareArrays)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<defs>
|
||||||
|
{currentPageShapeIds.map((id) => (
|
||||||
|
<Def key={id} id={id} />
|
||||||
|
))}
|
||||||
|
</defs>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Def({ id }: { id: string }) {
|
||||||
|
const shape = useSelector(({ data }) => getPage(data).shapes[id])
|
||||||
|
|
||||||
|
return getShapeUtils(shape).render(shape)
|
||||||
|
}
|
|
@ -6,14 +6,14 @@ import { getShapeUtils } from "lib/shape-utils"
|
||||||
import { getPage } from "utils/utils"
|
import { getPage } from "utils/utils"
|
||||||
|
|
||||||
function Shape({ id }: { id: string }) {
|
function Shape({ id }: { id: string }) {
|
||||||
const rGroup = useRef<SVGGElement>(null)
|
|
||||||
|
|
||||||
const isHovered = useSelector((state) => state.data.hoveredId === id)
|
const isHovered = useSelector((state) => state.data.hoveredId === id)
|
||||||
|
|
||||||
const isSelected = useSelector((state) => state.values.selectedIds.has(id))
|
const isSelected = useSelector((state) => state.values.selectedIds.has(id))
|
||||||
|
|
||||||
const shape = useSelector(({ data }) => getPage(data).shapes[id])
|
const shape = useSelector(({ data }) => getPage(data).shapes[id])
|
||||||
|
|
||||||
|
const rGroup = useRef<SVGGElement>(null)
|
||||||
|
|
||||||
const handlePointerDown = useCallback(
|
const handlePointerDown = useCallback(
|
||||||
(e: React.PointerEvent) => {
|
(e: React.PointerEvent) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
@ -72,9 +72,9 @@ function Shape({ id }: { id: string }) {
|
||||||
onPointerMove={handlePointerMove}
|
onPointerMove={handlePointerMove}
|
||||||
>
|
>
|
||||||
<defs>{getShapeUtils(shape).render(shape)}</defs>
|
<defs>{getShapeUtils(shape).render(shape)}</defs>
|
||||||
<HoverIndicator as="use" xlinkHref={"#" + id} />
|
<HoverIndicator as="use" href={"#" + id} />
|
||||||
<MainShape as="use" xlinkHref={"#" + id} {...shape.style} />
|
<MainShape as="use" href={"#" + id} {...shape.style} />
|
||||||
<Indicator as="use" xlinkHref={"#" + id} />
|
<Indicator as="use" href={"#" + id} />
|
||||||
</StyledGroup>
|
</StyledGroup>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import React, { useEffect, useRef } from "react"
|
||||||
import state from "state"
|
import state from "state"
|
||||||
import inputs from "state/inputs"
|
import inputs from "state/inputs"
|
||||||
import * as vec from "utils/vec"
|
import * as vec from "utils/vec"
|
||||||
|
import { usePinch } from "react-use-gesture"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Capture zoom gestures (pinches, wheels and pans) and send to the state.
|
* Capture zoom gestures (pinches, wheels and pans) and send to the state.
|
||||||
|
@ -65,5 +66,36 @@ export default function useZoomEvents(
|
||||||
}
|
}
|
||||||
}, [ref])
|
}, [ref])
|
||||||
|
|
||||||
return {}
|
const rPinchDa = useRef<number[] | undefined>(undefined)
|
||||||
|
const rPinchAngle = useRef<number>(undefined)
|
||||||
|
const rPinchPoint = useRef<number[] | undefined>(undefined)
|
||||||
|
|
||||||
|
const bind = usePinch(({ pinching, da, origin }) => {
|
||||||
|
if (!pinching) {
|
||||||
|
state.send("STOPPED_PINCHING")
|
||||||
|
rPinchDa.current = undefined
|
||||||
|
rPinchPoint.current = undefined
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rPinchPoint.current === undefined) {
|
||||||
|
state.send("STARTED_PINCHING")
|
||||||
|
rPinchDa.current = da
|
||||||
|
rPinchPoint.current = origin
|
||||||
|
}
|
||||||
|
|
||||||
|
const [distanceDelta, angleDelta] = vec.sub(rPinchDa.current, da)
|
||||||
|
|
||||||
|
state.send("PINCHED", {
|
||||||
|
delta: vec.sub(rPinchPoint.current, origin),
|
||||||
|
point: origin,
|
||||||
|
distanceDelta,
|
||||||
|
angleDelta,
|
||||||
|
})
|
||||||
|
|
||||||
|
rPinchDa.current = da
|
||||||
|
rPinchPoint.current = origin
|
||||||
|
})
|
||||||
|
|
||||||
|
return { ...bind() }
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
"react-feather": "^2.0.9",
|
"react-feather": "^2.0.9",
|
||||||
|
"react-use-gesture": "^9.1.3",
|
||||||
"uuid": "^8.3.2"
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -130,6 +130,7 @@ const state = createState({
|
||||||
STRETCHED: "stretchSelection",
|
STRETCHED: "stretchSelection",
|
||||||
DISTRIBUTED: "distributeSelection",
|
DISTRIBUTED: "distributeSelection",
|
||||||
MOVED: "moveSelection",
|
MOVED: "moveSelection",
|
||||||
|
STARTED_PINCHING: { to: "pinching" },
|
||||||
},
|
},
|
||||||
initial: "notPointing",
|
initial: "notPointing",
|
||||||
states: {
|
states: {
|
||||||
|
@ -248,6 +249,12 @@ const state = createState({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
pinching: {
|
||||||
|
on: {
|
||||||
|
STOPPED_PINCHING: { to: "selecting" },
|
||||||
|
PINCHED: { do: "pinchCamera" },
|
||||||
|
},
|
||||||
|
},
|
||||||
draw: {
|
draw: {
|
||||||
initial: "creating",
|
initial: "creating",
|
||||||
states: {
|
states: {
|
||||||
|
@ -829,12 +836,31 @@ const state = createState({
|
||||||
|
|
||||||
setZoomCSS(camera.zoom)
|
setZoomCSS(camera.zoom)
|
||||||
},
|
},
|
||||||
panCamera(data, payload: { delta: number[]; point: number[] }) {
|
panCamera(data, payload: { delta: number[] }) {
|
||||||
const { camera } = data
|
const { camera } = data
|
||||||
data.camera.point = vec.sub(
|
camera.point = vec.sub(camera.point, vec.div(payload.delta, camera.zoom))
|
||||||
camera.point,
|
},
|
||||||
vec.div(payload.delta, camera.zoom)
|
pinchCamera(
|
||||||
)
|
data,
|
||||||
|
payload: {
|
||||||
|
delta: number[]
|
||||||
|
distanceDelta: number
|
||||||
|
angleDelta: number
|
||||||
|
point: number[]
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
const { camera } = data
|
||||||
|
|
||||||
|
camera.point = vec.sub(camera.point, vec.div(payload.delta, camera.zoom))
|
||||||
|
|
||||||
|
const next = camera.zoom - (payload.distanceDelta / 300) * camera.zoom
|
||||||
|
|
||||||
|
const p0 = screenToWorld(payload.point, data)
|
||||||
|
camera.zoom = clamp(next, 0.1, 3)
|
||||||
|
const p1 = screenToWorld(payload.point, data)
|
||||||
|
camera.point = vec.add(camera.point, vec.sub(p1, p0))
|
||||||
|
|
||||||
|
setZoomCSS(camera.zoom)
|
||||||
},
|
},
|
||||||
deleteSelectedIds(data) {
|
deleteSelectedIds(data) {
|
||||||
commands.deleteSelected(data)
|
commands.deleteSelected(data)
|
||||||
|
|
|
@ -45,7 +45,7 @@ const { styled, global, css, theme, getCssString } = createCss({
|
||||||
zStrokeWidth: () => (value: number | number[]) => {
|
zStrokeWidth: () => (value: number | number[]) => {
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
return {
|
return {
|
||||||
strokeWidth: `calc(${value[0]} / var(--camera-zoom))`,
|
strokeWidth: `calc(${value[0]}px / var(--camera-zoom))`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ const { styled, global, css, theme, getCssString } = createCss({
|
||||||
// }
|
// }
|
||||||
|
|
||||||
return {
|
return {
|
||||||
strokeWidth: `calc(${value} / var(--camera-zoom))`,
|
strokeWidth: `calc(${value}px / var(--camera-zoom))`,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -6697,6 +6697,11 @@ react-style-singleton@^2.1.0:
|
||||||
invariant "^2.2.4"
|
invariant "^2.2.4"
|
||||||
tslib "^1.0.0"
|
tslib "^1.0.0"
|
||||||
|
|
||||||
|
react-use-gesture@^9.1.3:
|
||||||
|
version "9.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-use-gesture/-/react-use-gesture-9.1.3.tgz#92bd143e4f58e69bd424514a5bfccba2a1d62ec0"
|
||||||
|
integrity sha512-CdqA2SmS/fj3kkS2W8ZU8wjTbVBAIwDWaRprX7OKaj7HlGwBasGEFggmk5qNklknqk9zK/h8D355bEJFTpqEMg==
|
||||||
|
|
||||||
react@17.0.2:
|
react@17.0.2:
|
||||||
version "17.0.2"
|
version "17.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||||
|
|
Loading…
Reference in a new issue