[fix] minimap, common page bounds (#1770)
This PR fixes a bug that was introduced (by me) in #1751, where: - the `commonBoundsOfAllShapesOnCurrentPage` would mutate the first bounding box - the render reactor would fire too often ### Change Type - [x] `patch` — Bug fix ### Test Plan 1. Use the minimap
This commit is contained in:
parent
7e4fb59a48
commit
1014a71abb
3 changed files with 29 additions and 47 deletions
|
@ -4227,7 +4227,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
const bounds = this.getMaskedPageBounds(shapeId)
|
const bounds = this.getMaskedPageBounds(shapeId)
|
||||||
if (!bounds) return
|
if (!bounds) return
|
||||||
if (!commonBounds) {
|
if (!commonBounds) {
|
||||||
commonBounds = bounds
|
commonBounds = bounds.clone()
|
||||||
} else {
|
} else {
|
||||||
commonBounds = commonBounds.expand(bounds)
|
commonBounds = commonBounds.expand(bounds)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ import {
|
||||||
intersectPolygonPolygon,
|
intersectPolygonPolygon,
|
||||||
normalizeWheel,
|
normalizeWheel,
|
||||||
setPointerCapture,
|
setPointerCapture,
|
||||||
track,
|
useComputed,
|
||||||
useContainer,
|
|
||||||
useEditor,
|
useEditor,
|
||||||
|
useIsDarkMode,
|
||||||
useQuickReactor,
|
useQuickReactor,
|
||||||
} from '@tldraw/editor'
|
} from '@tldraw/editor'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
|
@ -22,30 +22,22 @@ export interface MinimapProps {
|
||||||
viewportFill: string
|
viewportFill: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Minimap = track(function Minimap({
|
export function Minimap({ shapeFill, selectFill, viewportFill }: MinimapProps) {
|
||||||
shapeFill,
|
|
||||||
selectFill,
|
|
||||||
viewportFill,
|
|
||||||
}: MinimapProps) {
|
|
||||||
const editor = useEditor()
|
const editor = useEditor()
|
||||||
|
|
||||||
const rCanvas = React.useRef<HTMLCanvasElement>(null!)
|
const rCanvas = React.useRef<HTMLCanvasElement>(null!)
|
||||||
|
|
||||||
const container = useContainer()
|
|
||||||
|
|
||||||
const rPointing = React.useRef(false)
|
const rPointing = React.useRef(false)
|
||||||
|
|
||||||
const minimap = React.useMemo(
|
const isDarkMode = useIsDarkMode()
|
||||||
() => new MinimapManager(editor, editor.instanceState.devicePixelRatio),
|
const devicePixelRatio = useComputed('dpr', () => editor.instanceState.devicePixelRatio, [editor])
|
||||||
[editor]
|
const presences = React.useMemo(() => editor.store.query.records('instance_presence'), [editor])
|
||||||
)
|
|
||||||
|
|
||||||
const isDarkMode = editor.user.isDarkMode
|
const minimap = React.useMemo(() => new MinimapManager(editor), [editor])
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
// Must check after render
|
// Must check after render
|
||||||
const raf = requestAnimationFrame(() => {
|
const raf = requestAnimationFrame(() => {
|
||||||
const style = getComputedStyle(container)
|
const style = getComputedStyle(editor.getContainer())
|
||||||
|
|
||||||
minimap.colors = {
|
minimap.colors = {
|
||||||
shapeFill: style.getPropertyValue(shapeFill).trim(),
|
shapeFill: style.getPropertyValue(shapeFill).trim(),
|
||||||
|
@ -58,7 +50,7 @@ export const Minimap = track(function Minimap({
|
||||||
return () => {
|
return () => {
|
||||||
cancelAnimationFrame(raf)
|
cancelAnimationFrame(raf)
|
||||||
}
|
}
|
||||||
}, [container, selectFill, shapeFill, viewportFill, minimap, isDarkMode])
|
}, [editor, selectFill, shapeFill, viewportFill, minimap, isDarkMode])
|
||||||
|
|
||||||
const onDoubleClick = React.useCallback(
|
const onDoubleClick = React.useCallback(
|
||||||
(e: React.MouseEvent<HTMLCanvasElement>) => {
|
(e: React.MouseEvent<HTMLCanvasElement>) => {
|
||||||
|
@ -163,17 +155,15 @@ export const Minimap = track(function Minimap({
|
||||||
|
|
||||||
// Update the minimap's dpr when the dpr changes
|
// Update the minimap's dpr when the dpr changes
|
||||||
useQuickReactor(
|
useQuickReactor(
|
||||||
'update dpr',
|
'update when dpr changes',
|
||||||
() => {
|
() => {
|
||||||
const {
|
const dpr = devicePixelRatio.value
|
||||||
instanceState: { devicePixelRatio },
|
minimap.setDpr(dpr)
|
||||||
} = editor
|
|
||||||
minimap.setDpr(devicePixelRatio)
|
|
||||||
|
|
||||||
const canvas = rCanvas.current as HTMLCanvasElement
|
const canvas = rCanvas.current as HTMLCanvasElement
|
||||||
const rect = canvas.getBoundingClientRect()
|
const rect = canvas.getBoundingClientRect()
|
||||||
const width = rect.width * devicePixelRatio
|
const width = rect.width * dpr
|
||||||
const height = rect.height * devicePixelRatio
|
const height = rect.height * dpr
|
||||||
|
|
||||||
// These must happen in order
|
// These must happen in order
|
||||||
canvas.width = width
|
canvas.width = width
|
||||||
|
@ -182,26 +172,19 @@ export const Minimap = track(function Minimap({
|
||||||
|
|
||||||
minimap.cvs = rCanvas.current
|
minimap.cvs = rCanvas.current
|
||||||
},
|
},
|
||||||
[editor, minimap]
|
[devicePixelRatio, minimap]
|
||||||
)
|
)
|
||||||
|
|
||||||
const presences = React.useMemo(() => {
|
|
||||||
return editor.store.query.records('instance_presence')
|
|
||||||
}, [editor])
|
|
||||||
|
|
||||||
useQuickReactor(
|
useQuickReactor(
|
||||||
'minimap render when pagebounds or collaborators changes',
|
'minimap render when pagebounds or collaborators changes',
|
||||||
() => {
|
() => {
|
||||||
const {
|
const { shapeIdsOnCurrentPage, viewportPageBounds, commonBoundsOfAllShapesOnCurrentPage } =
|
||||||
instanceState: { devicePixelRatio },
|
editor
|
||||||
viewportPageBounds,
|
|
||||||
commonBoundsOfAllShapesOnCurrentPage: allShapesCommonBounds,
|
|
||||||
} = editor
|
|
||||||
|
|
||||||
devicePixelRatio // dereference dpr so that it renders then, too
|
const _dpr = devicePixelRatio.value
|
||||||
|
|
||||||
minimap.contentPageBounds = allShapesCommonBounds
|
minimap.contentPageBounds = commonBoundsOfAllShapesOnCurrentPage
|
||||||
? Box2d.Expand(allShapesCommonBounds, viewportPageBounds)
|
? Box2d.Expand(commonBoundsOfAllShapesOnCurrentPage, viewportPageBounds)
|
||||||
: viewportPageBounds
|
: viewportPageBounds
|
||||||
|
|
||||||
minimap.updateContentScreenBounds()
|
minimap.updateContentScreenBounds()
|
||||||
|
@ -210,8 +193,9 @@ export const Minimap = track(function Minimap({
|
||||||
|
|
||||||
const allShapeBounds = [] as (Box2d & { id: TLShapeId })[]
|
const allShapeBounds = [] as (Box2d & { id: TLShapeId })[]
|
||||||
|
|
||||||
editor.shapeIdsOnCurrentPage.forEach((id) => {
|
shapeIdsOnCurrentPage.forEach((id) => {
|
||||||
let pageBounds = editor.getPageBounds(id)! as Box2d & { id: TLShapeId }
|
let pageBounds = editor.getPageBounds(id) as Box2d & { id: TLShapeId }
|
||||||
|
if (!pageBounds) return
|
||||||
|
|
||||||
const pageMask = editor.getPageMask(id)
|
const pageMask = editor.getPageMask(id)
|
||||||
|
|
||||||
|
@ -230,11 +214,7 @@ export const Minimap = track(function Minimap({
|
||||||
})
|
})
|
||||||
|
|
||||||
minimap.pageBounds = allShapeBounds
|
minimap.pageBounds = allShapeBounds
|
||||||
|
|
||||||
// Collaborators
|
|
||||||
|
|
||||||
minimap.collaborators = presences.value
|
minimap.collaborators = presences.value
|
||||||
|
|
||||||
minimap.render()
|
minimap.render()
|
||||||
},
|
},
|
||||||
[editor, minimap]
|
[editor, minimap]
|
||||||
|
@ -253,4 +233,4 @@ export const Minimap = track(function Minimap({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
|
|
@ -10,7 +10,9 @@ import {
|
||||||
} from '@tldraw/editor'
|
} from '@tldraw/editor'
|
||||||
|
|
||||||
export class MinimapManager {
|
export class MinimapManager {
|
||||||
constructor(public editor: Editor, private dpr: number) {}
|
constructor(public editor: Editor) {}
|
||||||
|
|
||||||
|
dpr = 1
|
||||||
|
|
||||||
colors = {
|
colors = {
|
||||||
shapeFill: 'rgba(144, 144, 144, .1)',
|
shapeFill: 'rgba(144, 144, 144, .1)',
|
||||||
|
@ -212,7 +214,6 @@ export class MinimapManager {
|
||||||
const by = ry / 4
|
const by = ry / 4
|
||||||
|
|
||||||
// shapes
|
// shapes
|
||||||
|
|
||||||
const shapesPath = new Path2D()
|
const shapesPath = new Path2D()
|
||||||
const selectedPath = new Path2D()
|
const selectedPath = new Path2D()
|
||||||
|
|
||||||
|
@ -234,6 +235,7 @@ export class MinimapManager {
|
||||||
clamp(ry, ay, pb.height / by)
|
clamp(ry, ay, pb.height / by)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill the shapes paths
|
// Fill the shapes paths
|
||||||
ctx.fillStyle = shapeFill
|
ctx.fillStyle = shapeFill
|
||||||
ctx.fill(shapesPath)
|
ctx.fill(shapesPath)
|
||||||
|
|
Loading…
Reference in a new issue