Perf: Improve text outline performance (#3429)
We use text shadows to create "outlines" around text shapes. These shadows are rendered on the GPU. In Chrome (and on computers with a capable GPU) text shadows work pretty well, however on Safari—and in particular on iOS—they cause massive frame drops. https://github.com/tldraw/tldraw/assets/23072548/b65cbcaa-6cc3-46f3-b54d-1f9cc07fc499 This PR: - adds an LOD to text shadows, removing them at < 35% zoom - removes text shadows entirely on Safari If we had a "high performance" or "low-end device" mode, then shadows / text shadows would be the first to go. ### Change Type - [x] `sdk` — Changes the tldraw SDK - [x] `improvement` — Improving existing features ### Test Plan 1. Use text shapes on iOS. 2. Use text shapes on Safari. 3. Use text shapes on Chrome. ### Release Notes - Improves performance of text shapes on iOS / Safari.
This commit is contained in:
parent
6305e83830
commit
2bbab1a790
2 changed files with 33 additions and 2 deletions
|
@ -3,9 +3,10 @@ import { TLHandle, TLShapeId } from '@tldraw/tlschema'
|
|||
import { dedupe, modulate, objectMapValues } from '@tldraw/utils'
|
||||
import classNames from 'classnames'
|
||||
import { Fragment, JSX, useEffect, useRef, useState } from 'react'
|
||||
import { COARSE_HANDLE_RADIUS, HANDLE_RADIUS } from '../../constants'
|
||||
import { COARSE_HANDLE_RADIUS, HANDLE_RADIUS, TEXT_SHADOW_LOD } from '../../constants'
|
||||
import { useCanvasEvents } from '../../hooks/useCanvasEvents'
|
||||
import { useCoarsePointer } from '../../hooks/useCoarsePointer'
|
||||
import { useContainer } from '../../hooks/useContainer'
|
||||
import { useDocumentEvents } from '../../hooks/useDocumentEvents'
|
||||
import { useEditor } from '../../hooks/useEditor'
|
||||
import { useEditorComponents } from '../../hooks/useEditorComponents'
|
||||
|
@ -37,6 +38,7 @@ export function DefaultCanvas({ className }: TLCanvasComponentProps) {
|
|||
const rCanvas = useRef<HTMLDivElement>(null)
|
||||
const rHtmlLayer = useRef<HTMLDivElement>(null)
|
||||
const rHtmlLayer2 = useRef<HTMLDivElement>(null)
|
||||
const container = useContainer()
|
||||
|
||||
useScreenBounds(rCanvas)
|
||||
useDocumentEvents()
|
||||
|
@ -45,11 +47,37 @@ export function DefaultCanvas({ className }: TLCanvasComponentProps) {
|
|||
useGestureEvents(rCanvas)
|
||||
useFixSafariDoubleTapZoomPencilEvents(rCanvas)
|
||||
|
||||
const rMemoizedStuff = useRef({ lodDisableTextOutline: false, allowTextOutline: true })
|
||||
|
||||
useQuickReactor(
|
||||
'position layers',
|
||||
function positionLayersWhenCameraMoves() {
|
||||
const { x, y, z } = editor.getCamera()
|
||||
|
||||
// This should only run once on first load
|
||||
if (rMemoizedStuff.current.allowTextOutline && editor.environment.isSafari) {
|
||||
container.style.setProperty('--tl-text-outline', 'none')
|
||||
rMemoizedStuff.current.allowTextOutline = false
|
||||
}
|
||||
|
||||
// And this should only run if we're not in Safari;
|
||||
// If we're below the lod distance for text shadows, turn them off
|
||||
if (
|
||||
rMemoizedStuff.current.allowTextOutline &&
|
||||
z < TEXT_SHADOW_LOD !== rMemoizedStuff.current.lodDisableTextOutline
|
||||
) {
|
||||
const lodDisableTextOutline = z < TEXT_SHADOW_LOD
|
||||
container.style.setProperty(
|
||||
'--tl-text-outline',
|
||||
lodDisableTextOutline
|
||||
? 'none'
|
||||
: `0 var(--b) 0 var(--color-background), 0 var(--a) 0 var(--color-background),
|
||||
var(--b) var(--b) 0 var(--color-background), var(--a) var(--b) 0 var(--color-background),
|
||||
var(--a) var(--a) 0 var(--color-background), var(--b) var(--a) 0 var(--color-background)`
|
||||
)
|
||||
rMemoizedStuff.current.lodDisableTextOutline = lodDisableTextOutline
|
||||
}
|
||||
|
||||
// Because the html container has a width/height of 1px, we
|
||||
// need to create a small offset when zoomed to ensure that
|
||||
// the html container and svg container are lined up exactly.
|
||||
|
@ -62,7 +90,7 @@ export function DefaultCanvas({ className }: TLCanvasComponentProps) {
|
|||
setStyleProperty(rHtmlLayer.current, 'transform', transform)
|
||||
setStyleProperty(rHtmlLayer2.current, 'transform', transform)
|
||||
},
|
||||
[editor]
|
||||
[editor, container]
|
||||
)
|
||||
|
||||
const events = useCanvasEvents()
|
||||
|
|
|
@ -107,3 +107,6 @@ export const HANDLE_RADIUS = 12
|
|||
|
||||
/** @internal */
|
||||
export const LONG_PRESS_DURATION = 500
|
||||
|
||||
/** @internal */
|
||||
export const TEXT_SHADOW_LOD = 0.35
|
||||
|
|
Loading…
Reference in a new issue