@tldraw/editor
Version:
tldraw infinite canvas SDK (editor).
8 lines (7 loc) • 11.3 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../../../src/lib/components/default-components/CanvasOverlays.tsx"],
"sourcesContent": ["import { EffectScheduler, computed } from '@tldraw/state'\nimport { memo, useEffect, useRef } from 'react'\nimport { useEditor } from '../../hooks/useEditor'\nimport { Geometry2d } from '../../primitives/geometry/Geometry2d'\nimport { Group2d } from '../../primitives/geometry/Group2d'\nimport { debugFlags } from '../../utils/debug-flags'\n\ninterface RenderInputs {\n\tdpr: number\n\tw: number\n\th: number\n\tcx: number\n\tcy: number\n\tzoom: number\n}\n\n/** @internal @react */\nexport const CanvasOverlays = memo(function CanvasOverlays() {\n\tconst editor = useEditor()\n\tconst canvasRef = useRef<HTMLCanvasElement>(null)\n\n\tuseEffect(() => {\n\t\t// Bundle the primitive scalars the renderer needs into one computed so the\n\t\t// effect only refires on actual visual change. Reading the whole instance\n\t\t// state directly would otherwise wake the renderer on every cursor move,\n\t\t// brush update, etc.\n\n\t\tconst renderInputs$ = computed<RenderInputs>(\n\t\t\t'canvas overlays render inputs',\n\t\t\t() => {\n\t\t\t\tconst instance = editor.getInstanceState()\n\t\t\t\tconst camera = editor.getCamera()\n\t\t\t\treturn {\n\t\t\t\t\tdpr: instance.devicePixelRatio,\n\t\t\t\t\tw: instance.screenBounds.w,\n\t\t\t\t\th: instance.screenBounds.h,\n\t\t\t\t\tcx: camera.x,\n\t\t\t\t\tcy: camera.y,\n\t\t\t\t\tzoom: camera.z,\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\tisEqual: (a, b) =>\n\t\t\t\t\ta.dpr === b.dpr &&\n\t\t\t\t\ta.w === b.w &&\n\t\t\t\t\ta.h === b.h &&\n\t\t\t\t\ta.cx === b.cx &&\n\t\t\t\t\ta.cy === b.cy &&\n\t\t\t\t\ta.zoom === b.zoom,\n\t\t\t}\n\t\t)\n\n\t\tconst scheduler = new EffectScheduler('canvas overlays render', () => {\n\t\t\tconst canvas = canvasRef.current\n\t\t\tif (!canvas) return\n\n\t\t\tconst ctx = canvas.getContext('2d')\n\t\t\tif (!ctx) return\n\n\t\t\tconst { dpr, w, h, cx, cy, zoom } = renderInputs$.get()\n\n\t\t\tconst canvasWidth = Math.ceil(w * dpr)\n\t\t\tconst canvasHeight = Math.ceil(h * dpr)\n\n\t\t\tif (canvas.width !== canvasWidth || canvas.height !== canvasHeight) {\n\t\t\t\tcanvas.width = canvasWidth\n\t\t\t\tcanvas.height = canvasHeight\n\t\t\t\tcanvas.style.width = `${w}px`\n\t\t\t\tcanvas.style.height = `${h}px`\n\t\t\t}\n\n\t\t\tctx.setTransform(1, 0, 0, 1, 0, 0)\n\t\t\tctx.clearRect(0, 0, canvas.width, canvas.height)\n\n\t\t\t// One setTransform = DPR scale * zoom scale * camera translate, into page space.\n\t\t\tconst s = dpr * zoom\n\t\t\tctx.setTransform(s, 0, 0, s, s * cx, s * cy)\n\n\t\t\t// Render all active overlay utils in zIndex order (low to high).\n\t\t\tfor (const { util, overlays } of editor.overlays.getActiveOverlayEntries()) {\n\t\t\t\tctx.save()\n\t\t\t\tutil.render(ctx, overlays)\n\t\t\t\tctx.restore()\n\t\t\t}\n\n\t\t\t// Debug: draw all geometry\n\t\t\tif (debugFlags.debugGeometry.get()) {\n\t\t\t\tconst currentPagePoint = editor.inputs.getCurrentPagePoint()\n\n\t\t\t\t// Shape geometries\n\t\t\t\tconst renderingShapes = editor.getRenderingShapes()\n\t\t\t\tfor (const result of renderingShapes) {\n\t\t\t\t\tconst shape = editor.getShape(result.id)\n\t\t\t\t\tif (!shape || shape.type === 'group') continue\n\n\t\t\t\t\tconst geometry = editor.getShapeGeometry(shape)\n\t\t\t\t\tconst pageTransform = editor.getShapePageTransform(shape)\n\t\t\t\t\tif (!pageTransform) continue\n\n\t\t\t\t\tctx.save()\n\t\t\t\t\tconst m = pageTransform\n\t\t\t\t\tctx.transform(m.a, m.b, m.c, m.d, m.e, m.f)\n\n\t\t\t\t\t// Outline\n\t\t\t\t\tctx.strokeStyle = geometry.debugColor ?? 'red'\n\t\t\t\t\tctx.lineWidth = 2 / zoom\n\t\t\t\t\tctx.fillStyle = 'none'\n\t\t\t\t\tdrawGeometryStroke(ctx, geometry)\n\n\t\t\t\t\t// Vertices\n\t\t\t\t\tconst { vertices } = geometry\n\t\t\t\t\tfor (let i = 0; i < vertices.length; i++) {\n\t\t\t\t\t\tconst v = vertices[i]\n\t\t\t\t\t\tconst hue = vertices.length > 1 ? 120 + ((200 - 120) * i) / (vertices.length - 1) : 160\n\t\t\t\t\t\tctx.fillStyle = `hsl(${hue}, 100%, 50%)`\n\t\t\t\t\t\tctx.strokeStyle = 'black'\n\t\t\t\t\t\tctx.lineWidth = 1 / zoom\n\t\t\t\t\t\tctx.beginPath()\n\t\t\t\t\t\tctx.arc(v.x, v.y, 2 / zoom, 0, Math.PI * 2)\n\t\t\t\t\t\tctx.fill()\n\t\t\t\t\t\tctx.stroke()\n\t\t\t\t\t}\n\n\t\t\t\t\t// Nearest point line\n\t\t\t\t\tconst pointInShapeSpace = editor.getPointInShapeSpace(shape, currentPagePoint)\n\t\t\t\t\tconst dist = Math.abs(geometry.distanceToPoint(pointInShapeSpace, true)) * zoom\n\t\t\t\t\tif (dist < 150) {\n\t\t\t\t\t\tconst nearestPoint = geometry.nearestPoint(pointInShapeSpace)\n\t\t\t\t\t\tconst hitInside = geometry.distanceToPoint(pointInShapeSpace, true) < 0\n\t\t\t\t\t\tctx.strokeStyle = hitInside ? 'goldenrod' : 'dodgerblue'\n\t\t\t\t\t\tctx.lineWidth = 2 / zoom\n\t\t\t\t\t\tctx.globalAlpha = 1 - dist / 150\n\t\t\t\t\t\tctx.beginPath()\n\t\t\t\t\t\tctx.moveTo(nearestPoint.x, nearestPoint.y)\n\t\t\t\t\t\tctx.lineTo(pointInShapeSpace.x, pointInShapeSpace.y)\n\t\t\t\t\t\tctx.stroke()\n\t\t\t\t\t\tctx.globalAlpha = 1\n\t\t\t\t\t}\n\n\t\t\t\t\tctx.restore()\n\t\t\t\t}\n\n\t\t\t\t// Overlay hit-test geometries\n\t\t\t\tctx.save()\n\t\t\t\tctx.strokeStyle = 'magenta'\n\t\t\t\tctx.fillStyle = 'rgba(255, 0, 255, 0.1)'\n\t\t\t\tctx.lineWidth = 1 / zoom\n\t\t\t\tfor (const { overlays } of editor.overlays.getActiveOverlayEntries()) {\n\t\t\t\t\tfor (const overlay of overlays) {\n\t\t\t\t\t\tconst geometry = editor.overlays.getOverlayGeometry(overlay)\n\t\t\t\t\t\tif (!geometry) continue\n\t\t\t\t\t\tconst vertices = geometry.vertices\n\t\t\t\t\t\tif (vertices.length < 2) continue\n\t\t\t\t\t\tctx.beginPath()\n\t\t\t\t\t\tctx.moveTo(vertices[0].x, vertices[0].y)\n\t\t\t\t\t\tfor (let i = 1; i < vertices.length; i++) {\n\t\t\t\t\t\t\tctx.lineTo(vertices[i].x, vertices[i].y)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (geometry.isClosed) {\n\t\t\t\t\t\t\tctx.closePath()\n\t\t\t\t\t\t\tctx.fill()\n\t\t\t\t\t\t}\n\t\t\t\t\t\tctx.stroke()\n\t\t\t\t\t\tfor (const v of vertices) {\n\t\t\t\t\t\t\tctx.beginPath()\n\t\t\t\t\t\t\tctx.arc(v.x, v.y, 2 / zoom, 0, Math.PI * 2)\n\t\t\t\t\t\t\tctx.fill()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tctx.restore()\n\t\t\t}\n\t\t})\n\n\t\tscheduler.attach()\n\t\tscheduler.execute()\n\t\treturn () => scheduler.detach()\n\t}, [editor])\n\n\treturn <canvas ref={canvasRef} className=\"tl-canvas-overlays\" />\n})\n\nfunction drawGeometryStroke(ctx: CanvasRenderingContext2D, geometry: Geometry2d) {\n\tif (geometry instanceof Group2d) {\n\t\tconst prevStroke = ctx.strokeStyle\n\t\tfor (const child of geometry.children) {\n\t\t\tif (child.debugColor) ctx.strokeStyle = child.debugColor\n\t\t\tdrawGeometryStroke(ctx, child)\n\t\t\tctx.strokeStyle = prevStroke\n\t\t}\n\t\tfor (const child of geometry.ignoredChildren) {\n\t\t\tif (child.debugColor) ctx.strokeStyle = child.debugColor\n\t\t\tdrawGeometryStroke(ctx, child)\n\t\t\tctx.strokeStyle = prevStroke\n\t\t}\n\t\treturn\n\t}\n\n\tconst vertices = geometry.vertices\n\tif (vertices.length < 2) return\n\tctx.beginPath()\n\tctx.moveTo(vertices[0].x, vertices[0].y)\n\tfor (let i = 1; i < vertices.length; i++) {\n\t\tctx.lineTo(vertices[i].x, vertices[i].y)\n\t}\n\tif (geometry.isClosed) ctx.closePath()\n\tctx.stroke()\n}\n"],
"mappings": "AAmLQ;AAnLR,SAAS,iBAAiB,gBAAgB;AAC1C,SAAS,MAAM,WAAW,cAAc;AACxC,SAAS,iBAAiB;AAE1B,SAAS,eAAe;AACxB,SAAS,kBAAkB;AAYpB,MAAM,iBAAiB,KAAK,SAASA,kBAAiB;AAC5D,QAAM,SAAS,UAAU;AACzB,QAAM,YAAY,OAA0B,IAAI;AAEhD,YAAU,MAAM;AAMf,UAAM,gBAAgB;AAAA,MACrB;AAAA,MACA,MAAM;AACL,cAAM,WAAW,OAAO,iBAAiB;AACzC,cAAM,SAAS,OAAO,UAAU;AAChC,eAAO;AAAA,UACN,KAAK,SAAS;AAAA,UACd,GAAG,SAAS,aAAa;AAAA,UACzB,GAAG,SAAS,aAAa;AAAA,UACzB,IAAI,OAAO;AAAA,UACX,IAAI,OAAO;AAAA,UACX,MAAM,OAAO;AAAA,QACd;AAAA,MACD;AAAA,MACA;AAAA,QACC,SAAS,CAAC,GAAG,MACZ,EAAE,QAAQ,EAAE,OACZ,EAAE,MAAM,EAAE,KACV,EAAE,MAAM,EAAE,KACV,EAAE,OAAO,EAAE,MACX,EAAE,OAAO,EAAE,MACX,EAAE,SAAS,EAAE;AAAA,MACf;AAAA,IACD;AAEA,UAAM,YAAY,IAAI,gBAAgB,0BAA0B,MAAM;AACrE,YAAM,SAAS,UAAU;AACzB,UAAI,CAAC,OAAQ;AAEb,YAAM,MAAM,OAAO,WAAW,IAAI;AAClC,UAAI,CAAC,IAAK;AAEV,YAAM,EAAE,KAAK,GAAG,GAAG,IAAI,IAAI,KAAK,IAAI,cAAc,IAAI;AAEtD,YAAM,cAAc,KAAK,KAAK,IAAI,GAAG;AACrC,YAAM,eAAe,KAAK,KAAK,IAAI,GAAG;AAEtC,UAAI,OAAO,UAAU,eAAe,OAAO,WAAW,cAAc;AACnE,eAAO,QAAQ;AACf,eAAO,SAAS;AAChB,eAAO,MAAM,QAAQ,GAAG,CAAC;AACzB,eAAO,MAAM,SAAS,GAAG,CAAC;AAAA,MAC3B;AAEA,UAAI,aAAa,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AACjC,UAAI,UAAU,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM;AAG/C,YAAM,IAAI,MAAM;AAChB,UAAI,aAAa,GAAG,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,EAAE;AAG3C,iBAAW,EAAE,MAAM,SAAS,KAAK,OAAO,SAAS,wBAAwB,GAAG;AAC3E,YAAI,KAAK;AACT,aAAK,OAAO,KAAK,QAAQ;AACzB,YAAI,QAAQ;AAAA,MACb;AAGA,UAAI,WAAW,cAAc,IAAI,GAAG;AACnC,cAAM,mBAAmB,OAAO,OAAO,oBAAoB;AAG3D,cAAM,kBAAkB,OAAO,mBAAmB;AAClD,mBAAW,UAAU,iBAAiB;AACrC,gBAAM,QAAQ,OAAO,SAAS,OAAO,EAAE;AACvC,cAAI,CAAC,SAAS,MAAM,SAAS,QAAS;AAEtC,gBAAM,WAAW,OAAO,iBAAiB,KAAK;AAC9C,gBAAM,gBAAgB,OAAO,sBAAsB,KAAK;AACxD,cAAI,CAAC,cAAe;AAEpB,cAAI,KAAK;AACT,gBAAM,IAAI;AACV,cAAI,UAAU,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAG1C,cAAI,cAAc,SAAS,cAAc;AACzC,cAAI,YAAY,IAAI;AACpB,cAAI,YAAY;AAChB,6BAAmB,KAAK,QAAQ;AAGhC,gBAAM,EAAE,SAAS,IAAI;AACrB,mBAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACzC,kBAAM,IAAI,SAAS,CAAC;AACpB,kBAAM,MAAM,SAAS,SAAS,IAAI,OAAQ,MAAM,OAAO,KAAM,SAAS,SAAS,KAAK;AACpF,gBAAI,YAAY,OAAO,GAAG;AAC1B,gBAAI,cAAc;AAClB,gBAAI,YAAY,IAAI;AACpB,gBAAI,UAAU;AACd,gBAAI,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,MAAM,GAAG,KAAK,KAAK,CAAC;AAC1C,gBAAI,KAAK;AACT,gBAAI,OAAO;AAAA,UACZ;AAGA,gBAAM,oBAAoB,OAAO,qBAAqB,OAAO,gBAAgB;AAC7E,gBAAM,OAAO,KAAK,IAAI,SAAS,gBAAgB,mBAAmB,IAAI,CAAC,IAAI;AAC3E,cAAI,OAAO,KAAK;AACf,kBAAM,eAAe,SAAS,aAAa,iBAAiB;AAC5D,kBAAM,YAAY,SAAS,gBAAgB,mBAAmB,IAAI,IAAI;AACtE,gBAAI,cAAc,YAAY,cAAc;AAC5C,gBAAI,YAAY,IAAI;AACpB,gBAAI,cAAc,IAAI,OAAO;AAC7B,gBAAI,UAAU;AACd,gBAAI,OAAO,aAAa,GAAG,aAAa,CAAC;AACzC,gBAAI,OAAO,kBAAkB,GAAG,kBAAkB,CAAC;AACnD,gBAAI,OAAO;AACX,gBAAI,cAAc;AAAA,UACnB;AAEA,cAAI,QAAQ;AAAA,QACb;AAGA,YAAI,KAAK;AACT,YAAI,cAAc;AAClB,YAAI,YAAY;AAChB,YAAI,YAAY,IAAI;AACpB,mBAAW,EAAE,SAAS,KAAK,OAAO,SAAS,wBAAwB,GAAG;AACrE,qBAAW,WAAW,UAAU;AAC/B,kBAAM,WAAW,OAAO,SAAS,mBAAmB,OAAO;AAC3D,gBAAI,CAAC,SAAU;AACf,kBAAM,WAAW,SAAS;AAC1B,gBAAI,SAAS,SAAS,EAAG;AACzB,gBAAI,UAAU;AACd,gBAAI,OAAO,SAAS,CAAC,EAAE,GAAG,SAAS,CAAC,EAAE,CAAC;AACvC,qBAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACzC,kBAAI,OAAO,SAAS,CAAC,EAAE,GAAG,SAAS,CAAC,EAAE,CAAC;AAAA,YACxC;AACA,gBAAI,SAAS,UAAU;AACtB,kBAAI,UAAU;AACd,kBAAI,KAAK;AAAA,YACV;AACA,gBAAI,OAAO;AACX,uBAAW,KAAK,UAAU;AACzB,kBAAI,UAAU;AACd,kBAAI,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,MAAM,GAAG,KAAK,KAAK,CAAC;AAC1C,kBAAI,KAAK;AAAA,YACV;AAAA,UACD;AAAA,QACD;AACA,YAAI,QAAQ;AAAA,MACb;AAAA,IACD,CAAC;AAED,cAAU,OAAO;AACjB,cAAU,QAAQ;AAClB,WAAO,MAAM,UAAU,OAAO;AAAA,EAC/B,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO,oBAAC,YAAO,KAAK,WAAW,WAAU,sBAAqB;AAC/D,CAAC;AAED,SAAS,mBAAmB,KAA+B,UAAsB;AAChF,MAAI,oBAAoB,SAAS;AAChC,UAAM,aAAa,IAAI;AACvB,eAAW,SAAS,SAAS,UAAU;AACtC,UAAI,MAAM,WAAY,KAAI,cAAc,MAAM;AAC9C,yBAAmB,KAAK,KAAK;AAC7B,UAAI,cAAc;AAAA,IACnB;AACA,eAAW,SAAS,SAAS,iBAAiB;AAC7C,UAAI,MAAM,WAAY,KAAI,cAAc,MAAM;AAC9C,yBAAmB,KAAK,KAAK;AAC7B,UAAI,cAAc;AAAA,IACnB;AACA;AAAA,EACD;AAEA,QAAM,WAAW,SAAS;AAC1B,MAAI,SAAS,SAAS,EAAG;AACzB,MAAI,UAAU;AACd,MAAI,OAAO,SAAS,CAAC,EAAE,GAAG,SAAS,CAAC,EAAE,CAAC;AACvC,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACzC,QAAI,OAAO,SAAS,CAAC,EAAE,GAAG,SAAS,CAAC,EAAE,CAAC;AAAA,EACxC;AACA,MAAI,SAAS,SAAU,KAAI,UAAU;AACrC,MAAI,OAAO;AACZ;",
"names": ["CanvasOverlays"]
}