Adds space panning, tweak css for performance

This commit is contained in:
Steve Ruiz 2021-10-14 13:33:39 +01:00
parent 5a99f5e49c
commit f8cb7f03b6
17 changed files with 308 additions and 239 deletions

View file

@ -10,7 +10,7 @@ interface BoundsBgProps {
rotation: number
}
export function BoundsBg({ bounds, rotation }: BoundsBgProps): JSX.Element {
export const BoundsBg = React.memo(({ bounds, rotation }: BoundsBgProps): JSX.Element => {
const events = useBoundsEvents()
return (
@ -20,4 +20,4 @@ export function BoundsBg({ bounds, rotation }: BoundsBgProps): JSX.Element {
</SVGContainer>
</Container>
)
}
})

View file

@ -17,88 +17,83 @@ interface BoundsProps {
viewportWidth: number
}
export function Bounds({
zoom,
bounds,
viewportWidth,
rotation,
isHidden,
isLocked,
}: BoundsProps): JSX.Element {
// Touch target size
const targetSize = (viewportWidth < 768 ? 16 : 8) / zoom
// Handle size
const size = 8 / zoom
export const Bounds = React.memo(
({ zoom, bounds, viewportWidth, rotation, isHidden, isLocked }: BoundsProps): JSX.Element => {
// Touch target size
const targetSize = (viewportWidth < 768 ? 16 : 8) / zoom
// Handle size
const size = 8 / zoom
const smallDimension = Math.min(bounds.width, bounds.height) * zoom
// If the bounds are small, don't show the rotate handle
const showRotateHandle = !isHidden && !isLocked && smallDimension > 32
// If the bounds are very small, don't show the corner handles
const showHandles = !isHidden && !isLocked && smallDimension > 16
const smallDimension = Math.min(bounds.width, bounds.height) * zoom
// If the bounds are small, don't show the rotate handle
const showRotateHandle = !isHidden && !isLocked && smallDimension > 32
// If the bounds are very small, don't show the corner handles
const showHandles = !isHidden && !isLocked && smallDimension > 16
return (
<Container bounds={bounds} rotation={rotation}>
<SVGContainer opacity={isHidden ? 0 : 1}>
<CenterHandle bounds={bounds} isLocked={isLocked} />
<EdgeHandle
targetSize={targetSize}
size={size}
bounds={bounds}
edge={TLBoundsEdge.Top}
isHidden={!showHandles}
/>
<EdgeHandle
targetSize={targetSize}
size={size}
bounds={bounds}
edge={TLBoundsEdge.Right}
isHidden={!showHandles}
/>
<EdgeHandle
targetSize={targetSize}
size={size}
bounds={bounds}
edge={TLBoundsEdge.Bottom}
isHidden={!showHandles}
/>
<EdgeHandle
targetSize={targetSize}
size={size}
bounds={bounds}
edge={TLBoundsEdge.Left}
isHidden={!showHandles}
/>
<CornerHandle
targetSize={targetSize}
size={size}
bounds={bounds}
corner={TLBoundsCorner.TopLeft}
/>
<CornerHandle
targetSize={targetSize}
size={size}
bounds={bounds}
corner={TLBoundsCorner.TopRight}
/>
<CornerHandle
targetSize={targetSize}
size={size}
bounds={bounds}
corner={TLBoundsCorner.BottomRight}
/>
<CornerHandle
targetSize={targetSize}
size={size}
bounds={bounds}
corner={TLBoundsCorner.BottomLeft}
/>
<RotateHandle
targetSize={targetSize}
size={size}
bounds={bounds}
isHidden={!showHandles || !showRotateHandle}
/>
</SVGContainer>
</Container>
)
}
return (
<Container bounds={bounds} rotation={rotation}>
<SVGContainer opacity={isHidden ? 0 : 1}>
<CenterHandle bounds={bounds} isLocked={isLocked} />
<EdgeHandle
targetSize={targetSize}
size={size}
bounds={bounds}
edge={TLBoundsEdge.Top}
isHidden={!showHandles}
/>
<EdgeHandle
targetSize={targetSize}
size={size}
bounds={bounds}
edge={TLBoundsEdge.Right}
isHidden={!showHandles}
/>
<EdgeHandle
targetSize={targetSize}
size={size}
bounds={bounds}
edge={TLBoundsEdge.Bottom}
isHidden={!showHandles}
/>
<EdgeHandle
targetSize={targetSize}
size={size}
bounds={bounds}
edge={TLBoundsEdge.Left}
isHidden={!showHandles}
/>
<CornerHandle
targetSize={targetSize}
size={size}
bounds={bounds}
corner={TLBoundsCorner.TopLeft}
/>
<CornerHandle
targetSize={targetSize}
size={size}
bounds={bounds}
corner={TLBoundsCorner.TopRight}
/>
<CornerHandle
targetSize={targetSize}
size={size}
bounds={bounds}
corner={TLBoundsCorner.BottomRight}
/>
<CornerHandle
targetSize={targetSize}
size={size}
bounds={bounds}
corner={TLBoundsCorner.BottomLeft}
/>
<RotateHandle
targetSize={targetSize}
size={size}
bounds={bounds}
isHidden={!showHandles || !showRotateHandle}
/>
</SVGContainer>
</Container>
)
}
)

View file

@ -59,14 +59,15 @@ export function Canvas<T extends TLShape, M extends Record<string, unknown>>({
useSafariFocusOutFix()
usePreventNavigation(rCanvas)
usePreventNavigation(rCanvas, inputs.bounds.width)
const events = useCanvasEvents()
useCameraCss(rLayer, rContainer, pageState)
const preventScrolling = React.useCallback((e: React.UIEvent<HTMLDivElement, UIEvent>) => {
e.currentTarget.scrollTo(0, 0)
e.preventDefault()
// e.currentTarget.scrollTo(0, 0)
}, [])
useKeyEvents()

View file

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

View file

@ -9,27 +9,34 @@ export function useCameraCss(
) {
// Update the tl-zoom CSS variable when the zoom changes
const rZoom = React.useRef(pageState.camera.zoom)
const rPoint = React.useRef(pageState.camera.point)
React.useLayoutEffect(() => {
const {
zoom,
point: [x, y],
} = pageState.camera
const { zoom, point } = pageState.camera
if (zoom !== rZoom.current) {
rZoom.current = zoom
const didZoom = zoom !== rZoom.current
const didPan = point !== rPoint.current
const container = containerRef.current
rZoom.current = zoom
rPoint.current = point
if (container) {
container.style.setProperty('--tl-zoom', zoom.toString())
if (didZoom || didPan) {
// If we zoomed, set the CSS variable for the zoom
if (didZoom) {
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)`)
// Either way, position the layer
const layer = layerRef.current
if (layer) {
layer.style.setProperty(
'transform',
`scale(${zoom}) translateX(${point[0]}px) translateY(${point[1]}px)`
)
}
}
}, [pageState.camera])
}

View file

@ -1,3 +1,4 @@
import Vec from '@tldraw/vec'
import * as React from 'react'
import { useTLContext } from './useTLContext'
@ -10,8 +11,9 @@ export function useCanvasEvents() {
if (!inputs.pointerIsValid(e)) return
e.currentTarget.setPointerCapture(e.pointerId)
const info = inputs.pointerDown(e, 'canvas')
if (e.button === 0) {
const info = inputs.pointerDown(e, 'canvas')
callbacks.onPointCanvas?.(info, e)
callbacks.onPointerDown?.(info, e)
}
@ -22,11 +24,12 @@ export function useCanvasEvents() {
const onPointerMove = React.useCallback(
(e: React.PointerEvent) => {
if (!inputs.pointerIsValid(e)) return
const info = inputs.pointerMove(e, 'canvas')
if (e.currentTarget.hasPointerCapture(e.pointerId)) {
const info = inputs.pointerMove(e, 'canvas')
callbacks.onDragCanvas?.(info, e)
}
const info = inputs.pointerMove(e, 'canvas')
callbacks.onPointerMove?.(info, e)
},
[callbacks, inputs]

View file

@ -25,7 +25,7 @@ export function usePosition(bounds: TLBounds, rotation = 0, zIndex = 0) {
`calc(${Math.floor(bounds.height)}px + (var(--tl-padding) * 2))`
)
elm.style.setProperty('z-index', zIndex + '')
// elm.style.setProperty('z-index', zIndex + '')
}, [bounds, rotation])
return rBounds

View file

@ -1,7 +1,10 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import * as React from 'react'
export function usePreventNavigation(rCanvas: React.RefObject<HTMLDivElement>): void {
export function usePreventNavigation(
rCanvas: React.RefObject<HTMLDivElement>,
width: number
): void {
React.useEffect(() => {
const preventGestureNavigation = (event: TouchEvent) => {
event.preventDefault()
@ -17,10 +20,7 @@ export function usePreventNavigation(rCanvas: React.RefObject<HTMLDivElement>):
// if the touch area overlaps with the screen edges
// it's likely to trigger the navigation. We prevent the
// touchstart event in that case.
if (
touchXPosition - touchXRadius < 10 ||
touchXPosition + touchXRadius > window.innerWidth - 10
) {
if (touchXPosition - touchXRadius < 10 || touchXPosition + touchXRadius > width - 10) {
event.preventDefault()
}
}
@ -56,5 +56,5 @@ export function usePreventNavigation(rCanvas: React.RefObject<HTMLDivElement>):
elm.removeEventListener('touchstart', preventNavigation)
}
}
}, [rCanvas])
}, [rCanvas, width])
}

View file

@ -1,3 +1,4 @@
import * as React from 'react'
import type { TLPage, TLPageState, TLShape, TLBounds, TLShapeUtils, TLBinding } from '+types'
import Utils from '+utils'
import { useTLContext } from '+hooks'
@ -13,6 +14,7 @@ export function useSelection<T extends TLShape, E extends Element>(
) {
const { rSelectionBounds } = useTLContext()
const { selectedIds } = pageState
const rPrevBounds = React.useRef<TLBounds>()
let bounds: TLBounds | undefined = undefined
let rotation = 0
@ -62,5 +64,20 @@ export function useSelection<T extends TLShape, E extends Element>(
rSelectionBounds.current = null
}
const prevBounds = rPrevBounds.current
if (!prevBounds || !bounds) {
rPrevBounds.current = bounds
} else if (bounds) {
if (
prevBounds.minX === bounds.minX &&
prevBounds.minY === bounds.minY &&
prevBounds.maxX === bounds.maxX &&
prevBounds.maxY === bounds.maxY
) {
bounds = rPrevBounds.current
}
}
return { bounds, rotation, isLocked }
}

View file

@ -135,19 +135,20 @@ const tlcss = css`
.tl-canvas {
position: absolute;
overflow: hidden;
width: 100%;
height: 100%;
touch-action: none;
pointer-events: all;
overflow: clip;
}
.tl-layer {
position: absolute;
top: 0;
left: 0;
height: 0;
width: 0;
position: fixed;
top: 0px;
left: 0px;
height: 0px;
width: 0px;
contain: layout style size;
}
.tl-absolute {
@ -166,13 +167,14 @@ const tlcss = css`
display: flex;
align-items: center;
justify-content: center;
overflow: clip;
overflow: hidden;
contain: layout style size;
}
.tl-positioned-svg {
width: 100%;
height: 100%;
overflow: clip;
overflow: hidden;
}
.tl-positioned-div {
@ -181,7 +183,7 @@ const tlcss = css`
height: 100%;
overflow: hidden;
padding: var(--tl-padding);
overflow: clip;
overflow: hidden;
}
.tl-counter-scaled {
@ -263,6 +265,14 @@ const tlcss = css`
.tl-bounds {
pointer-events: none;
contain: layout style size;
}
.tl-bounds-bg {
stroke: none;
fill: var(--tl-selectFill);
pointer-events: all;
contain: layout style size;
}
.tl-bounds-center {
@ -271,17 +281,12 @@ const tlcss = css`
stroke-width: calc(1.5px * var(--tl-scale));
}
.tl-bounds-bg {
stroke: none;
fill: var(--tl-selectFill);
pointer-events: all;
}
.tl-brush {
fill: var(--tl-brushFill);
stroke: var(--tl-brushStroke);
stroke-width: calc(1px * var(--tl-scale));
pointer-events: none;
contain: layout style size;
}
.tl-dot {

View file

@ -2,7 +2,7 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import * as React from 'react'
import { useTLContext } from './useTLContext'
import { useGesture } from '@use-gesture/react'
import { useGesture, usePinch, useWheel } from '@use-gesture/react'
import { Vec } from '@tldraw/vec'
// Capture zoom gestures (pinches, wheels and pans)
@ -31,22 +31,38 @@ export function useZoomEvents<T extends HTMLElement>(zoom: number, ref: React.Re
}
}, [])
React.useEffect(() => {
const elm = ref.current
function handleWheel(e: WheelEvent) {
if (e.altKey) {
const point = inputs.pointer?.point ?? [inputs.bounds.width / 2, inputs.bounds.height / 2]
const info = inputs.pinch(point, point)
callbacks.onZoom?.({ ...info, delta: [...point, e.deltaY] }, e)
return
}
e.preventDefault()
if (inputs.isPinching) return
if (Vec.isEqual([e.deltaX, e.deltaY], [0, 0])) return
const info = inputs.pan([e.deltaX, e.deltaY], e as WheelEvent)
callbacks.onPan?.(info, e)
}
elm?.addEventListener('wheel', handleWheel, { passive: false })
return () => {
elm?.removeEventListener('wheel', handleWheel)
}
}, [ref, callbacks, inputs])
useGesture(
{
onWheel: ({ event: e, delta }) => {
const elm = ref.current
if (!elm || !(e.target === elm || elm.contains(e.target as Node))) return
e.preventDefault()
if (inputs.isPinching) return
if (Vec.isEqual(delta, [0, 0])) return
const info = inputs.pan(delta, e as WheelEvent)
callbacks.onPan?.(info, e)
},
onPinchStart: ({ origin, event }) => {
const elm = ref.current

View file

@ -61,6 +61,7 @@ export class Inputs {
ctrlKey,
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
altKey,
spaceKey: this.keys[' '],
}
this.pointer = info
@ -84,6 +85,7 @@ export class Inputs {
ctrlKey,
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
altKey,
spaceKey: this.keys[' '],
}
this.pointer = info
@ -116,6 +118,7 @@ export class Inputs {
ctrlKey,
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
altKey,
spaceKey: this.keys[' '],
}
this.pointer = info
@ -141,6 +144,7 @@ export class Inputs {
ctrlKey,
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
altKey,
spaceKey: this.keys[' '],
}
this.pointer = info
@ -167,6 +171,7 @@ export class Inputs {
ctrlKey,
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
altKey,
spaceKey: this.keys[' '],
}
this.pointer = info
@ -195,6 +200,7 @@ export class Inputs {
ctrlKey,
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
altKey,
spaceKey: this.keys[' '],
}
this.pointer = info
@ -225,6 +231,7 @@ export class Inputs {
ctrlKey,
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
altKey,
spaceKey: this.keys[' '],
}
this.pointer = info
@ -248,6 +255,7 @@ export class Inputs {
ctrlKey,
metaKey,
altKey,
spaceKey: this.keys[' '],
}
this.pointer = info
@ -275,6 +283,7 @@ export class Inputs {
ctrlKey,
metaKey,
altKey,
spaceKey: this.keys[' '],
}
this.pointer = info
@ -348,6 +357,7 @@ export class Inputs {
ctrlKey,
metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
altKey,
spaceKey: this.keys[' '],
}
this.pointer = info

View file

@ -265,6 +265,7 @@ export interface TLPointerInfo<T extends string = string> {
ctrlKey: boolean
metaKey: boolean
altKey: boolean
spaceKey: boolean
}
export interface TLKeyboardInfo {

View file

@ -14,7 +14,7 @@ export default function Editor(props: TLDrawProps): JSX.Element {
return (
<div className="tldraw">
<TLDraw id="tldraw" {...props} onMount={handleMount} autofocus />
<TLDraw id="tldraw1" {...props} onMount={handleMount} autofocus />
</div>
)
}

View file

@ -4,6 +4,8 @@ html,
}
body {
position: fixed;
overflow: hidden;
overscroll-behavior: none;
margin: 0px;
padding: 0px;

View file

@ -176,7 +176,7 @@ function InnerTldraw({
// Hide indicators when not using the select tool, or when in session
const hideIndicators =
(isInSession && tlstate.appState.status.current !== TLDrawStatus.Brushing) || !isSelecting
(isInSession && tlstate.appState.status !== TLDrawStatus.Brushing) || !isSelecting
// Custom rendering meta, with dark mode for shapes
const meta = React.useMemo(() => ({ isDarkMode }), [isDarkMode])
@ -215,87 +215,89 @@ function InnerTldraw({
<div ref={rWrapper} tabIndex={0}>
<div className={layout()}>
<OneOff focusableRef={rWrapper} autofocus={autofocus} />
<ContextMenu>
<Renderer
id={id}
containerRef={rWrapper}
page={page}
pageState={pageState}
users={users}
userId={tlstate.state.room?.userId}
shapeUtils={tldrawShapeUtils}
theme={theme}
meta={meta}
hideBounds={hideBounds}
hideHandles={hideHandles}
hideIndicators={hideIndicators}
onPinchStart={tlstate.onPinchStart}
onPinchEnd={tlstate.onPinchEnd}
onPinch={tlstate.onPinch}
onPan={tlstate.onPan}
onZoom={tlstate.onZoom}
onPointerDown={tlstate.onPointerDown}
onPointerMove={tlstate.onPointerMove}
onPointerUp={tlstate.onPointerUp}
onPointCanvas={tlstate.onPointCanvas}
onDoubleClickCanvas={tlstate.onDoubleClickCanvas}
onRightPointCanvas={tlstate.onRightPointCanvas}
onDragCanvas={tlstate.onDragCanvas}
onReleaseCanvas={tlstate.onReleaseCanvas}
onPointShape={tlstate.onPointShape}
onDoubleClickShape={tlstate.onDoubleClickShape}
onRightPointShape={tlstate.onRightPointShape}
onDragShape={tlstate.onDragShape}
onHoverShape={tlstate.onHoverShape}
onUnhoverShape={tlstate.onUnhoverShape}
onReleaseShape={tlstate.onReleaseShape}
onPointBounds={tlstate.onPointBounds}
onDoubleClickBounds={tlstate.onDoubleClickBounds}
onRightPointBounds={tlstate.onRightPointBounds}
onDragBounds={tlstate.onDragBounds}
onHoverBounds={tlstate.onHoverBounds}
onUnhoverBounds={tlstate.onUnhoverBounds}
onReleaseBounds={tlstate.onReleaseBounds}
onPointBoundsHandle={tlstate.onPointBoundsHandle}
onDoubleClickBoundsHandle={tlstate.onDoubleClickBoundsHandle}
onRightPointBoundsHandle={tlstate.onRightPointBoundsHandle}
onDragBoundsHandle={tlstate.onDragBoundsHandle}
onHoverBoundsHandle={tlstate.onHoverBoundsHandle}
onUnhoverBoundsHandle={tlstate.onUnhoverBoundsHandle}
onReleaseBoundsHandle={tlstate.onReleaseBoundsHandle}
onPointHandle={tlstate.onPointHandle}
onDoubleClickHandle={tlstate.onDoubleClickHandle}
onRightPointHandle={tlstate.onRightPointHandle}
onDragHandle={tlstate.onDragHandle}
onHoverHandle={tlstate.onHoverHandle}
onUnhoverHandle={tlstate.onUnhoverHandle}
onReleaseHandle={tlstate.onReleaseHandle}
onError={tlstate.onError}
onRenderCountChange={tlstate.onRenderCountChange}
onShapeChange={tlstate.onShapeChange}
onShapeBlur={tlstate.onShapeBlur}
onBoundsChange={tlstate.updateBounds}
onKeyDown={tlstate.onKeyDown}
onKeyUp={tlstate.onKeyUp}
/>
</ContextMenu>
{isFocusMode ? (
<div className={unfocusButton()}>
<button className={iconButton({ bp: breakpoints })} onClick={tlstate.toggleFocusMode}>
<DotFilledIcon />
</button>
</div>
) : (
<>
<div className={menuButtons()}>
{showMenu && <Menu />}
{showPages && <PagePanel />}
{/* <ContextMenu> */}
<Renderer
id={id}
containerRef={rWrapper}
page={page}
pageState={pageState}
users={users}
userId={tlstate.state.room?.userId}
shapeUtils={tldrawShapeUtils}
theme={theme}
meta={meta}
hideBounds={hideBounds}
hideHandles={hideHandles}
hideIndicators={hideIndicators}
onPinchStart={tlstate.onPinchStart}
onPinchEnd={tlstate.onPinchEnd}
onPinch={tlstate.onPinch}
onPan={tlstate.onPan}
onZoom={tlstate.onZoom}
onPointerDown={tlstate.onPointerDown}
onPointerMove={tlstate.onPointerMove}
onPointerUp={tlstate.onPointerUp}
onPointCanvas={tlstate.onPointCanvas}
onDoubleClickCanvas={tlstate.onDoubleClickCanvas}
onRightPointCanvas={tlstate.onRightPointCanvas}
onDragCanvas={tlstate.onDragCanvas}
onReleaseCanvas={tlstate.onReleaseCanvas}
onPointShape={tlstate.onPointShape}
onDoubleClickShape={tlstate.onDoubleClickShape}
onRightPointShape={tlstate.onRightPointShape}
onDragShape={tlstate.onDragShape}
onHoverShape={tlstate.onHoverShape}
onUnhoverShape={tlstate.onUnhoverShape}
onReleaseShape={tlstate.onReleaseShape}
onPointBounds={tlstate.onPointBounds}
onDoubleClickBounds={tlstate.onDoubleClickBounds}
onRightPointBounds={tlstate.onRightPointBounds}
onDragBounds={tlstate.onDragBounds}
onHoverBounds={tlstate.onHoverBounds}
onUnhoverBounds={tlstate.onUnhoverBounds}
onReleaseBounds={tlstate.onReleaseBounds}
onPointBoundsHandle={tlstate.onPointBoundsHandle}
onDoubleClickBoundsHandle={tlstate.onDoubleClickBoundsHandle}
onRightPointBoundsHandle={tlstate.onRightPointBoundsHandle}
onDragBoundsHandle={tlstate.onDragBoundsHandle}
onHoverBoundsHandle={tlstate.onHoverBoundsHandle}
onUnhoverBoundsHandle={tlstate.onUnhoverBoundsHandle}
onReleaseBoundsHandle={tlstate.onReleaseBoundsHandle}
onPointHandle={tlstate.onPointHandle}
onDoubleClickHandle={tlstate.onDoubleClickHandle}
onRightPointHandle={tlstate.onRightPointHandle}
onDragHandle={tlstate.onDragHandle}
onHoverHandle={tlstate.onHoverHandle}
onUnhoverHandle={tlstate.onUnhoverHandle}
onReleaseHandle={tlstate.onReleaseHandle}
onError={tlstate.onError}
onRenderCountChange={tlstate.onRenderCountChange}
onShapeChange={tlstate.onShapeChange}
onShapeBlur={tlstate.onShapeBlur}
onBoundsChange={tlstate.updateBounds}
onKeyDown={tlstate.onKeyDown}
onKeyUp={tlstate.onKeyUp}
/>
{/* </ContextMenu> */}
<div className={ui()}>
{isFocusMode ? (
<div className={unfocusButton()}>
<button className={iconButton({ bp: breakpoints })} onClick={tlstate.toggleFocusMode}>
<DotFilledIcon />
</button>
</div>
<div className={spacer()} />
<StylePanel />
<ToolsPanel />
</>
)}
) : (
<>
<div className={menuButtons()}>
{showMenu && <Menu />}
{showPages && <PagePanel />}
</div>
<div className={spacer()} />
<StylePanel />
<ToolsPanel />
</>
)}
</div>
</div>
</div>
)
@ -331,28 +333,33 @@ const layout = css({
maxHeight: '100%',
maxWidth: '100%',
overflow: 'hidden',
padding: '8px 8px 0 8px',
display: 'flex',
alignItems: 'flex-start',
justifyContent: 'flex-start',
boxSizing: 'border-box',
pointerEvents: 'none',
outline: 'none',
zIndex: 1,
border: '1px solid $blurred',
'&:focus': {
border: '1px solid $focused',
},
'& > *': {
pointerEvents: 'all',
},
'& .tl-container': {
position: 'absolute',
top: 0,
left: 0,
height: '100%',
width: '100%',
zIndex: 1,
},
})
const ui = css({
position: 'absolute',
top: 0,
left: 0,
height: '100%',
width: '100%',
padding: '8px 8px 0 8px',
display: 'flex',
alignItems: 'flex-start',
justifyContent: 'flex-start',
pointerEvents: 'none',
zIndex: 2,
'& > *': {
pointerEvents: 'all',
},
})

View file

@ -10,6 +10,7 @@ const pointsBoundsCache = new WeakMap<DrawShape['points'], TLBounds>([])
const shapeBoundsCache = new Map<string, TLBounds>()
const rotatedCache = new WeakMap<DrawShape, number[][]>([])
const pointCache: Record<string, number[]> = {}
export const Draw = new ShapeUtil<DrawShape, SVGSVGElement, TLDrawMeta>(() => ({
type: TLDrawShapeType.Draw,
@ -139,6 +140,8 @@ export const Draw = new ShapeUtil<DrawShape, SVGSVGElement, TLDrawMeta>(() => ({
return getSolidStrokePathData(shape, false)
}, [points])
if (!shape) return null
const bounds = this.getBounds(shape)
const verySmall = bounds.width < 4 && bounds.height < 4
@ -151,6 +154,8 @@ export const Draw = new ShapeUtil<DrawShape, SVGSVGElement, TLDrawMeta>(() => ({
},
getBounds(shape: DrawShape): TLBounds {
// return Utils.translateBounds(Utils.getBoundsFromPoints(shape.points), shape.point)
// The goal here is to avoid recalculating the bounds from the
// points array, which is expensive. However, we still need a
// new bounds if the point has changed, but we will reuse the