Skip to content

Commit

Permalink
feat: 添加编辑多边形plugin功能 (#394)
Browse files Browse the repository at this point in the history
Co-authored-by: weicheng.liang <[email protected]>
  • Loading branch information
ByeWord and weicheng.liang authored May 23, 2024
1 parent 69c9ab8 commit a4ba260
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .eslintrc-auto-import.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,4 @@
"watchPostEffect": true,
"watchSyncEffect": true
}
}
}
4 changes: 4 additions & 0 deletions packages/core/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ class Editor extends EventEmitter {
this._initServersPlugin();
}

get fabricCanvas() {
return this.canvas;
}

// 引入组件
use(plugin: IPluginClass, options?: IPluginOption) {
if (this._checkPlugin(plugin) && this.canvas) {
Expand Down
1 change: 1 addition & 0 deletions packages/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export { default as RulerPlugin } from './plugin/RulerPlugin';
export { default as MaterialPlugin } from './plugin/MaterialPlugin';
export { default as WaterMarkPlugin } from './plugin/WaterMarkPlugin';
export { default as FontPlugin } from './plugin/FontPlugin';
export { default as PolygonModifyPlugin } from './plugin/PolygonModifyPlugin';
import EventType from './eventType';
import Utils from './utils/utils';

Expand Down
171 changes: 171 additions & 0 deletions packages/core/plugin/PolygonModifyPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import Editor from '../Editor';
import { fabric } from 'fabric';
import { drawImg } from '../utils/utils';
import edgeImg from '../assets/edgecontrol.svg';
import { noop } from 'lodash-es';

type IEditor = Editor;

export type Options = {
fill: string;
style: fabric.IObjectOptions['cornerStyle'];
};

interface PointIndexPolygon extends fabric.Polygon {
pointIndex: number;
__corner: string;
_setPositionDimensions: (...args: any[]) => any;
}

interface PointIndexControl extends fabric.Control {
pointIndex: number;
}

const actionHandler: fabric.Control['actionHandler'] = function (
eventData: MouseEvent,
transform: fabric.Transform,
x: number,
y: number
) {
const polygon = transform.target as PointIndexPolygon,
currentControl = polygon.controls[polygon.__corner] as PointIndexControl,
mouseLocalPosition = polygon.toLocalPoint(new fabric.Point(x, y), 'center', 'center'),
polygonBaseSize = getObjectSizeWithStroke(polygon),
size = polygon._getTransformedDimensions(0, 0);
if (polygon.points == null) return false;
polygon.points[currentControl.pointIndex] = new fabric.Point(
(mouseLocalPosition.x * polygonBaseSize.x) / size.x + polygon.pathOffset.x,
(mouseLocalPosition.y * polygonBaseSize.y) / size.y + polygon.pathOffset.y
);
return true;
};
const anchorWrapper = function (anchorIndex: number, fn: fabric.Control['actionHandler']) {
return function (eventData: MouseEvent, transform: fabric.Transform, x: number, y: number) {
const fabricObject = transform.target as PointIndexPolygon;
if (fabricObject.points == null) return false;
const absolutePoint = fabric.util.transformPoint(
new fabric.Point(
fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x,
fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y
),
fabricObject.calcTransformMatrix()
),
actionPerformed = fn(eventData, transform, x, y),
newDim = fabricObject._setPositionDimensions({}),
polygonBaseSize = getObjectSizeWithStroke(fabricObject),
newX = (fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x) / polygonBaseSize.x,
newY = (fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y) / polygonBaseSize.y;
const originX = (newX + 0.5) as any;
const originY = (newY + 0.5) as any;
fabricObject.setPositionByOrigin(absolutePoint, originX, originY);
return actionPerformed;
};
};
const getObjectSizeWithStroke = function (object: fabric.Object) {
const stroke = new fabric.Point(
object.strokeUniform ? 1 / object.scaleX! : 1,
object.strokeUniform ? 1 / object.scaleY! : 1
).multiply(object.strokeWidth!);
return new fabric.Point(object.width! + stroke.x, object.height! + stroke.y);
};
const polygonPositionHandler = function (
this: PointIndexControl,
dim: any,
finalMatrix: any,
fabricObject: any
) {
const x = fabricObject.points[this.pointIndex].x - fabricObject.pathOffset.x,
y = fabricObject.points[this.pointIndex].y - fabricObject.pathOffset.y;
// 求出在世界坐标系的位置
return fabric.util.transformPoint(
new fabric.Point(x, y), // 物体坐标系下的位置
fabric.util.multiplyTransformMatrices(
fabricObject.canvas.viewportTransform,
fabricObject.calcTransformMatrix()
)
);
};
function renderIconEdge(
ctx: CanvasRenderingContext2D,
left: number,
top: number,
styleOverride: any,
fabricObject: fabric.Object
) {
const img = document.createElement('img');
img.src = edgeImg;
drawImg(ctx, left, top, img, 25, 25, fabric.util.degreesToRadians(fabricObject.angle || 0));
}

class PolygonModifyPlugin {
public isEdit: boolean;
static pluginName = 'PolygonModifyPlugin';
static events = [];
static apis = ['toggleEdit', 'activeEdit', 'inActiveEdit'];

constructor(public canvas: fabric.Canvas, public editor: IEditor) {
this.isEdit = false;
this.init();
}
init() {
console.info('[PolygonModifyPlugin]: init');
}
_onDeselected: () => any = noop;
_ensureEvent(poly: fabric.Object) {
poly.off('deselected', this._onDeselected);
}
toggleEdit() {
this.isEdit ? this.inActiveEdit() : this.activeEdit();
}
activeEdit() {
this.isEdit = true;
const poly = this.canvas.getActiveObject() as fabric.Polygon;
if (poly && poly.type === 'polygon') {
this._ensureEvent(poly);
if (poly.points == null) return;
const lastControl = poly.points.length - 1;
poly.controls = poly.points.reduce<Record<string, PointIndexControl>>(function (
acc,
point,
index
) {
acc['p' + index] = <PointIndexControl>new fabric.Control({
positionHandler: polygonPositionHandler,
actionHandler: anchorWrapper(index > 0 ? index - 1 : lastControl, actionHandler),
actionName: 'modifyPolygon',
render: renderIconEdge,
});
Object.defineProperty(acc['p' + index], 'pointIndex', { value: index });
return acc;
},
{});
poly.set({
objectCaching: false,
});
poly.hasBorders = !this.isEdit;
this.canvas.requestRenderAll();
this._onDeselected = () => this.inActiveEdit(poly);
poly.on('deselected', this._onDeselected);
}
}
inActiveEdit(poly?: fabric.Polygon) {
this.isEdit = false;
poly = poly || (this.canvas.getActiveObject() as fabric.Polygon);
if (poly && poly.type === 'polygon') {
poly.cornerColor = 'blue';
poly.cornerStyle = 'rect';
poly.controls = fabric.Object.prototype.controls;
poly.hasBorders = !this.isEdit;
poly.set({
objectCaching: true,
});
if (this._onDeselected) {
poly.off('deselected', this._onDeselected);
this._onDeselected = noop;
}
}
this.canvas.requestRenderAll();
}
}

export default PolygonModifyPlugin;
18 changes: 18 additions & 0 deletions packages/core/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,28 @@ export function downFile(fileStr: string, fileType: string) {
anchorEl.remove();
}

export function drawImg(
ctx: CanvasRenderingContext2D,
left: number,
top: number,
img: HTMLImageElement,
wSize: number,
hSize: number,
angle: number | undefined
) {
if (angle === undefined) return;
ctx.save();
ctx.translate(left, top);
ctx.rotate(angle);
ctx.drawImage(img, -wSize / 2, -hSize / 2, wSize, hSize);
ctx.restore();
}

export default {
getImgStr,
downFile,
selectFiles,
insertImgFile,
clipboardText,
drawImg,
};
21 changes: 21 additions & 0 deletions src/components/edit.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup lang="ts">
import useSelect from '@/hooks/select';
const { mixinState, canvasEditor } = useSelect();
import { Message } from 'view-ui-plus';
const onEditPolygon = () => {
const obj = canvasEditor.fabricCanvas?.getActiveObject();
if (obj && obj.type === 'polygon') {
canvasEditor.activeEdit();
} else {
Message.warning('请检查选择polygon');
}
};
</script>

<template>
<Tooltip :content="$t('quick.editPoly')" v-if="mixinState.mSelectMode === 'one'">
<Button long @click="onEditPolygon" icon="md-brush" type="text"></Button>
</Tooltip>
</template>

<style scoped lang="less"></style>
5 changes: 3 additions & 2 deletions src/language/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@
"del": "删除",
"copy": "复制",
"lock": "锁定",
"hide": "隐藏"
"hide": "隐藏",
"editPoly": "编辑多边形"
},
"insertFile": {
"insert": "插入",
Expand Down Expand Up @@ -226,4 +227,4 @@
"myMaterial": {
"uploadBtn": "上传素材"
}
}
}
4 changes: 4 additions & 0 deletions src/views/home/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
<dele></dele>
<clone></clone>
<hide></hide>
<edit></edit>
</div>
<!-- <Divider plain></Divider> -->
<!-- 居中对齐 -->
Expand Down Expand Up @@ -231,7 +232,9 @@ import Editor, {
MaterialPlugin,
WaterMarkPlugin,
FontPlugin,
PolygonModifyPlugin,
} from '@kuaitu/core';
import Edit from '@/components/edit.vue';
// 创建编辑器
const canvasEditor = new Editor();
Expand Down Expand Up @@ -307,6 +310,7 @@ onMounted(() => {
// 初始化编辑器
canvasEditor.init(canvas);
canvasEditor.use(DringPlugin);
canvasEditor.use(PolygonModifyPlugin);
canvasEditor.use(AlignGuidLinePlugin);
canvasEditor.use(ControlsPlugin);
// canvasEditor.use(ControlsRotatePlugin);
Expand Down
1 change: 1 addition & 0 deletions typings/auto-imports.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
export {}
declare global {
Expand Down

0 comments on commit a4ba260

Please sign in to comment.