UNPKG

fabric

Version:

Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.

1 lines 11 kB
{"version":3,"file":"pathControl.min.mjs","names":[],"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"],"mappings":"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"}