Skip to content

Commit

Permalink
feat: ✨ 画布元素锁定
Browse files Browse the repository at this point in the history
  • Loading branch information
Qiu-Jun committed Dec 26, 2024
1 parent 2e5a199 commit eaf1d85
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 36 deletions.
11 changes: 9 additions & 2 deletions src/components/QuickOperation/components/Lock.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@
* @Author: June
* @Description: Description
* @Date: 2024-08-19 12:53:30
* @LastEditTime: 2024-11-28 14:25:10
* @LastEditTime: 2024-12-26 14:07:37
* @LastEditors: June
-->
<template>
<el-tooltip :content="$t('editor.quickOperation.lock')" v-if="isOne">
<el-tooltip
:content="
isLock
? $t('editor.quickOperation.unlock')
: $t('editor.quickOperation.lock')
"
v-if="isOne"
>
<el-button
v-if="isLock"
@click="doLock(false)"
Expand Down
7 changes: 7 additions & 0 deletions src/lib/core/assets/lock.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
212 changes: 179 additions & 33 deletions src/lib/core/plugin/LockPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,77 +2,223 @@
* @Author: 秦少卫
* @Date: 2024-07-04 14:27:05
* @LastEditors: June
* @LastEditTime: 2024-07-26 09:00:11
* @LastEditTime: 2024-12-26 14:03:10
* @Description: 锁定文件
*/
import { fabric } from 'fabric';
import type Editor from '../Editor';
import { SelectMode } from '../eventType';
import { fabric } from 'fabric'
import { SelectEvent, SelectMode } from '../eventType'
import Editor from '../Editor'
import lockImg from '../assets/lock.svg'

enum ItypeKey {
lockMovementX = 'lockMovementX',
lockMovementY = 'lockMovementY',
lockRotation = 'lockRotation',
lockScalingX = 'lockScalingX',
lockScalingY = 'lockScalingY',
lockScalingY = 'lockScalingY'
}

enum IControlKey {
bl = 'bl',
br = 'br',
mb = 'mb',
ml = 'ml',
mr = 'mr',
mt = 'mt',
tl = 'tl',
tr = 'tr',
mtr = 'mtr',
lock = 'lock'
}

export default class LockPlugin implements IPluginTempl {
static pluginName = 'LockPlugin';
static apis = ['lock', 'unLock'];
constructor(public canvas: fabric.Canvas, public editor: Editor) {}
static pluginName = 'LockPlugin'
static apis = ['lock', 'unLock']
constructor(
public canvas: fabric.Canvas,
public editor: Editor
) {
this.init()
}

init() {
const imgEl = document.createElement('img')
imgEl.src = lockImg
const that = this
function renderIcon(
ctx: CanvasRenderingContext2D,
left: number,
top: number,
styleOverride: any,
fabricObject: fabric.Object
) {
const iconWith = 25
ctx.save()
ctx.translate(left, top)
const angle = fabricObject.angle as number
ctx.rotate(fabric.util.degreesToRadians(angle))
ctx.drawImage(imgEl, -iconWith / 2, -iconWith / 2, iconWith, iconWith)
ctx.restore()
}

function unLockObject(eventData: any, transform: any): boolean {
that.unLock()
return true
}

fabric.Object.prototype.controls.lock = new fabric.Control({
x: 0.5,
y: 0.5,
offsetY: 0,
cursorStyle: 'pointer',
mouseUpHandler: unLockObject,
render: renderIcon
})

fabric.Textbox.prototype.controls.lock = new fabric.Control({
x: 0.5,
y: 0.5,
offsetY: 0,
cursorStyle: 'pointer',
mouseUpHandler: unLockObject,
render: renderIcon
})
this.canvas.on('selection:created', () => this.renderCornerByActiveObj())
this.canvas.on('selection:updated', () => this.renderCornerByActiveObj())

// 鼠标框选不能多选锁定元素
;(fabric.Canvas.prototype as any)._groupSelectedObjects = function (
e: any
) {
const group = this._collectObjects(e)
let aGroup

for (let i = group.length - 1; i >= 0; i--) {
if (group[i].lockMovementX) {
group.splice(i, 1)
}
}

// do not create group for 1 element only
if (group.length === 1) {
this.setActiveObject(group[0], e)
} else if (group.length > 1) {
aGroup = new fabric.ActiveSelection(group.reverse(), {
canvas: this
})
this.setActiveObject(aGroup, e)
}
}

// shift+左键点选不能多选锁定元素
;(fabric.Canvas.prototype as any)._handleGrouping = function (
e: any,
target: fabric.Object
) {
const activeObject = this._activeObject
// avoid multi select when shift click on a corner
if (activeObject.__corner) {
return
}

if (target.lockMovementX) return
if (activeObject.lockMovementX) return

if (target === activeObject) {
// if it's a group, find target again, using activeGroup objects
target = this.findTarget(e, true)
// if even object is not found or we are on activeObjectCorner, bail out
if (!target || !target.selectable) {
return
}
if (target.lockMovementX) return
}
if (activeObject && activeObject.type === 'activeSelection') {
this._updateActiveSelection(target, e)
} else {
this._createActiveSelection(target, e)
}
}
}

controlCornersVisible(obj: fabric.Object) {
const isLocked = obj.lockMovementX
Object.values(IControlKey).forEach((key: IControlKey) => {
if (key === IControlKey.lock) {
obj.setControlVisible(key, isLocked)
} else {
obj.setControlVisible(key, !isLocked)
}
})
}

renderCornerByActiveObj() {
const actives = this.canvas
.getActiveObjects()
.filter((item) => !(item instanceof fabric.GuideLine))
if (actives && actives.length === 1) {
const active = actives[0]
this.controlCornersVisible(active)
} else if (actives && actives.length > 1) {
const active = this.canvas.getActiveObject()
if (active) {
this.controlCornersVisible(active)
}
}
}

hookImportAfter() {
this.canvas.forEachObject((obj) => {
this.canvas.forEachObject((obj: fabric.Object) => {
if (obj.hasControls === false && obj.selectable === false) {
this.canvas.setActiveObject(obj);
this.lock();
this.canvas.setActiveObject(obj)
this.lock()
}
});
return Promise.resolve();
})
return Promise.resolve()
}

lock() {
const activeObject = this.canvas.getActiveObject() as fabric.Object;
const activeObject = this.canvas.getActiveObject() as fabric.Object
if (activeObject) {
activeObject.hasControls = false;
activeObject.selectable = false;
activeObject.evented = false;
// 修改默认属性
Object.values(ItypeKey).forEach((key: ItypeKey) => {
activeObject[key] = true;
});
this.canvas.discardActiveObject().renderAll();
activeObject[key] = true
})
this.controlCornersVisible(activeObject)
this.canvas.renderAll()
this.editor.emit(SelectEvent.ONE, [activeObject])
}
}

unLock() {
const activeObject = this.canvas.getActiveObject() as fabric.Object;
const activeObject = this.canvas.getActiveObject() as fabric.Object
if (activeObject) {
activeObject.hasControls = true;
activeObject.selectable = true;
activeObject.evented = true;
activeObject.hasControls = true
activeObject.selectable = true
activeObject.evented = true
// 修改默认属性
Object.values(ItypeKey).forEach((key: ItypeKey) => {
activeObject[key] = false;
});
this.canvas.discardActiveObject().renderAll();
activeObject[key] = false
})
this.controlCornersVisible(activeObject)
this.canvas.renderAll()
this.editor.emit(SelectEvent.ONE, [activeObject])
}
}

contextMenu() {
const selectedMode = this.editor.getSelectMode();
const activeObject = this.canvas.getActiveObject();
const selectedMode = this.editor.getSelectMode()
const activeObject = this.canvas.getActiveObject()
if (selectedMode === SelectMode.ONE && activeObject) {
if (activeObject.selectable) {
return [{ text: '锁定', hotkey: '', onclick: () => this.lock() }];
return [{ text: '锁定', hotkey: '', onclick: () => this.lock() }]
} else {
return [{ text: '解锁', hotkey: '', onclick: () => this.unLock() }];
return [{ text: '解锁', hotkey: '', onclick: () => this.unLock() }]
}
}
}

destroy() {
console.log('pluginDestroy');
console.log('pluginDestroy')
}
}
}
1 change: 1 addition & 0 deletions src/locales/langs/en/editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"quickOperation": {
"title": "QuickOperation",
"lock": "Lock",
"unlock": "UnLock",
"editPoly": "EditPoly",
"delete": "Delete",
"copy": "Copy",
Expand Down
1 change: 1 addition & 0 deletions src/locales/langs/zh-CN/editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@
"quickOperation": {
"title": "快捷操作",
"lock": "锁定",
"unlock": "解锁",
"delete": "删除",
"copy": "复制",
"editPoly": "编辑多边形",
Expand Down
6 changes: 5 additions & 1 deletion vite.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* @Description:
* @Date: 2024-07-24 17:34:22
* @LastEditors: June
* @LastEditTime: 2024-11-29 12:07:04
* @LastEditTime: 2024-12-26 14:13:40
* @FilePath: /element-fabric-editor/vite.config.mts
*/

Expand Down Expand Up @@ -33,6 +33,10 @@ export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => {
target: 'https://github.com/',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/fontFile/, '')
},
'/api': {
target: 'https://www.kuaitu.cc/',
changeOrigin: true
}
}
},
Expand Down

0 comments on commit eaf1d85

Please sign in to comment.