This commit is contained in:
Steve Ruiz 2021-12-27 12:32:17 +00:00
commit d1fb7d1115
32 changed files with 158 additions and 29 deletions

View file

@ -201,6 +201,7 @@ export default function App({ onMount }: AppProps): JSX.Element {
shapeUtils={shapeUtils} // Required
page={appState.data.page} // Required
pageState={appState.data.pageState} // Required
performanceMode={appState.data.performanceMode}
meta={appState.data.meta}
snapLines={appState.data.overlays.snapLines}
onPointShape={onPointShape}

View file

@ -11,3 +11,4 @@ export * from './snaps'
export * from './transform'
export * from './translate'
export * from './mutables'
export * from './performance'

View file

@ -0,0 +1,5 @@
import type { Action } from 'state/constants'
export const clearPerformanceMode: Action = (data) => {
data.performanceMode = undefined
}

View file

@ -0,0 +1,3 @@
export * from './clearPerformanceMode'
export * from './setTranslatePerformanceMode'
export * from './setTransformPerformanceMode'

View file

@ -0,0 +1,6 @@
import { TLPerformanceMode } from '@tldraw/core'
import type { Action } from 'state/constants'
export const setTransformPerformanceMode: Action = (data) => {
data.performanceMode = TLPerformanceMode.TransformSelected
}

View file

@ -0,0 +1,6 @@
import { TLPerformanceMode } from '@tldraw/core'
import type { Action } from 'state/constants'
export const setTranslatePerformanceMode: Action = (data) => {
data.performanceMode = TLPerformanceMode.TranslateSelected
}

View file

@ -1,4 +1,4 @@
import type { TLBinding, TLPage, TLPageState, TLSnapLine } from '@tldraw/core'
import type { TLBinding, TLPage, TLPageState, TLPerformanceMode, TLSnapLine } from '@tldraw/core'
import type { Shape } from '../shapes'
import type { S } from '@state-designer/react'
@ -104,6 +104,7 @@ export const INITIAL_DATA = {
meta: {
isDarkMode: false,
},
performanceMode: undefined as TLPerformanceMode | undefined,
}
export type AppDocument = {

View file

@ -2,7 +2,6 @@ import { createState } from '@state-designer/react'
import type { TLPointerInfo } from '@tldraw/core'
import { INITIAL_DATA } from './constants'
import Vec from '@tldraw/vec'
import { getPagePoint } from './helpers'
import * as actions from './actions'
import { mutables } from './mutables'
@ -42,7 +41,7 @@ export const machine = createState({
initial: 'idle',
states: {
idle: {
onEnter: ['clearPointedShape'],
onEnter: ['clearPointedShape', 'clearPerformanceMode'],
on: {
SELECTED_ALL: 'selectAllShapes',
DESELECTED_ALL: 'deselectAllShapes',
@ -149,7 +148,7 @@ export const machine = createState({
},
},
translating: {
onEnter: 'setSnapInfo',
onEnter: ['setSnapInfo', 'setTranslatePerformanceMode'],
onExit: ['clearSnapInfo', 'clearSnapLines', 'clearIsCloning'],
on: {
CANCELLED: {
@ -181,7 +180,7 @@ export const machine = createState({
},
},
transforming: {
onEnter: ['setSnapInfo', 'setInitialCommonBounds'],
onEnter: ['setSnapInfo', 'setInitialCommonBounds', 'setTransformPerformanceMode'],
onExit: ['clearSnapInfo', 'clearSnapLines', 'clearPointedBoundsHandle'],
on: {
TOGGLED_MODIFIER: ['transformSelectedShapes', 'updateBoundShapes'],
@ -260,6 +259,7 @@ export const machine = createState({
},
},
pencil: {
onEnter: 'setTransformPerformanceMode',
initial: 'idle',
states: {
idle: {
@ -294,6 +294,7 @@ export const machine = createState({
},
},
box: {
onEnter: 'setTransformPerformanceMode',
initial: 'idle',
states: {
idle: {
@ -334,6 +335,7 @@ export const machine = createState({
},
},
arrow: {
onEnter: 'setTransformPerformanceMode',
initial: 'idle',
states: {
idle: {

View file

@ -2,12 +2,13 @@
import { observer } from 'mobx-react-lite'
import * as React from 'react'
import {
usePreventNavigation,
usePreventNavigationCss,
useZoomEvents,
useSafariFocusOutFix,
useCanvasEvents,
useCameraCss,
useKeyEvents,
usePerformanceCss,
} from '~hooks'
import type {
TLAssets,
@ -15,6 +16,7 @@ import type {
TLBounds,
TLPage,
TLPageState,
TLPerformanceMode,
TLShape,
TLSnapLine,
TLUsers,
@ -46,6 +48,7 @@ interface CanvasProps<T extends TLShape, M extends Record<string, unknown>> {
hideRotateHandle: boolean
hideGrid: boolean
externalContainerRef?: React.RefObject<HTMLElement>
performanceMode?: TLPerformanceMode
meta?: M
id?: string
onBoundsChange: (bounds: TLBounds) => void
@ -64,6 +67,7 @@ export const Canvas = observer(function _Canvas<
users,
userId,
meta,
performanceMode,
externalContainerRef,
hideHandles,
hideBounds,
@ -87,10 +91,12 @@ export const Canvas = observer(function _Canvas<
useSafariFocusOutFix()
usePreventNavigation(rCanvas)
usePreventNavigationCss(rCanvas)
useCameraCss(rLayer, rContainer, pageState)
usePerformanceCss(performanceMode, rContainer)
useKeyEvents()
const events = useCanvasEvents()

View file

@ -1,5 +1,5 @@
import { observer } from 'mobx-react-lite'
import type {HTMLProps} from 'react'
import type { HTMLProps } from 'react'
import * as React from 'react'
import type { TLBounds } from '~types'
import { usePosition } from '~hooks'
@ -7,8 +7,9 @@ import { usePosition } from '~hooks'
interface ContainerProps extends HTMLProps<HTMLDivElement> {
id?: string
bounds: TLBounds
isGhost?: boolean
rotation?: number
isGhost?: boolean
isSelected?: boolean
children: React.ReactNode
}
@ -17,6 +18,7 @@ export const Container = observer<ContainerProps>(function Container({
bounds,
rotation = 0,
isGhost = false,
isSelected = false,
children,
...props
}) {
@ -26,7 +28,9 @@ export const Container = observer<ContainerProps>(function Container({
<div
id={id}
ref={rPositioned}
className={isGhost ? 'tl-positioned tl-ghost' : 'tl-positioned'}
className={`tl-positioned${isGhost ? ' tl-ghost' : ''}${
isSelected ? ` tl-positioned-selected` : ''
}`}
aria-label="container"
data-testid="container"
{...props}

View file

@ -12,6 +12,7 @@ import type {
TLSnapLine,
TLUsers,
TLAssets,
TLPerformanceMode,
} from '../../types'
import { Canvas } from '../Canvas'
import { Inputs } from '../../inputs'
@ -34,7 +35,7 @@ export interface RendererProps<T extends TLShape, M = any> extends Partial<TLCal
/**
* A map of assets to be used in the renderer.
*/
assets: TLAssets
assets?: TLAssets
/**
* (optional) A unique id to be applied to the renderer element, used to scope styles.
*/
@ -100,6 +101,10 @@ export interface RendererProps<T extends TLShape, M = any> extends Partial<TLCal
* (optional) The size of the grid step.
*/
grid?: number
/**
* (optional) Use a performance mode.
*/
performanceMode?: TLPerformanceMode
/**
* (optional) A callback that receives the renderer's inputs manager.
*/
@ -126,7 +131,7 @@ export const Renderer = observer(function _Renderer<
shapeUtils,
page,
pageState,
assets,
assets = EMPTY_OBJECT,
users,
userId,
theme,
@ -134,6 +139,7 @@ export const Renderer = observer(function _Renderer<
snapLines,
grid,
containerRef,
performanceMode,
hideHandles = false,
hideIndicators = false,
hideCloneHandles = false,
@ -198,8 +204,11 @@ export const Renderer = observer(function _Renderer<
hideResizeHandles={hideResizeHandles}
hideGrid={hideGrid}
onBoundsChange={onBoundsChange}
performanceMode={performanceMode}
meta={meta}
/>
</TLContext.Provider>
)
})
const EMPTY_OBJECT = {} as TLAssets

View file

@ -22,7 +22,14 @@ export const Shape = observer(function Shape<T extends TLShape, E extends Elemen
const events = useShapeEvents(shape.id)
return (
<Container id={shape.id} bounds={bounds} rotation={shape.rotation} data-shape={shape.type}>
<Container
id={shape.id}
bounds={bounds}
rotation={shape.rotation}
data-shape={shape.type}
isGhost={rest.isGhost}
isSelected={rest.isSelected}
>
<RenderedShape
shape={shape}
utils={utils as any}

View file

@ -11,8 +11,9 @@ export * from './useCameraCss'
export * from './useSelection'
export * from './useHandleEvents'
export * from './useHandles'
export * from './usePreventNavigation'
export * from './usePreventNavigationCss'
export * from './useBoundsEvents'
export * from './usePosition'
export * from './useKeyEvents'
export * from './useCursorAnimation'
export * from './usePerformanceCss'

View file

@ -0,0 +1,40 @@
import * as React from 'react'
import { TLPerformanceMode } from '~types'
export function usePerformanceCss(
performanceMode: TLPerformanceMode | undefined,
rContainer: React.ForwardedRef<HTMLDivElement>
) {
React.useLayoutEffect(() => {
if (rContainer && 'current' in rContainer) {
const container = rContainer?.current
if (!container) return
switch (performanceMode) {
case TLPerformanceMode.TransformSelected: {
container.style.setProperty('--tl-performance-all', 'auto')
container.style.setProperty('--tl-performance-selected', 'transform, contents')
break
}
case TLPerformanceMode.TransformAll: {
container.style.setProperty('--tl-performance-all', 'transform, contents')
container.style.setProperty('--tl-performance-selected', 'transform, contents')
break
}
case TLPerformanceMode.TranslateSelected: {
container.style.setProperty('--tl-performance-all', 'auto')
container.style.setProperty('--tl-performance-selected', 'transform')
break
}
case TLPerformanceMode.TranslateAll: {
container.style.setProperty('--tl-performance-all', 'transform')
container.style.setProperty('--tl-performance-selected', 'transform')
break
}
default: {
container.style.setProperty('--tl-performance-all', 'auto')
container.style.setProperty('--tl-performance-selected', 'auto')
}
}
}
}, [performanceMode])
}

View file

@ -2,7 +2,7 @@
import * as React from 'react'
import { useTLContext } from './useTLContext'
export function usePreventNavigation(rCanvas: React.RefObject<HTMLDivElement>): void {
export function usePreventNavigationCss(rCanvas: React.RefObject<HTMLDivElement>): void {
const { bounds } = useTLContext()
React.useEffect(() => {

View file

@ -115,6 +115,8 @@ const tlcss = css`
--tl-zoom: 1;
--tl-scale: calc(1 / var(--tl-zoom));
--tl-padding: calc(64px * max(1, var(--tl-scale)));
--tl-performance-all: auto;
--tl-performance-selected: auto;
position: relative;
top: 0px;
left: 0px;
@ -200,6 +202,7 @@ const tlcss = css`
justify-content: center;
overflow: hidden;
contain: layout style size;
will-change: var(--tl-performance-all);
}
.tl-positioned-svg {
@ -219,6 +222,10 @@ const tlcss = css`
contain: layout style size;
}
.tl-positioned-selected {
will-change: var(--tl-performance-selected);
}
.tl-inner-div {
position: relative;
width: 100%;
@ -327,6 +334,10 @@ const tlcss = css`
stroke-width: calc(2.5px * min(5, var(--tl-scale)));
}
.tl-performance {
will-change: transform, contents;
}
.tl-clone-target {
pointer-events: all;
}

View file

@ -239,7 +239,7 @@ export class Inputs {
this.pointer = info
this.pointerUpTime = Date.now()
this.pointerUpTime = performance.now()
return info
}
@ -300,7 +300,7 @@ export class Inputs {
const { origin, point } = this.pointer
const isDoubleClick =
Date.now() - this.pointerUpTime < DOUBLE_CLICK_DURATION && Vec.dist(origin, point) < 4
performance.now() - this.pointerUpTime < DOUBLE_CLICK_DURATION && Vec.dist(origin, point) < 4
// Reset the active pointer, in case it got stuck
if (isDoubleClick) this.activePointer = undefined

View file

@ -4,6 +4,13 @@
import type React from 'react'
export enum TLPerformanceMode {
TransformSelected = 'transform_selected',
TranslateSelected = 'translate_selected',
TransformAll = 'transform_all',
TranslateAll = 'translate_all',
}
export type TLAssets = Record<string, TLAsset>
export interface TLAsset {

View file

@ -473,6 +473,7 @@ const InnerTldraw = React.memo(function InnerTldraw({
hideCloneHandles={hideCloneHandles}
hideRotateHandles={!settings.showRotateHandles}
hideGrid={!settings.showGrid}
performanceMode={app.session?.performanceMode}
onPinchStart={app.onPinchStart}
onPinchEnd={app.onPinchEnd}
onPinch={app.onPinch}

View file

@ -15,6 +15,7 @@ import {
Utils,
TLBounds,
TLDropEventHandler,
TLPerformanceMode,
} from '@tldraw/core'
import {
FlipType,
@ -858,7 +859,7 @@ export class TldrawApp extends StateManager<TDSnapshot> {
setEditingId = (id?: string) => {
if (this.readOnly) return
this.editingStartTime = Date.now()
this.editingStartTime = performance.now()
this.patchState(
{
document: {
@ -3266,7 +3267,7 @@ export class TldrawApp extends StateManager<TDSnapshot> {
onShapeBlur = () => {
// This prevents an auto-blur event from Safari
if (Date.now() - this.editingStartTime < 50) return
if (performance.now() - this.editingStartTime < 50) return
const { editingId } = this.pageState
const { isToolLocked } = this.getAppState()

View file

@ -13,6 +13,7 @@ const checkPermissions = async (handle: FileSystemHandle) => {
}
export async function loadFileHandle() {
if (typeof Window === 'undefined' || !('_location' in Window)) return
const fileHandle = await getFromIdb(`Tldraw_file_handle_${window.location.origin}`)
if (!fileHandle) return null
return fileHandle

View file

@ -14,10 +14,11 @@ import { TLDR } from '~state/TLDR'
import { shapeUtils } from '~state/shapes'
import { BaseSession } from '../BaseSession'
import type { TldrawApp } from '../../internal'
import { Utils } from '@tldraw/core'
import { TLPerformanceMode, Utils } from '@tldraw/core'
export class ArrowSession extends BaseSession {
type = SessionType.Arrow
performanceMode = TLPerformanceMode.TransformSelected
status = TDStatus.TranslatingHandle
newStartBindingId = Utils.uniqueId()
draggedBindingId = Utils.uniqueId()

View file

@ -1,16 +1,13 @@
import type { TLPerformanceMode } from '@tldraw/core'
import type { SessionType, TldrawCommand, TldrawPatch } from '~types'
import type { TldrawApp } from '../internal'
export abstract class BaseSession {
abstract type: SessionType
abstract performanceMode: TLPerformanceMode | undefined
constructor(public app: TldrawApp) {}
abstract start: () => TldrawPatch | undefined
abstract update: () => TldrawPatch | undefined
abstract complete: () => TldrawPatch | TldrawCommand | undefined
abstract cancel: () => TldrawPatch | undefined
}

View file

@ -5,6 +5,7 @@ import { BaseSession } from '../BaseSession'
export class BrushSession extends BaseSession {
type = SessionType.Brush
performanceMode = undefined
status = TDStatus.Brushing
initialSelectedIds: Set<string>
shapesToTest: {

View file

@ -1,4 +1,4 @@
import { Utils } from '@tldraw/core'
import { TLPerformanceMode, Utils } from '@tldraw/core'
import { Vec } from '@tldraw/vec'
import { SessionType, TDStatus, TldrawPatch, TldrawCommand, DrawShape } from '~types'
import type { TldrawApp } from '../../internal'
@ -6,6 +6,7 @@ import { BaseSession } from '../BaseSession'
export class DrawSession extends BaseSession {
type = SessionType.Draw
performanceMode = TLPerformanceMode.TransformSelected
status = TDStatus.Creating
topLeft: number[]
points: number[][]

View file

@ -13,6 +13,7 @@ import { BaseSession } from '../BaseSession'
export class EraseSession extends BaseSession {
type = SessionType.Draw
performanceMode = undefined
status = TDStatus.Creating
isLocked?: boolean
lockedDirection?: 'horizontal' | 'vertical'

View file

@ -15,6 +15,7 @@ import type { TldrawApp } from '../../internal'
export class GridSession extends BaseSession {
type = SessionType.Grid
performanceMode = undefined
status = TDStatus.Translating
shape: TDShape
bounds: TLBounds

View file

@ -3,9 +3,11 @@ import { SessionType, ShapesWithProp, TldrawCommand, TldrawPatch, TDStatus } fro
import { TLDR } from '~state/TLDR'
import { BaseSession } from '../BaseSession'
import type { TldrawApp } from '../../internal'
import { TLPerformanceMode } from '@tldraw/core'
export class HandleSession extends BaseSession {
type = SessionType.Handle
performanceMode = TLPerformanceMode.TransformSelected
status = TDStatus.TranslatingHandle
commandId: string
topLeft: number[]

View file

@ -1,4 +1,4 @@
import { Utils } from '@tldraw/core'
import { TLPerformanceMode, Utils } from '@tldraw/core'
import { Vec } from '@tldraw/vec'
import { SessionType, TldrawCommand, TldrawPatch, TDShape, TDStatus } from '~types'
import { TLDR } from '~state/TLDR'
@ -8,6 +8,7 @@ import type { TldrawApp } from '../../internal'
export class RotateSession extends BaseSession {
type = SessionType.Rotate
status = TDStatus.Transforming
performanceMode = TLPerformanceMode.TranslateSelected
delta = [0, 0]
commonBoundsCenter: number[]
initialAngle: number

View file

@ -1,4 +1,4 @@
import { TLBounds, TLBoundsCorner, TLBoundsEdge, Utils } from '@tldraw/core'
import { TLBounds, TLBoundsCorner, TLBoundsEdge, TLPerformanceMode, Utils } from '@tldraw/core'
import { Vec } from '@tldraw/vec'
import type { TLSnapLine, TLBoundsWithCenter } from '@tldraw/core'
import { SessionType, TldrawCommand, TldrawPatch, TDShape, TDStatus } from '~types'
@ -18,6 +18,7 @@ type SnapInfo =
export class TransformSession extends BaseSession {
type = SessionType.Transform
performanceMode = TLPerformanceMode.TransformSelected
status = TDStatus.Transforming
scaleX = 1
scaleY = 1

View file

@ -5,6 +5,7 @@ import {
Utils,
TLBoundsWithCenter,
TLBounds,
TLPerformanceMode,
} from '@tldraw/core'
import { Vec } from '@tldraw/vec'
import { SessionType, TldrawCommand, TldrawPatch, TDShape, TDStatus } from '~types'
@ -25,6 +26,7 @@ type SnapInfo =
export class TransformSingleSession extends BaseSession {
type = SessionType.TransformSingle
status = TDStatus.Transforming
performanceMode = TLPerformanceMode.TransformSelected
transformType: TLBoundsEdge | TLBoundsCorner
scaleX = 1
scaleY = 1

View file

@ -1,5 +1,12 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { TLPageState, Utils, TLBoundsWithCenter, TLSnapLine, TLBounds } from '@tldraw/core'
import {
TLPageState,
Utils,
TLBoundsWithCenter,
TLSnapLine,
TLBounds,
TLPerformanceMode,
} from '@tldraw/core'
import { Vec } from '@tldraw/vec'
import {
TDShape,
@ -41,6 +48,7 @@ type SnapInfo =
}
export class TranslateSession extends BaseSession {
performanceMode = TLPerformanceMode.TranslateSelected
type = SessionType.Translate
status = TDStatus.Translating
delta = [0, 0]