improve canvas positioning, fix zaxis reordering

This commit is contained in:
Steve Ruiz 2021-09-22 16:00:20 +01:00
parent 9c0de7ad9c
commit b90c57bbc3
12 changed files with 58 additions and 35 deletions

View file

@ -59,7 +59,7 @@
"dependencies": {
"@tldraw/intersect": "^0.0.98",
"@tldraw/vec": "^0.0.98",
"@use-gesture/react": "^10.0.0-beta.24"
"@use-gesture/react": "^10.0.0-beta.26"
},
"gitHead": "5cb031ddc264846ec6732d7179511cddea8ef034"
}

View file

@ -39,6 +39,7 @@ export function Canvas<T extends TLShape, M extends Record<string, unknown>>({
}: CanvasProps<T, M>): JSX.Element {
const rCanvas = React.useRef<HTMLDivElement>(null)
const rContainer = React.useRef<HTMLDivElement>(null)
const rLayer = React.useRef<HTMLDivElement>(null)
useResizeObserver(rCanvas)
@ -50,7 +51,7 @@ export function Canvas<T extends TLShape, M extends Record<string, unknown>>({
const events = useCanvasEvents()
const rLayer = useCameraCss(rContainer, pageState)
useCameraCss(rLayer, rContainer, pageState)
const preventScrolling = React.useCallback((e: React.UIEvent<HTMLDivElement, UIEvent>) => {
e.currentTarget.scrollTo(0, 0)

View file

@ -3,9 +3,9 @@ import type { TLBounds } from '+types'
import { usePosition } from '+hooks'
interface ContainerProps {
id?: string
bounds: TLBounds
rotation?: number
id?: string
className?: string
children: React.ReactNode
}

View file

@ -52,7 +52,7 @@ export function Page<T extends TLShape, M extends Record<string, unknown>>({
return (
<>
{bounds && !hideBounds && <BoundsBg bounds={bounds} rotation={rotation} />}
{shapeTree.map((node, i) => (
{shapeTree.map((node) => (
<ShapeNode key={node.shape.id} utils={shapeUtils} {...node} />
))}
{bounds && !hideBounds && (

View file

@ -31,7 +31,7 @@ export const ShapeNode = React.memo(
meta={meta}
/>
{children &&
children.map((childNode) => (
children.map((childNode, i) => (
<ShapeNode key={childNode.shape.id} utils={utils} {...childNode} />
))}
</>

View file

@ -2,18 +2,34 @@
import * as React from 'react'
import type { TLPageState } from '+types'
export function useCameraCss(ref: React.RefObject<HTMLDivElement>, pageState: TLPageState) {
const rLayer = React.useRef<HTMLDivElement>(null)
export function useCameraCss(
layerRef: React.RefObject<HTMLDivElement>,
containerRef: React.RefObject<HTMLDivElement>,
pageState: TLPageState
) {
// Update the tl-zoom CSS variable when the zoom changes
React.useEffect(() => {
ref.current!.style.setProperty('--tl-zoom', pageState.camera.zoom.toString())
}, [pageState.camera.zoom])
const rZoom = React.useRef(pageState.camera.zoom)
React.useEffect(() => {
ref.current!.style.setProperty('--tl-camera-x', pageState.camera.point[0] + 'px')
ref.current!.style.setProperty('--tl-camera-y', pageState.camera.point[1] + 'px')
}, [pageState.camera.point])
React.useLayoutEffect(() => {
const {
zoom,
point: [x, y],
} = pageState.camera
return rLayer
if (zoom !== rZoom.current) {
rZoom.current = zoom
const container = containerRef.current
if (container) {
container.style.setProperty('--tl-zoom', zoom.toString())
}
}
const layer = layerRef.current
if (layer) {
layer.style.setProperty('transform', `scale(${zoom}) translate(${x}px, ${y}px)`)
}
}, [pageState.camera])
}

View file

@ -2,7 +2,7 @@
import * as React from 'react'
import type { TLBounds } from '+types'
export function usePosition(bounds: TLBounds, rotation = 0) {
export function usePosition(bounds: TLBounds, rotation = 0, zIndex = 0) {
const rBounds = React.useRef<HTMLDivElement>(null)
// Update the transform
@ -24,6 +24,8 @@ export function usePosition(bounds: TLBounds, rotation = 0) {
'height',
`calc(${Math.floor(bounds.height)}px + (var(--tl-padding) * 2))`
)
elm.style.setProperty('z-index', zIndex + '')
}, [bounds, rotation])
return rBounds

View file

@ -111,7 +111,8 @@ export function useShapeTree<
shapesIdsToRender.clear()
Object.values(page.shapes)
.filter((shape) => {
.sort((a, b) => a.childIndex - b.childIndex)
.forEach((shape) => {
// Don't hide selected shapes (this breaks certain drag interactions)
if (
selectedIds.includes(shape.id) ||
@ -123,7 +124,6 @@ export function useShapeTree<
}
}
})
.sort((a, b) => a.childIndex - b.childIndex)
// Call onChange callback when number of rendering shapes changes

View file

@ -111,8 +111,6 @@ const tlcss = css`
.tl-container {
--tl-zoom: 1;
--tl-scale: calc(1 / var(--tl-zoom));
--tl-camera-x: 0px;
--tl-camera-y: 0px;
--tl-padding: calc(64px * max(1, var(--tl-scale)));
position: relative;
top: 0px;
@ -150,8 +148,6 @@ const tlcss = css`
left: 0;
height: 0;
width: 0;
contain: layout size;
transform: scale(var(--tl-zoom)) translate(var(--tl-camera-x), var(--tl-camera-y));
}
.tl-absolute {
@ -171,7 +167,6 @@ const tlcss = css`
align-items: center;
justify-content: center;
overflow: clip;
contain: layout size paint;
}
.tl-positioned-svg {

View file

@ -925,8 +925,11 @@ export class TLDrawState extends StateManager<Data> {
}))
)
}
try {
if (!('clipboard' in navigator && navigator.clipboard.readText)) {
throw Error('This browser does not support the clipboard API.')
}
navigator.clipboard.readText().then((result) => {
try {
const data: { type: string; shapes: TLDrawShape[] } = JSON.parse(result)
@ -937,6 +940,8 @@ export class TLDrawState extends StateManager<Data> {
pasteInCurrentPage(data.shapes)
} catch (e) {
console.warn(e)
const shapeId = Utils.uniqueId()
this.createShapes({
@ -951,7 +956,8 @@ export class TLDrawState extends StateManager<Data> {
this.select(shapeId)
}
})
} catch {
} catch (e: any) {
console.warn(e.message)
// Navigator does not support clipboard. Note that this fallback will
// not support pasting from one document to another.
if (this.clipboard) {
@ -1122,7 +1128,7 @@ export class TLDrawState extends StateManager<Data> {
if (shapes.length === 0) return this
const bounds = Utils.getCommonBounds(Object.values(shapes).map(TLDR.getBounds))
const bounds = Utils.getCommonBounds(shapes.map(TLDR.getBounds))
const zoom = TLDR.getCameraZoom(
this.bounds.width < this.bounds.height
@ -1135,7 +1141,7 @@ export class TLDrawState extends StateManager<Data> {
return this.setCamera(
Vec.round(Vec.add([-bounds.minX, -bounds.minY], [mx, my])),
this.pageState.camera.zoom,
zoom,
`zoomed_to_fit`
)
}
@ -2326,6 +2332,7 @@ export class TLDrawState extends StateManager<Data> {
// const nextZoom = TLDR.getCameraZoom(i * 0.25)
// this.zoomTo(nextZoom, inputs.pointer?.point)
// }
this.undoSelect()
this.setStatus(TLDrawStatus.Idle)
}

View file

@ -16,12 +16,14 @@ export default function Editor({ id = 'home' }: EditorProps) {
// Send events to gtag as actions.
const handleChange = React.useCallback((_tlstate: TLDrawState, _state: Data, reason: string) => {
if (reason.startsWith('command')) {
gtag.event({
action: reason,
category: 'editor',
label: `page:${id}`,
value: 0,
})
}
}, [])
return (

View file

@ -4127,7 +4127,7 @@
resolved "https://registry.yarnpkg.com/@use-gesture/core/-/core-10.0.0-beta.26.tgz#275a69ed65c6463cb59872a6f0b24b66212e8bf5"
integrity sha512-Y+fwWlYHzGlc/c4aE3fUkQy7cE4+Tbd1CWQqAlO4czN2nGpgZksW7V98FMgCHrmMGXi+VdNSTS8WBHmw8kq3Zg==
"@use-gesture/react@^10.0.0-beta.24":
"@use-gesture/react@^10.0.0-beta.26":
version "10.0.0-beta.26"
resolved "https://registry.yarnpkg.com/@use-gesture/react/-/react-10.0.0-beta.26.tgz#9b5c3a9111d6cda62de125612fb830efcc1df571"
integrity sha512-TENwg1CgB+okRTBWvcMtEaKYgQvktU0ezG4sQdo/mqZXNvTzuk9g/qK4v++8UzIhj/srqRp49NPBbY0qzsgg1w==