Add fog of war example (#4025)

This PR adds a cute fog of war example.

![Kapture 2024-06-26 at 20 47
37](https://github.com/tldraw/tldraw/assets/23072548/b426776f-b027-419e-9770-29f5c7ab2563)

### Change Type

- [x] `docs` 
- [x] `improvement` 

### Release Notes

- Adds fog of war example.
This commit is contained in:
Steve Ruiz 2024-06-26 19:06:51 +01:00 committed by GitHub
parent d686b1f0c5
commit 3d07262e20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 116 additions and 0 deletions

View file

@ -0,0 +1,106 @@
import { useEffect, useRef } from 'react'
import { Box, TLComponents, Tldraw, Vec, useEditor, useReactor } from 'tldraw'
import 'tldraw/tldraw.css'
const CELL_SIZE = 32
const COUNT = 100
const boxes: Box[][] = []
const cells: boolean[][] = []
for (let i = 0; i < COUNT; i++) {
cells[i] = []
boxes[i] = []
for (let j = 0; j < COUNT; j++) {
cells[i].push(false)
boxes[i].push(
new Box((i - COUNT / 2) * CELL_SIZE, (j - COUNT / 2) * CELL_SIZE, CELL_SIZE, CELL_SIZE)
)
}
}
export function Fog() {
const rCanvas = useRef<HTMLCanvasElement>(null)
const rVisibility = useRef<boolean[][]>(cells)
const editor = useEditor()
useEffect(() => {
const cvs = rCanvas.current!
const rect = cvs.getBoundingClientRect()
cvs.width = rect.width
cvs.height = rect.height
}, [editor])
useReactor(
'update fog',
() => {
const cells = rVisibility.current
const shapes = editor.getCurrentPageShapes()
for (const shape of shapes) {
const point = editor.getShapePageBounds(shape)!.point
const geometry = editor.getShapeGeometry(shape)
for (let i = 0; i < boxes.length; i++) {
for (let j = 0; j < boxes[i].length; j++) {
const box = boxes[i][j]
box.translate(Vec.Neg(point))
if (geometry.bounds.collides(box)) {
cells[i][j] = true
}
box.translate(point)
}
}
}
const cvs = rCanvas.current!
const ctx = cvs.getContext('2d')!
ctx.resetTransform()
const camera = editor.getCamera()
ctx.clearRect(0, 0, cvs.width, cvs.height)
ctx.fillStyle = 'rgba(0,0,0,0.9)'
ctx.fillRect(0, 0, cvs.width, cvs.height)
ctx.translate(100, 100)
ctx.scale(camera.z, camera.z)
ctx.translate(camera.x, camera.y)
for (let i = 0; i < boxes.length; i++) {
for (let j = 0; j < boxes[i].length; j++) {
if (!cells[i][j]) continue
const box = boxes[i][j]
ctx.filter = 'drop-shadow(100px)'
ctx.clearRect(box.x, box.y, box.width, box.height)
}
}
},
[editor]
)
return (
<canvas
ref={rCanvas}
style={{
zIndex: 999999,
position: 'absolute',
top: -100,
left: -100,
width: 'calc(100% + 200px)',
height: 'calc(100% + 200px)',
WebkitFilter: 'blur(15px)',
filter: 'blur(15px)',
pointerEvents: 'none',
}}
/>
)
}
const components: TLComponents = {
InFrontOfTheCanvas: Fog,
}
export default function BasicExample() {
return (
<div className="tldraw__editor">
<Tldraw persistenceKey="example" components={components} />
</div>
)
}

View file

@ -0,0 +1,10 @@
---
title: Fog of war
component: ./FogOfWarExample.tsx
category: basic
keywords: [ui, fog, overlay]
---
---
This example shows how you might keep an HTML canvas in sync with canvas content. It implements a simple "fog of war" effect, where the canvas is covered by a black overlay that can be cleared by drawing shapes on the canvas.