fabric
Version:
Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
1 lines • 12.5 kB
Source Map (JSON)
{"version":3,"file":"pathControl.min.mjs","sources":["../../../src/controls/pathControl.ts"],"sourcesContent":["import { Point } from '../Point';\nimport { Control } from './Control';\nimport type { TMat2D } from '../typedefs';\nimport type { Path } from '../shapes/Path';\nimport { multiplyTransformMatrices } from '../util/misc/matrix';\nimport type {\n TModificationEvents,\n TPointerEvent,\n Transform,\n} from '../EventTypeDefs';\nimport { sendPointToPlane } from '../util/misc/planeChange';\nimport type { TSimpleParseCommandType } from '../util/path/typedefs';\nimport type { ControlRenderingStyleOverride } from './controlRendering';\nimport { fireEvent } from './fireEvent';\nimport { commonEventInfo } from './util';\n\nconst ACTION_NAME: TModificationEvents = 'modifyPath' as const;\n\ntype TTransformAnchor = Transform;\n\nexport type PathPointControlStyle = {\n controlFill?: string;\n controlStroke?: string;\n connectionDashArray?: number[];\n};\n\nconst calcPathPointPosition = (\n pathObject: Path,\n commandIndex: number,\n pointIndex: number,\n) => {\n const { path, pathOffset } = pathObject;\n const command = path[commandIndex];\n return new Point(\n (command[pointIndex] as number) - pathOffset.x,\n (command[pointIndex + 1] as number) - pathOffset.y,\n ).transform(\n multiplyTransformMatrices(\n pathObject.getViewportTransform(),\n pathObject.calcTransformMatrix(),\n ),\n );\n};\n\nconst movePathPoint = (\n pathObject: Path,\n x: number,\n y: number,\n commandIndex: number,\n pointIndex: number,\n) => {\n const { path, pathOffset } = pathObject;\n\n const anchorCommand =\n path[(commandIndex > 0 ? commandIndex : path.length) - 1];\n const anchorPoint = new Point(\n anchorCommand[pointIndex] as number,\n anchorCommand[pointIndex + 1] as number,\n );\n\n const anchorPointInParentPlane = anchorPoint\n .subtract(pathOffset)\n .transform(pathObject.calcOwnMatrix());\n\n const mouseLocalPosition = sendPointToPlane(\n new Point(x, y),\n undefined,\n pathObject.calcOwnMatrix(),\n );\n\n path[commandIndex][pointIndex] = mouseLocalPosition.x + pathOffset.x;\n path[commandIndex][pointIndex + 1] = mouseLocalPosition.y + pathOffset.y;\n pathObject.setDimensions();\n\n const newAnchorPointInParentPlane = anchorPoint\n .subtract(pathObject.pathOffset)\n .transform(pathObject.calcOwnMatrix());\n\n const diff = newAnchorPointInParentPlane.subtract(anchorPointInParentPlane);\n pathObject.left -= diff.x;\n pathObject.top -= diff.y;\n pathObject.set('dirty', true);\n return true;\n};\n\n/**\n * This function locates the controls.\n * It'll be used both for drawing and for interaction.\n */\nfunction pathPositionHandler(\n this: PathPointControl,\n dim: Point,\n finalMatrix: TMat2D,\n pathObject: Path,\n) {\n const { commandIndex, pointIndex } = this;\n return calcPathPointPosition(pathObject, commandIndex, pointIndex);\n}\n\n/**\n * This function defines what the control does.\n * It'll be called on every mouse move after a control has been clicked and is being dragged.\n * The function receives as argument the mouse event, the current transform object\n * and the current position in canvas coordinate `transform.target` is a reference to the\n * current object being transformed.\n */\nfunction pathActionHandler(\n this: PathPointControl,\n eventData: TPointerEvent,\n transform: TTransformAnchor,\n x: number,\n y: number,\n) {\n const { target } = transform;\n const { commandIndex, pointIndex } = this;\n const actionPerformed = movePathPoint(\n target as Path,\n x,\n y,\n commandIndex,\n pointIndex,\n );\n if (actionPerformed) {\n fireEvent(this.actionName as TModificationEvents, {\n ...commonEventInfo(eventData, transform, x, y),\n commandIndex,\n pointIndex,\n });\n }\n return actionPerformed;\n}\n\nconst indexFromPrevCommand = (previousCommandType: TSimpleParseCommandType) =>\n previousCommandType === 'C' ? 5 : previousCommandType === 'Q' ? 3 : 1;\n\nclass PathPointControl extends Control {\n declare commandIndex: number;\n declare pointIndex: number;\n declare controlFill: string;\n declare controlStroke: string;\n constructor(options?: Partial<PathPointControl>) {\n super(options);\n }\n\n render(\n ctx: CanvasRenderingContext2D,\n left: number,\n top: number,\n styleOverride: ControlRenderingStyleOverride | undefined,\n fabricObject: Path,\n ) {\n const overrides: ControlRenderingStyleOverride = {\n ...styleOverride,\n cornerColor: this.controlFill,\n cornerStrokeColor: this.controlStroke,\n transparentCorners: !this.controlFill,\n };\n super.render(ctx, left, top, overrides, fabricObject);\n }\n}\n\nclass PathControlPointControl extends PathPointControl {\n declare connectionDashArray?: number[];\n declare connectToCommandIndex: number;\n declare connectToPointIndex: number;\n constructor(options?: Partial<PathControlPointControl>) {\n super(options);\n }\n\n render(\n this: PathControlPointControl,\n ctx: CanvasRenderingContext2D,\n left: number,\n top: number,\n styleOverride: ControlRenderingStyleOverride | undefined,\n fabricObject: Path,\n ) {\n const { path } = fabricObject;\n const {\n commandIndex,\n pointIndex,\n connectToCommandIndex,\n connectToPointIndex,\n } = this;\n ctx.save();\n ctx.strokeStyle = this.controlStroke;\n if (this.connectionDashArray) {\n ctx.setLineDash(this.connectionDashArray);\n }\n const [commandType] = path[commandIndex];\n const point = calcPathPointPosition(\n fabricObject,\n connectToCommandIndex,\n connectToPointIndex,\n );\n\n if (commandType === 'Q') {\n // one control point connects to 2 points\n const point2 = calcPathPointPosition(\n fabricObject,\n commandIndex,\n pointIndex + 2,\n );\n ctx.moveTo(point2.x, point2.y);\n ctx.lineTo(left, top);\n } else {\n ctx.moveTo(left, top);\n }\n ctx.lineTo(point.x, point.y);\n ctx.stroke();\n ctx.restore();\n\n super.render(ctx, left, top, styleOverride, fabricObject);\n }\n}\n\nconst createControl = (\n commandIndexPos: number,\n pointIndexPos: number,\n isControlPoint: boolean,\n options: Partial<Control> & {\n controlPointStyle?: PathPointControlStyle;\n pointStyle?: PathPointControlStyle;\n },\n connectToCommandIndex?: number,\n connectToPointIndex?: number,\n) =>\n new (isControlPoint ? PathControlPointControl : PathPointControl)({\n commandIndex: commandIndexPos,\n pointIndex: pointIndexPos,\n actionName: ACTION_NAME,\n positionHandler: pathPositionHandler,\n actionHandler: pathActionHandler,\n connectToCommandIndex,\n connectToPointIndex,\n ...options,\n ...(isControlPoint ? options.controlPointStyle : options.pointStyle),\n } as Partial<PathControlPointControl>);\n\nexport function createPathControls(\n path: Path,\n options: Partial<Control> & {\n controlPointStyle?: PathPointControlStyle;\n pointStyle?: PathPointControlStyle;\n } = {},\n): Record<string, Control> {\n const controls = {} as Record<string, Control>;\n let previousCommandType: TSimpleParseCommandType = 'M';\n path.path.forEach((command, commandIndex) => {\n const commandType = command[0];\n\n if (commandType !== 'Z') {\n controls[`c_${commandIndex}_${commandType}`] = createControl(\n commandIndex,\n command.length - 2,\n false,\n options,\n );\n }\n switch (commandType) {\n case 'C':\n controls[`c_${commandIndex}_C_CP_1`] = createControl(\n commandIndex,\n 1,\n true,\n options,\n commandIndex - 1,\n indexFromPrevCommand(previousCommandType),\n );\n controls[`c_${commandIndex}_C_CP_2`] = createControl(\n commandIndex,\n 3,\n true,\n options,\n commandIndex,\n 5,\n );\n break;\n case 'Q':\n controls[`c_${commandIndex}_Q_CP_1`] = createControl(\n commandIndex,\n 1,\n true,\n options,\n commandIndex,\n 3,\n );\n break;\n }\n previousCommandType = commandType;\n });\n return controls;\n}\n"],"names":["calcPathPointPosition","pathObject","commandIndex","pointIndex","path","pathOffset","command","Point","x","y","transform","multiplyTransformMatrices","getViewportTransform","calcTransformMatrix","pathPositionHandler","dim","finalMatrix","this","pathActionHandler","eventData","target","actionPerformed","movePathPoint","anchorCommand","length","anchorPoint","anchorPointInParentPlane","subtract","calcOwnMatrix","mouseLocalPosition","sendPointToPlane","undefined","setDimensions","diff","left","top","set","fireEvent","actionName","_objectSpread","commonEventInfo","PathPointControl","Control","constructor","options","super","render","ctx","styleOverride","fabricObject","overrides","cornerColor","controlFill","cornerStrokeColor","controlStroke","transparentCorners","PathControlPointControl","connectToCommandIndex","connectToPointIndex","save","strokeStyle","connectionDashArray","setLineDash","commandType","point","point2","moveTo","lineTo","stroke","restore","createControl","commandIndexPos","pointIndexPos","isControlPoint","positionHandler","actionHandler","controlPointStyle","pointStyle","createPathControls","arguments","controls","previousCommandType","forEach","concat","indexFromPrevCommand"],"mappings":"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"}