Skip to content

Commit

Permalink
fix: optimize dot grid svg rendering in Safari
Browse files Browse the repository at this point in the history
  • Loading branch information
pengx17 committed Nov 18, 2022
1 parent b495205 commit a72bb8b
Show file tree
Hide file tree
Showing 2 changed files with 8 additions and 91 deletions.
97 changes: 7 additions & 90 deletions tldraw/packages/react/src/components/ui/Grid/Grid.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { isSafari, modulate } from '@tldraw/core'
import { modulate, clamp } from '@tldraw/core'
import { observer } from 'mobx-react-lite'
import { transparentize } from 'polished'
import React from 'react'
import { useRendererContext } from '../../../hooks'
import type { TLGridProps } from '../../../types'

Expand All @@ -27,7 +25,9 @@ const SVGGrid = observer(function CanvasGrid({ size }: TLGridProps) {
const yo = point[1] * zoom
const gxo = xo > 0 ? xo % s : s + (xo % s)
const gyo = yo > 0 ? yo % s : s + (yo % s)
const opacity = zoom < mid ? modulate(zoom, [min, mid], [0, 1]) : 1
const opacity = modulate(zoom, [min, mid], [0, 1])

const hide = opacity > 2 || opacity < 0.1

return (
<pattern
Expand All @@ -37,7 +37,9 @@ const SVGGrid = observer(function CanvasGrid({ size }: TLGridProps) {
height={s}
patternUnits="userSpaceOnUse"
>
<circle className={`tl-grid-dot`} cx={gxo} cy={gyo} r={1.5} opacity={opacity} />
{!hide && (
<circle className={`tl-grid-dot`} cx={gxo} cy={gyo} r={1.5} opacity={clamp(opacity, 0, 1)} />
)}
</pattern>
)
})}
Expand All @@ -49,91 +51,6 @@ const SVGGrid = observer(function CanvasGrid({ size }: TLGridProps) {
)
})

// Grid is slow to render. Maybe we render it using canvas?
const CanvasGrid = observer(function Grid({ size }: TLGridProps) {
const {
viewport: {
camera: { point, zoom },
bounds,
},
} = useRendererContext()

const ref = React.useRef<HTMLCanvasElement>(null)

// Use useEffect will cause the render flickering
React.useLayoutEffect(() => {
if (ref.current) {
const canvas = ref.current
if (canvas?.getContext) {
const fillColor = getComputedStyle(canvas)
.getPropertyValue('--ls-quaternary-background-color')
.trim()
const ctx = canvas.getContext('2d')
if (ctx && fillColor) {
const { width, height } = canvas
// fill the canvas with dots
ctx.clearRect(0, 0, width, height)

const xo = point[0] * zoom
const yo = point[1] * zoom

STEPS.forEach(([min, mid, _size]) => {
const s = _size * size * zoom
const gxo = xo > 0 ? xo % s : s + (xo % s)
const gyo = yo > 0 ? yo % s : s + (yo % s)
const opacity = zoom < mid ? modulate(zoom, [min, mid], [0, 1], true) : 1
ctx.fillStyle = transparentize(1 - opacity, fillColor)

if (opacity < 0.5 || s < 32) return
for (let i = gyo; i < height; i += s) {
for (let j = gxo; j < width; j += s) {
ctx.beginPath()
ctx.arc(j, i, 1.5, 0, 2 * Math.PI)
ctx.closePath()
ctx.fill()
}
}
// Pattern should have better performance, but I cannot make the offset correctly ...
// for (let i = 0; i < height; i += _size) {
// const y = i * _size
// for (let j = 0; j < width; j += _size) {
// const x = j * _size
// ctx.fillRect(x, y, _size, _size)
// }
// }
// const pattern = document.createElement('canvas').getContext('2d')
// if (pattern) {
// const s = _size * size * zoom
// if (s < 1) {
// return
// }
// const xo = point[0] * zoom
// const yo = point[1] * zoom
// const gxo = xo > 0 ? xo % s : s + (xo % s)
// const gyo = yo > 0 ? yo % s : s + (yo % s)
// const opacity = zoom < mid ? modulate(zoom, [min, mid], [0, 1]) : 1
// pattern.canvas.width = s
// pattern.canvas.height = s
// pattern.beginPath()
// pattern.arc(gxo, gyo, 1.5, 0, 2 * Math.PI)
// pattern.fillStyle = transparentize(1 - opacity, fillColor)
// pattern.fill()
// pattern.closePath()
// ctx.fillStyle = ctx.createPattern(pattern.canvas, 'repeat')!
// ctx.fillRect(0, 0, width, height)
// }
})
}
}
}
}, [point[0], point[1], zoom, bounds.width, bounds.height])

return <canvas ref={ref} width={bounds.width} height={bounds.height} className="tl-grid-canvas" />
})

export const Grid = observer(function Grid({ size }: TLGridProps) {
if (isSafari()) {
return <CanvasGrid size={size} />
}
return <SVGGrid size={size} />
})
2 changes: 1 addition & 1 deletion tldraw/packages/react/src/hooks/useGestureEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function useGestureEvents(ref: React.RefObject<HTMLDivElement>) {

const [x, y, z] = normalizeWheel(event)

if (inputs.state === 'pinching' || rWheelTs.current === event.timeStamp) {
if (inputs.state === 'pinching' || rWheelTs.current >= event.timeStamp) {
return
}

Expand Down

0 comments on commit a72bb8b

Please sign in to comment.