成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

圖形編輯器:工具管理和切換

開發(fā) 前端
因?yàn)椴恢烙脩魰诋嫴忌袭嬌隙嗌賵D形,所以需要在渲染引擎上下很大的功夫,去提高繪制的性能。性能決定了編輯器的上限,這也是為什么很多編輯器選擇了 Canvas 作為繪制方案。

大家好,我是前端西瓜哥。今天我們看看對于一款圖形編輯器,應(yīng)該怎么去實(shí)現(xiàn)工具,比如繪制矩形、選中工具,以及如何去管理它們的。

項(xiàng)目地址,歡迎 star:

https://github.com/F-star/suika

線上體驗(yàn):

https://blog.fstars.wang/app/suika/

一款編輯器,有兩個(gè)很重要的方面,一個(gè)是性能,另一個(gè)是架構(gòu)。

因?yàn)椴恢烙脩魰诋嫴忌袭嬌隙嗌賵D形,所以需要在渲染引擎上下很大的功夫,去提高繪制的性能。性能決定了編輯器的上限,這也是為什么很多編輯器選擇了 Canvas 作為繪制方案。

另一個(gè)則是架構(gòu),編輯器很復(fù)雜,即便是看上去很簡單編輯器。因?yàn)槔锩娴哪K非常多,比如工具管理模塊、縮放管理、歷史記錄、圖形樹維護(hù)、輔助線、標(biāo)尺、設(shè)置、視口管理、熱鍵、光標(biāo)維護(hù)等等。如果模塊化不夠好,就會導(dǎo)致代碼擴(kuò)展性差,加功能會非常痛苦。

今天西瓜哥談?wù)勅绾卧O(shè)計(jì)管理工具類,管理不同的工具。

工具類

工具的交互,通常會集中于用戶的鼠標(biāo)操作

比如繪制矩形,按下鼠標(biāo),會確定矩形的 x、y 值,然后拖拽鼠標(biāo),調(diào)整矩形的寬高,最后放開鼠標(biāo),矩形的形狀就確認(rèn)好了,并將這個(gè)繪制矩形的操作記錄到歷史操作中。如下圖:

圖片

所以,工具類(Tool)的設(shè)計(jì)為:

export interface ITool {
type: string; // 工具類
active: () => void; // 切換為當(dāng)前工具時(shí)調(diào)用
inactive: () => void; // 切換為其他工具時(shí)調(diào)用
start: (event: PointerEvent) => void; // 鼠標(biāo)按下
drag: (event: PointerEvent) => void; // 拖拽
end: (event: PointerEvent) => void; // 鼠標(biāo)釋放
moveExcludeDrag: (event: PointerEvent) => void; // 拖拽之外的鼠標(biāo)移動(dòng)
}

class DrawRectTool implements ITool {
// ...
}

有點(diǎn)像我們 Rect 和 Vue 中的組件的概念。這是因?yàn)楣ぞ哳惐举|(zhì)也是 在生命周期內(nèi)觸發(fā)一些鉤子(hook),拿到一些信息。

type 表示工具名稱,是一個(gè)標(biāo)識符,切換工具時(shí)會用到。

active 方法會在切換為當(dāng)前工具時(shí)調(diào)用,通常會做的事情有:

  1. 設(shè)置光標(biāo)樣式;
  2. 設(shè)置一些監(jiān)聽器,比如繪制矩形監(jiān)聽 shift 鍵是否按下,如果按下,就繪制方形;

inactive 會在切換為其他工具時(shí)調(diào)用,通常就是將光標(biāo)設(shè)置為默認(rèn)值,取消監(jiān)聽器。

start 是鼠標(biāo)按下事件,此時(shí)要記錄一些初始狀態(tài),后面的事件需要基于這個(gè)初始狀態(tài)進(jìn)行計(jì)算。這里其實(shí)我沒用鼠標(biāo)事件,而是用了 pointer 指針事件,一種適用范圍更廣的事件,除了鼠標(biāo)事件,還支持觸控筆和觸摸屏幕等場景。因?yàn)榇蠹伊?xí)慣鼠標(biāo)事件,后面我都用鼠標(biāo)事件來描述。

drag 就是鼠標(biāo)拖拽事件。end 是鼠標(biāo)釋放事件。

最后是比較特殊的 moveExcludeDrag,代表除了拖拽場景的鼠標(biāo)移動(dòng),比如選擇工具,懸停在一個(gè)圖形上,我們就可以用這個(gè)事件來判斷是哪個(gè)圖形被選中,對它進(jìn)行高亮。

這就是最基本的工具類,在此上我們可以進(jìn)行進(jìn)一步地封裝,比如更改光標(biāo)樣式,我們可以配個(gè) normalCursor、dragCursor 屬性,讓調(diào)用者幫我們統(tǒng)一設(shè)置光標(biāo)樣式。

這里的調(diào)用者就是工具管理類。

工具管理類

工具管理類支持的能力:

  1. 維護(hù)映射表,用 type 映射到對應(yīng)工具實(shí)例;
  2. 使用 setTool 方法切換工具,會根據(jù)傳入的字符串在映射表中找到對應(yīng)工具實(shí)例,然后調(diào)用舊的工具的 inactive 方法,再調(diào)用新工具的 active 方法,然后設(shè)置 this.currentTool 為新工具實(shí)例;
  3. 支持事件訂閱,監(jiān)聽工具的切換,提供給 UI 層去監(jiān)聽。比如我們用快捷鍵切換工具時(shí),UI 層就能通過監(jiān)聽獲得最新工具標(biāo)識符,將對應(yīng)按鈕設(shè)置為激活狀態(tài);
  4. 然后是給 DOM 元素掛載監(jiān)聽器,canvas 上掛載鼠標(biāo)按下事件,然后是特殊的,給 window 掛載鼠標(biāo)移動(dòng)和鼠標(biāo)。為什么不給 canvas 掛載這些事件,這是因?yàn)槲覀兛赡軙谕献r(shí)將鼠標(biāo)移出 canvas 甚至瀏覽器界面然后釋放,會導(dǎo)致拖拽、釋放事件沒能觸發(fā)。監(jiān)聽后,就會在何時(shí)的時(shí)機(jī)調(diào)用工具類的 start、drag、end 等方法。

ToolManager 實(shí)現(xiàn)如下:

class ToolManager {
toolMap = new Map<string, ITool>();
currentTool: ITool | null = null;
eventEmitter: EventEmitter;
_unbindEvent: () => void;

constructor(private editor: Editor) {
this.eventEmitter = new EventEmitter(); // 模仿 nodejs 的簡易版 EventEmitter
// 綁定 tool
this.toolMap.set(DrawRectTool.type, new DrawRectTool(editor));
this.toolMap.set(DrawEllipseTool.type, new DrawEllipseTool(editor));
this.toolMap.set(SelectTool.type, new SelectTool(editor));
this.toolMap.set(DragCanvasTool.type, new DragCanvasTool(editor));

this.setTool(DrawRectTool.type);

this._unbindEvent = this.bindEvent();
}
getToolName() {
return this.currentTool?.type;
}
bindEvent() {
let isPressing = false;

const handleDown = (e: PointerEvent) => {
if (e.button !== 0) { // 必須是鼠標(biāo)左鍵
return;
}
if (!this.currentTool) {
throw new Error('未設(shè)置當(dāng)前使用工具');
}
isPressing = true;
this.currentTool.start(e);
};
const handleMove = (e: PointerEvent) => {
if (!this.currentTool) {
throw new Error('未設(shè)置當(dāng)前使用工具');
}
if (isPressing) {
this.editor.hostEventManager.disableDragBySpace();
this.currentTool.drag(e);
} else {
this.currentTool.moveExcludeDrag(e);
}
};
const handleUp = (e: PointerEvent) => {
if (e.button !== 0) { // 必須是鼠標(biāo)左鍵
return;
}
if (!this.currentTool) {
throw new Error('未設(shè)置當(dāng)前使用工具');
}
if (isPressing) {
this.editor.hostEventManager.enableDragBySpace();
isPressing = false;
this.currentTool.end(e);
}
};
const canvas = this.editor.canvasElement;
canvas.addEventListener('pointerdown', handleDown);
window.addEventListener('pointermove', handleMove);
window.addEventListener('pointerup', handleUp);

return function unbindEvent() {
canvas.removeEventListener('pointerdown', handleDown);
window.removeEventListener('pointermove', handleMove);
window.removeEventListener('pointerup', handleUp);
};
}
unbindEvent() {
this._unbindEvent();
this._unbindEvent = noop;
}
setTool(toolName: string) {
const prevTool = this.currentTool;
const currentTool = this.currentTool = this.toolMap.get(toolName) || null;
if (!currentTool) {
throw new Error(`沒有 ${toolName} 對應(yīng)的工具對象`);
}
prevTool && prevTool.inactive();
currentTool.active();
this.eventEmitter.emit('change', currentTool.type);
}
on(eventName: 'change', handler: (toolName: string) => void) {
this.eventEmitter.on(eventName, handler);
}
off(eventName: 'change', handler: (toolName: string) => void) {
this.eventEmitter.off(eventName, handler);
}
destroy() {
this.currentTool?.inactive();
}
}

結(jié)尾

工具管理類基礎(chǔ)的設(shè)計(jì)就是這樣。因?yàn)槭腔谏芷谌ピO(shè)計(jì)的,所以看起來挺像 React、Vue 的組件寫法的。

責(zé)任編輯:姜華 來源: 前端西瓜哥
相關(guān)推薦

2023-09-07 08:24:35

圖形編輯器開發(fā)繪制圖形工具

2023-10-19 10:12:34

圖形編輯器開發(fā)縮放圖形

2023-10-08 08:11:40

圖形編輯器快捷鍵操作

2023-02-02 14:07:00

圖形編輯器Canvas

2023-08-31 11:32:57

圖形編輯器contain

2023-06-12 08:22:56

圖形編輯器工具

2023-01-18 08:30:40

圖形編輯器元素

2023-02-01 09:21:59

圖形編輯器標(biāo)尺

2023-04-07 08:02:30

圖形編輯器對齊功能

2024-01-03 08:43:17

圖形編輯器旋轉(zhuǎn)控制點(diǎn)縮放控制點(diǎn)

2011-01-10 16:17:49

2023-09-26 07:39:21

2023-04-10 08:45:44

圖形編輯器排列移動(dòng)功能

2023-09-11 09:02:31

圖形編輯器模塊間的通信

2023-05-09 08:15:32

圖形編輯器撤銷重做功能

2024-01-08 08:30:05

光標(biāo)圖形編輯器開發(fā)游標(biāo)

2013-06-18 01:22:46

CocoStudio工Cocos2d-x

2023-02-09 07:02:30

圖形編輯器修改圖形

2023-08-28 08:10:50

Hex圖形編輯器

2023-10-10 16:04:30

圖形編輯器格式轉(zhuǎn)換
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 欧美三级在线 | 91大片 | 91天堂网| 欧美中文在线 | 亚洲精品视频一区 | 久草综合在线 | 中文字幕在线播放不卡 | 色视频在线播放 | 成人精品国产 | 久久久久一区二区三区四区 | 欧美黄色网络 | 欧美一区二区三区国产精品 | 99re视频在线观看 | 日韩字幕 | 伊人久久在线 | 一区二区三区亚洲 | 国产婷婷综合 | 成人片免费看 | 91综合网 | 久久亚 | 日本欧美在线视频 | 国产免费福利小视频 | 国产欧美视频一区二区三区 | 欧美在线一区二区三区 | 成人a免费 | 国产一级片免费视频 | 九九综合| 天天碰日日操 | 天天天操操操 | 在线播放中文字幕 | 黄色毛片黄色毛片 | 国产色婷婷精品综合在线手机播放 | 精品不卡 | 成人在线观看免费爱爱 | 欧美伊人久久久久久久久影院 | 免费v片 | 国产欧美精品一区二区 | 国产精品.xx视频.xxtv | 免费人成在线观看网站 | 在线观看三级av | 国产精品久久久久久久免费大片 |