偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

圖形編輯器開發(fā):最基礎(chǔ)但卻復(fù)雜的選擇工具

開發(fā) 前端
選擇工具,是一款圖形設(shè)計(jì)軟件最基礎(chǔ)的功能。它的作用是選中的圖形,對(duì)它們進(jìn)行操作,目的是 更新指定圖形屬性。最基礎(chǔ)的操作是移動(dòng),接著是通過(guò)控制點(diǎn)實(shí)現(xiàn)的增強(qiáng)操作??刂泣c(diǎn)操作的兩個(gè)基本能力是旋轉(zhuǎn)和縮放。然后我們會(huì)根據(jù)不同類型的圖形,去實(shí)現(xiàn)不同的控制點(diǎn)邏輯。

大家好,我是前端西瓜哥。

對(duì)于一個(gè)圖形設(shè)計(jì)軟件,它最基礎(chǔ)的工具是什么?選擇工具

但這個(gè)選擇工具,卻是相當(dāng)?shù)膹?fù)雜。這次我來(lái)和各位,細(xì)說(shuō)細(xì)說(shuō)選擇工具的一些彎彎道道。

我正在開發(fā)的圖形設(shè)計(jì)工具:

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

線上體驗(yàn):

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

單選

最基本的,要做到單個(gè)圖形的選中。

光標(biāo)停留在圖形上方,按下鼠標(biāo)左鍵,這個(gè)圖形就被選中了。這就是一個(gè)簡(jiǎn)單的選中了單個(gè)圖形的場(chǎng)景。

注意必須是 mousedown,不是 click。后面會(huì)說(shuō)為什么。

在代碼層,我們會(huì)使用 “圖形拾取” 算法確定光標(biāo)落在哪個(gè)圖形的點(diǎn)擊區(qū)域上,注意考慮隱藏、鎖定、組的情況。

隱藏和鎖定的圖形會(huì)被忽略,如果點(diǎn)的是組下的一個(gè)元素,要將整個(gè)組的所有元素都選中。

清空 被選中圖形集合(暫且叫做 selectSet),然后把這個(gè)圖形添加進(jìn)去。

selectSet.clear()
selectSet.add(targetEl)

選中集合保存的是被選中的圖形,可以保存 id,也可以是圖形對(duì)象。

在渲染層,會(huì)對(duì)被選中的圖形進(jìn)行輪廓高亮,讓用戶有感知。

此外還會(huì)有一個(gè) 矩形選中框,上面還會(huì)有控制點(diǎn),讓用戶可以縮放和旋轉(zhuǎn)圖形。

選中框是圖形的包圍盒,通常是 帶旋轉(zhuǎn)的 OBB 包圍盒。

如果點(diǎn)擊到空白區(qū)域,要將 selectSet 清空。

圖片

多選

有時(shí)候我們希望選中出多個(gè)圖形。

通常的做法是,按住 Shift 鍵,然后點(diǎn)擊一個(gè)圖形。注意是在鼠標(biāo)按下時(shí)就按住

同時(shí)也要 支持取消選中:原來(lái)被選中的一個(gè)圖形,我按住 Shift 再

代碼的核心邏輯是:

如果這個(gè)圖形不在 selectSet 中,將其加入;如果這個(gè)圖形在 selectSet,將其移除。

if (event.shiftKey) {
  if (selectSet.has(targetEl)) {
    selectSet.delete(targetEl)
  } else {
    selectSet.add(targetEl)
  }
}

多個(gè)圖形被選中了,除了給它們高亮輪廓線,我們還需要用一個(gè)更大的矩形選中框包裹所有被選中圖形。

一個(gè)小點(diǎn):如果是取消選中的邏輯,需要鼠標(biāo)釋放后才更新 selectSet。因?yàn)橐乐购秃竺鏁?huì)說(shuō)的按住 Shift 水平垂直拖拽沖突。

圖片

框選

框選,提供了 一次性選中大量特定區(qū)域內(nèi)圖形 的能力。

在空白區(qū)域按下鼠標(biāo)拖拽,然后釋放,可以構(gòu)造出一個(gè)矩形,這個(gè)矩形我們稱為 “選區(qū)”。

圖片

選區(qū)矩形會(huì)和圖形進(jìn)行碰撞檢測(cè)判斷,決定將哪些圖形是被框選中的。

碰撞檢測(cè)有三種方案:

  1. 選區(qū)矩形和選中圖形的包圍盒屬于 包含(contain)關(guān)系;
  2. 選區(qū)矩形和選中圖形的包圍盒屬于 相交(intersect)關(guān)系
  3. 不使用包圍盒,精準(zhǔn)判斷是否有真正的 像素上的相交;

個(gè)人比較推薦相交的判斷方案,figma 也選擇了該方案。

框選可以和多選結(jié)合。即你可以按住 Shift 鍵,然后去框選。

它的效果是和按住 Shift 一個(gè)個(gè)去選中圖形的效果是一樣的。

核心代碼實(shí)現(xiàn):

if (!event.shiftKey) {
  selectSet.clear();
}

for (const el of elementsInScence) {
  // 判斷是否碰撞,這個(gè)方法
  if (isRectIntersect(selectionBox, el)) {
    // 普通框選
    if (!event.shiftKey) {
      selectSet.add(el);
    }
    // 連續(xù)和框選的組合
    else {
      if (selectSet.has(el)) {
        selectSet.delete(el);
      } else {
        selectSet.add(el);
      }
    }
  }
}

移動(dòng)

選擇工具,主要是用來(lái)選擇,選中后一個(gè)很普遍的操作是:移動(dòng)選中元素。

所以這也是它有時(shí)候也被叫做 移動(dòng)工具 的原因。

移動(dòng)的交互過(guò)程:

  1. 光標(biāo)停留在已經(jīng)被選中的圖形上,按下鼠標(biāo)不放。
  2. 然后拖拽鼠標(biāo),被選中圖形跟隨光標(biāo)移動(dòng)。
  3. 釋放鼠標(biāo),表示移動(dòng)到目標(biāo)位置,移動(dòng)結(jié)束。

圖片

代碼核心實(shí)現(xiàn):

  1. 移動(dòng)前此時(shí)記錄圖形的位置,和起始位置。
  2. 拖拽時(shí)計(jì)算相對(duì)位移,更新圖形的位置。
  3. 釋放時(shí)重置狀態(tài),以及記錄到歷史記錄中。
// 圖形移動(dòng)前位置
let elStartCoords = [];
// 鼠標(biāo)按下事件的光標(biāo)位置,計(jì)算偏移量時(shí)作為基準(zhǔn)
let startCoord = { x: undefined, y: undefined };

const onStart = (e) => {
  // 記錄初始坐標(biāo)
  elStartCoords = elements.map((el) => ({ x: el.x, y: el.y }));
  startCoord.x = e.clientX;
  startCoord.y = e.clientY;
};

const onDrag = (e) => {
  // 計(jì)算偏移量,更新坐標(biāo)
  const dx = e.clientX - startCoord.x;
  const dy = e.clientY - startCoord.y;
  elements.forEach((el, i) => {
    el.x = elStartCoords[i].x + dx;
    el.y = elStartCoords[i].y + dy;
  });
};

const onEnd = () => {
  // 重置狀態(tài)
  elStartCoords = [];
  startCoord = { x: undefined, y: undefined };
};

按住 Shift 鍵的垂直水平移動(dòng)

假設(shè)我們做好了幾個(gè)對(duì)齊的圖形,當(dāng)我們移動(dòng)其中一個(gè)圖形的時(shí)候,希望能夠保持原來(lái)的對(duì)齊。

這時(shí)候,限制移動(dòng)為水平或垂直方向就很有用。

通常通過(guò)在拖拽時(shí)按住 Shift 來(lái)開啟這個(gè)能力。

圖片

要點(diǎn):

  1. 拖拽的中途從沒按住 Shift 到按住,要立即響應(yīng),代碼實(shí)現(xiàn)上要補(bǔ)一個(gè)鍵盤事件監(jiān)聽,而不是靠鼠標(biāo)移動(dòng)事件,因?yàn)槟悴灰苿?dòng)鼠標(biāo),被選中元素就不會(huì)更新。
  2. 比較 dx 和 dy 的大小。dx 大,水平移動(dòng);dy 大,垂直移動(dòng)。這樣圖形就能盡量靠近十字線(水平線+垂直線)

對(duì)齊到像素網(wǎng)格

對(duì)齊到網(wǎng)格,開啟后,讓圖形在移動(dòng)的時(shí)候,讓圖片盡量貼到網(wǎng)格線上。

圖片

做法是將一個(gè)或多個(gè)圖形的包圍盒(AABB)的左上角坐標(biāo),進(jìn)行取余,得到一個(gè)落在網(wǎng)格線上的位置,用這位置去更新選中圖形。

擴(kuò)展能力:控制點(diǎn)

選中圖形,是為了對(duì)它們進(jìn)行操作。

這些 操作的實(shí)現(xiàn),要通過(guò)控制點(diǎn)來(lái)落地。

常見的有:

  • 縮放控制點(diǎn),在圖形選中框的 4 個(gè)角上。
  • 旋轉(zhuǎn)控制點(diǎn),拖拽它設(shè)置圖形的旋轉(zhuǎn),旋轉(zhuǎn)控制點(diǎn)。
  • 給圖形設(shè)置漸變填充色,需要指定兩種顏色的顏色和位置,需要的 漸變色控制點(diǎn)。

下面是 figma 的縮放和旋轉(zhuǎn)演示,我開發(fā)的編輯器還沒實(shí)現(xiàn)完整。

圖片

此外,不同圖形繪制工具可能會(huì)有它們獨(dú)有的操作方式,這些都需要你根據(jù)圖形的特性去設(shè)計(jì)。

看看 Figma 對(duì)不同圖形的特殊控制點(diǎn)邏輯。

圖片

所以選擇工具模塊在設(shè)計(jì)上,要提供 注冊(cè)各種類型圖形控制點(diǎn)邏輯 的能力。

在 “圖形拾取” 時(shí),要把控制點(diǎn)也考慮進(jìn)來(lái),光標(biāo)是否點(diǎn)在控制點(diǎn)上。

如果點(diǎn)在控制點(diǎn)上,拖拽邏輯就要走控制點(diǎn)的邏輯,不再走選擇工具的基礎(chǔ)邏輯。

其他

還有一些可考慮實(shí)現(xiàn)的增強(qiáng)能力:

  • 雙擊,進(jìn)入編輯模式,進(jìn)行一些更復(fù)雜的操作,比如可以變成貝塞爾曲線操作任意點(diǎn)。
  • 移動(dòng)時(shí),用線條顯示和其他圖形的點(diǎn)(比如中點(diǎn)、選中框角落的 4 個(gè)點(diǎn))的距離,并在很接近時(shí)吸附過(guò)去。

結(jié)尾

總結(jié)一下,選擇工具,是一款圖形設(shè)計(jì)軟件最基礎(chǔ)的功能。

它的作用是選中的圖形,對(duì)它們進(jìn)行操作,目的是 更新指定圖形屬性。

最基礎(chǔ)的操作是移動(dòng),接著是通過(guò)控制點(diǎn)實(shí)現(xiàn)的增強(qiáng)操作。

控制點(diǎn)操作的兩個(gè)基本能力是旋轉(zhuǎn)和縮放。然后我們會(huì)根據(jù)不同類型的圖形,去實(shí)現(xiàn)不同的控制點(diǎn)邏輯。

說(shuō)是工具的一種,但它其實(shí)的定位更多是底層的基礎(chǔ)建設(shè)。

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

2023-09-07 08:24:35

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

2023-10-19 10:12:34

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

2023-02-06 16:59:57

Canvas編輯器

2023-09-26 07:39:21

2023-08-31 11:32:57

圖形編輯器contain

2023-10-08 08:11:40

圖形編輯器快捷鍵操作

2023-09-11 09:02:31

圖形編輯器模塊間的通信

2024-01-08 08:30:05

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

2023-01-18 08:30:40

圖形編輯器元素

2023-02-01 09:21:59

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

2023-04-07 08:02:30

圖形編輯器對(duì)齊功能

2023-08-28 08:10:50

Hex圖形編輯器

2023-10-10 16:04:30

圖形編輯器格式轉(zhuǎn)換

2023-04-10 08:45:44

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

2024-01-03 08:43:17

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

2023-07-07 13:56:01

圖形編輯器畫布縮放

2023-07-31 08:46:07

圖形編輯器圖形自動(dòng)對(duì)齊

2023-02-02 14:07:00

圖形編輯器Canvas

2023-08-08 08:12:25

圖形編輯器幾何算法

2015-04-24 10:54:58

JavaScript開發(fā)工具代碼編輯器
點(diǎn)贊
收藏

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