UNPKG

tldraw

Version:

A tiny little drawing editor.

276 lines (275 loc) • 11.4 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var DrawShapeUtil_exports = {}; __export(DrawShapeUtil_exports, { DrawShapeUtil: () => DrawShapeUtil }); module.exports = __toCommonJS(DrawShapeUtil_exports); var import_jsx_runtime = require("react/jsx-runtime"); var import_editor = require("@tldraw/editor"); var import_ShapeFill = require("../shared/ShapeFill"); var import_default_shape_constants = require("../shared/default-shape-constants"); var import_defaultStyleDefs = require("../shared/defaultStyleDefs"); var import_getStrokePoints = require("../shared/freehand/getStrokePoints"); var import_svg = require("../shared/freehand/svg"); var import_svgInk = require("../shared/freehand/svgInk"); var import_interpolate_props = require("../shared/interpolate-props"); var import_useDefaultColorTheme = require("../shared/useDefaultColorTheme"); var import_getPath = require("./getPath"); class DrawShapeUtil extends import_editor.ShapeUtil { static type = "draw"; static props = import_editor.drawShapeProps; static migrations = import_editor.drawShapeMigrations; options = { maxPointsPerShape: 600 }; hideResizeHandles(shape) { return getIsDot(shape); } hideRotateHandle(shape) { return getIsDot(shape); } hideSelectionBoundsFg(shape) { return getIsDot(shape); } getDefaultProps() { return { segments: [], color: "black", fill: "none", dash: "draw", size: "m", isComplete: false, isClosed: false, isPen: false, scale: 1, scaleX: 1, scaleY: 1 }; } getGeometry(shape) { const points = (0, import_getPath.getPointsFromDrawSegments)( shape.props.segments, shape.props.scaleX, shape.props.scaleY ); const sw = (import_default_shape_constants.STROKE_SIZES[shape.props.size] + 1) * shape.props.scale; if (shape.props.segments.length === 1) { const box = import_editor.Box.FromPoints(points); if (box.width < sw * 2 && box.height < sw * 2) { return new import_editor.Circle2d({ x: -sw, y: -sw, radius: sw, isFilled: true }); } } const strokePoints = (0, import_getStrokePoints.getStrokePoints)( points, (0, import_getPath.getFreehandOptions)(shape.props, sw, shape.props.isPen, true) ).map((p) => p.point); if (shape.props.isClosed && strokePoints.length > 2) { return new import_editor.Polygon2d({ points: strokePoints, isFilled: shape.props.fill !== "none" }); } if (strokePoints.length === 1) { return new import_editor.Circle2d({ x: -sw, y: -sw, radius: sw, isFilled: true }); } return new import_editor.Polyline2d({ points: strokePoints }); } component(shape) { return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_editor.SVGContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DrawShapeSvg, { shape }) }); } indicator(shape) { const allPointsFromSegments = (0, import_getPath.getPointsFromDrawSegments)( shape.props.segments, shape.props.scaleX, shape.props.scaleY ); let sw = (import_default_shape_constants.STROKE_SIZES[shape.props.size] + 1) * shape.props.scale; const forceSolid = (0, import_editor.useValue)( "force solid", () => { const zoomLevel = this.editor.getEfficientZoomLevel(); return zoomLevel < 0.5 && zoomLevel < 1.5 / sw; }, [this.editor, sw] ); if (!forceSolid && !shape.props.isPen && shape.props.dash === "draw" && allPointsFromSegments.length === 1) { sw += (0, import_editor.rng)(shape.id)() * (sw / 6); } const showAsComplete = shape.props.isComplete || (0, import_editor.last)(shape.props.segments)?.type === "straight"; const options = (0, import_getPath.getFreehandOptions)(shape.props, sw, showAsComplete, true); const strokePoints = (0, import_getStrokePoints.getStrokePoints)(allPointsFromSegments, options); const solidStrokePath = strokePoints.length > 1 ? (0, import_svg.getSvgPathFromStrokePoints)(strokePoints, shape.props.isClosed) : getDot(allPointsFromSegments[0], sw); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: solidStrokePath }); } useLegacyIndicator() { return false; } getIndicatorPath(shape) { const allPointsFromSegments = (0, import_getPath.getPointsFromDrawSegments)( shape.props.segments, shape.props.scaleX, shape.props.scaleY ); let sw = (import_default_shape_constants.STROKE_SIZES[shape.props.size] + 1) * shape.props.scale; const zoomLevel = this.editor.getEfficientZoomLevel(); const forceSolid = zoomLevel < 0.5 && zoomLevel < 1.5 / sw; if (!forceSolid && !shape.props.isPen && shape.props.dash === "draw" && allPointsFromSegments.length === 1) { sw += (0, import_editor.rng)(shape.id)() * (sw / 6); } const showAsComplete = shape.props.isComplete || (0, import_editor.last)(shape.props.segments)?.type === "straight"; const options = (0, import_getPath.getFreehandOptions)(shape.props, sw, showAsComplete, true); const strokePoints = (0, import_getStrokePoints.getStrokePoints)(allPointsFromSegments, options); const solidStrokePath = strokePoints.length > 1 ? (0, import_svg.getSvgPathFromStrokePoints)(strokePoints, shape.props.isClosed) : getDot(allPointsFromSegments[0], sw); return new Path2D(solidStrokePath); } toSvg(shape, ctx) { ctx.addExportDef((0, import_defaultStyleDefs.getFillDefForExport)(shape.props.fill)); const scaleFactor = 1 / shape.props.scale; return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { transform: `scale(${scaleFactor})`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DrawShapeSvg, { shape, zoomOverride: 1 }) }); } getCanvasSvgDefs() { return [(0, import_defaultStyleDefs.getFillDefForCanvas)()]; } onResize(shape, info) { const newScaleX = info.scaleX * shape.props.scaleX; const newScaleY = info.scaleY * shape.props.scaleY; if (newScaleX === 0 || newScaleY === 0) return; return { props: { scaleX: newScaleX, scaleY: newScaleY } }; } expandSelectionOutlinePx(shape) { const multiplier = shape.props.dash === "draw" ? 1.6 : 1; return import_default_shape_constants.STROKE_SIZES[shape.props.size] * multiplier / 2 * shape.props.scale; } getInterpolatedProps(startShape, endShape, t) { return { ...t > 0.5 ? endShape.props : startShape.props, segments: (0, import_interpolate_props.interpolateSegments)(startShape.props.segments, endShape.props.segments, t), scale: (0, import_editor.lerp)(startShape.props.scale, endShape.props.scale, t) }; } } function getDot(point, sw) { const r = (sw + 1) * 0.5; return `M ${point.x} ${point.y} m -${r}, 0 a ${r},${r} 0 1,0 ${r * 2},0 a ${r},${r} 0 1,0 -${r * 2},0`; } function getIsDot(shape) { return shape.props.segments.length === 1 && shape.props.segments[0].path.length < 24; } function DrawShapeSvg({ shape, zoomOverride }) { const theme = (0, import_useDefaultColorTheme.useDefaultColorTheme)(); const editor = (0, import_editor.useEditor)(); const allPointsFromSegments = (0, import_getPath.getPointsFromDrawSegments)( shape.props.segments, shape.props.scaleX, shape.props.scaleY ); const showAsComplete = shape.props.isComplete || (0, import_editor.last)(shape.props.segments)?.type === "straight"; let sw = (import_default_shape_constants.STROKE_SIZES[shape.props.size] + 1) * shape.props.scale; const forceSolid = (0, import_editor.useValue)( "force solid", () => { const zoomLevel = zoomOverride ?? editor.getEfficientZoomLevel(); return zoomLevel < 0.5 && zoomLevel < 1.5 / sw; }, [editor, sw, zoomOverride] ); const dotAdjustment = (0, import_editor.useValue)( "dot adjustment", () => { const zoomLevel = zoomOverride ?? editor.getEfficientZoomLevel(); return zoomLevel < 0.2 ? 9 : 0.1; }, [editor, zoomOverride] ); if (!forceSolid && !shape.props.isPen && shape.props.dash === "draw" && allPointsFromSegments.length === 1) { sw += (0, import_editor.rng)(shape.id)() * (sw / 6); } const options = (0, import_getPath.getFreehandOptions)(shape.props, sw, showAsComplete, forceSolid); if (!forceSolid && shape.props.dash === "draw") { return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ shape.props.isClosed && shape.props.fill && allPointsFromSegments.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_ShapeFill.ShapeFill, { d: (0, import_svg.getSvgPathFromStrokePoints)( (0, import_getStrokePoints.getStrokePoints)(allPointsFromSegments, options), shape.props.isClosed ), theme, color: shape.props.color, fill: shape.props.isClosed ? shape.props.fill : "none", scale: shape.props.scale } ) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "path", { d: (0, import_svgInk.svgInk)(allPointsFromSegments, options), strokeLinecap: "round", fill: (0, import_editor.getColorValue)(theme, shape.props.color, "solid") } ) ] }); } const strokePoints = (0, import_getStrokePoints.getStrokePoints)(allPointsFromSegments, options); const isDot = strokePoints.length < 2; const solidStrokePath = isDot ? getDot(allPointsFromSegments[0], 0) : (0, import_svg.getSvgPathFromStrokePoints)(strokePoints, shape.props.isClosed); return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_ShapeFill.ShapeFill, { d: solidStrokePath, theme, color: shape.props.color, fill: isDot || shape.props.isClosed ? shape.props.fill : "none", scale: shape.props.scale } ), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "path", { d: solidStrokePath, strokeLinecap: "round", fill: isDot ? (0, import_editor.getColorValue)(theme, shape.props.color, "solid") : "none", stroke: (0, import_editor.getColorValue)(theme, shape.props.color, "solid"), strokeWidth: sw, strokeDasharray: isDot ? "none" : (0, import_getPath.getDrawShapeStrokeDashArray)(shape, sw, dotAdjustment), strokeDashoffset: "0" } ) ] }); } //# sourceMappingURL=DrawShapeUtil.js.map