mirror of https://github.com/logseq/logseq
refactor(whiteboard): using canvas to render the whiteboard grid dots
parent
8bf59a564b
commit
0260f5da5d
|
@ -84,6 +84,15 @@ export const DevTools = observer(() => {
|
|||
.map(p => p.join(''))
|
||||
.join('|')
|
||||
|
||||
const originPoint = canvasAnchorRef.current
|
||||
? ReactDOM.createPortal(
|
||||
<svg className="tl-renderer-dev-tools tl-grid">
|
||||
<circle cx={point[0] * zoom} cy={point[1] * zoom} r="4" fill="red" />
|
||||
</svg>,
|
||||
canvasAnchorRef.current
|
||||
)
|
||||
: null
|
||||
|
||||
const rendererStatus = statusbarAnchorRef.current
|
||||
? ReactDOM.createPortal(
|
||||
<div
|
||||
|
@ -101,6 +110,7 @@ export const DevTools = observer(() => {
|
|||
|
||||
return (
|
||||
<>
|
||||
{originPoint}
|
||||
{rendererStatus}
|
||||
<HistoryStack />
|
||||
</>
|
||||
|
|
|
@ -1,33 +1,11 @@
|
|||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import * as React from 'react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useApp } from '@tldraw/react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import type { Shape } from '../../lib'
|
||||
|
||||
export const StatusBar = observer(function StatusBar() {
|
||||
const app = useApp<Shape>()
|
||||
React.useEffect(() => {
|
||||
const canvas = document.querySelector<HTMLElement>('.logseq-tldraw-wrapper .tl-canvas')
|
||||
const actionBar = document.querySelector<HTMLElement>('.logseq-tldraw-wrapper .tl-action-bar')
|
||||
if (canvas) {
|
||||
canvas.style.height = 'calc(100% - 32px)'
|
||||
}
|
||||
|
||||
if (actionBar) {
|
||||
actionBar.style.marginBottom = '32px'
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (canvas) {
|
||||
canvas.style.height = '100%'
|
||||
}
|
||||
|
||||
if (actionBar) {
|
||||
actionBar.style.marginBottom = '0px'
|
||||
}
|
||||
}
|
||||
})
|
||||
return (
|
||||
<div className="tl-statusbar">
|
||||
{app.selectedTool.id} | {app.selectedTool.currentState.id}
|
||||
|
|
|
@ -13,7 +13,7 @@ export class TLSettings implements TLSettingsProps {
|
|||
}
|
||||
|
||||
@observable mode: 'dark' | 'light' = 'light'
|
||||
@observable showGrid = !isSafari()
|
||||
@observable showGrid = true
|
||||
|
||||
@action update(props: Partial<TLSettingsProps>): void {
|
||||
Object.assign(this, props)
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
"mobx": "^6.6.2",
|
||||
"mobx-react-lite": "^3.4.0",
|
||||
"mousetrap": "^1.6.5",
|
||||
"polished": "^4.2.2",
|
||||
"rbush": "^3.0.1",
|
||||
"uuid": "^8.0.0"
|
||||
},
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { modulate } 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'
|
||||
|
||||
|
@ -15,35 +17,81 @@ export const Grid = observer(function Grid({ size }: TLGridProps) {
|
|||
const {
|
||||
viewport: {
|
||||
camera: { point, zoom },
|
||||
bounds,
|
||||
},
|
||||
} = useRendererContext()
|
||||
return (
|
||||
<svg className="tl-grid" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
{STEPS.map(([min, mid, _size], i) => {
|
||||
const s = _size * size * zoom
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
return (
|
||||
<pattern
|
||||
key={`grid-pattern-${i}`}
|
||||
id={`grid-${i}`}
|
||||
width={s}
|
||||
height={s}
|
||||
patternUnits="userSpaceOnUse"
|
||||
>
|
||||
<circle className={`tl-grid-dot`} cx={gxo} cy={gyo} r={1.5} opacity={opacity} />
|
||||
</pattern>
|
||||
)
|
||||
})}
|
||||
</defs>
|
||||
{STEPS.map((_, i) => (
|
||||
<rect key={`grid-rect-${i}`} width="100%" height="100%" fill={`url(#grid-${i})`} />
|
||||
))}
|
||||
</svg>
|
||||
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" />
|
||||
)
|
||||
})
|
||||
|
|
|
@ -421,6 +421,13 @@ const tlcss = css`
|
|||
color: var(--tl-background);
|
||||
}
|
||||
|
||||
.tl-grid-canvas {
|
||||
position: absolute;
|
||||
touch-action: none;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.tl-grid {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
|
|
@ -3669,7 +3669,7 @@ pirates@^4.0.1:
|
|||
resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b"
|
||||
integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==
|
||||
|
||||
polished@^4.0.0:
|
||||
polished@^4.0.0, polished@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/polished/-/polished-4.2.2.tgz#2529bb7c3198945373c52e34618c8fe7b1aa84d1"
|
||||
integrity sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==
|
||||
|
|
Loading…
Reference in New Issue