iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 26
1
Modern Web

Fabricjs 筆記系列 第 26

Day 26 - Fabricjs 進階自訂控制項

今天要來做更進階的自訂控制項,不只能夠修改內建的一些樣式,夠能夠讓我們使用自己想要的圖示來做控制項,甚至變更每個控制項的功能。

今日就來做把左上控制項改成刪除功能。

物件控制一些基礎樣式調整可以參考
Day 15 - Fabricjs 物件控制項樣式調整

透過複寫原本的方法

因為 fabricjs 並沒有提供 api 能夠讓我們去改變物件的控制項,所以我們必須透過複寫原來 fabricjs 所繪畫控制項的方法,來達到自訂控制項的目的。

這邊講一下自己再複寫的心得,到 fabricjs docs 找有沒有對應的方法。可以看到我找到了 Object.drawControls 這個方法。

接著看到的 Source 點選 line 15906 連結可觀看 fabricjs 原始碼這個方法的所在地。

點選 line 15906

就能看到 drawControls 原本的樣貌啦

複寫 fabric.Object.prototype.drawControls

我們想要做的是改變所有產生出來的物件的控制項,所以我們必須要來複寫 fabricjs 原本畫出控制項的方法。

也就是 fabric.Object.prototype.drawControls,這個方法為 fabricjs 原本畫出控制項圖示的方法。

再覆蓋之前,我們得先去參考原本的寫法如何,再來做客製化修改。
Fabricjs 原始碼 #line15906 drawControls

// 變更所有物件畫出的控制項
fabric.Object.prototype.drawControls = function (ctx, styleOverride) {
  // 複寫他,改成什麼都不畫
}

const rect = new fabric.Rect({
  width: 100,
  height: 100,
  left: 100,
  top: 100
})

canvas.add(rect)

可看到因為把 drawControls 覆蓋過去了,所以看不到任何控制項。

接著直接將

fabricjs drawControls method source: http://fabricjs.com/docs/fabric.js.html#line15906

整段 call 下來複寫,就會正常畫出控制項了。

接著分析一下原始碼,可以發現控制向都是透過一個名為 _drawControl 的私有函數所畫出來的。

// top-left
this._drawControl('tl', ctx, methodName,
  left,
  top, styleOverride);

把任何一個拿掉就會少掉那一個控制項。

接著我們把原本的 _drawControl 拿掉,換成我們自己的方式畫出控制項。

// top-left
const cancel = new Image()
cancel.src = 'https://upload.wikimedia.org/wikipedia/commons/6/65/Crystal_button_cancel.svg'
ctx.drawImage(cancel, left, top, this.cornerSize, this.cornerSize)

OK,這樣一來,就能夠有自己定義的控制項圖示了。

不過這時我們是想將左上角的控制向改成刪除的功能,但發現只是改變了圖示,並沒有連功能都一起被改變。

再次來複寫 fabric.Object 的 prototype

接下來來改寫原本是縮放功能的控制項,改成刪除的功能。
一樣先看了 fabricjs doc 不過發現好像沒有什麼線索,只好透過 google 大神。

大神告訴我 _getActionFromCorner 這個方法,看起來就像是我們所需要的,稍微在看了一下 fabricjs 原始碼,大概了解了前因後果。

  1. __onMouseDown 當在 canvas 中點下後觸發。其中就判斷一些你是否點在物件上,若點選在物件上則觸發 _setupCurrentTransform 來設定接下來要轉變資訊。
  2. _setupCurrentTransform 設定轉變資訊,其中有一個 _getActionFromCorner 方法判斷點選的控制項使用什麼方法。
  3. _getActionFromCorner 紀錄會做的動作,因為原本的控制項就只有縮放和旋轉,都只是在之後 mousemove 發生時會做的動作,所以只有紀錄接下來會發生甚麼事情,而我們要改變的刪除方法,加在這邊就可以了。

複寫 fabric.Canvas.prototype._getActionFromCorner

這邊很簡單就複寫 _getActionFromCorner 方法,在 switch case 加上自己自訂的方法就可以了。

fabric.Canvas.prototype._getActionFromCorner = function(target, corner, e) {
      if (!corner) {
        return 'drag';
      }
      switch (corner) {
        case 'mtr':
          return 'rotate';
        case 'ml':
          break;
        case 'mr':
          return e[this.altActionKey] ? 'skewY' : 'scaleX';
        case 'mt':
        case 'mb':
          return e[this.altActionKey] ? 'skewX' : 'scaleY';
        case 'tl': // 增加左上刪除動作
          canvas.remove(target)
          return 'deleted'
        default:
          return 'scale';
      }
    }

改變鼠標樣式

最後我們將原本縮放的箭頭樣式,改成手指 pointer 樣式。

直接修改 fabric.Canvas.prototype.cursorMap 這個陣列就可以了。

  var cursorOffset = {
        mt: 0, // n
        tr: 1, // ne
        mr: 2, // e
        br: 3, // se
        mb: 4, // s
        bl: 5, // sw
        ml: 6, // w
        tl: 7 // nw
      }

fabric.Canvas.prototype.cursorMap[7] = 'pointer'

參考 fabricjs 原始碼 #line10929

這樣就完成客製化自己的控制項囉,將左上角改為刪除功能

本日小結

fabricjs 對使用自訂控制項,並沒有提供簡易的 api 來變換控制項。

但還是能透過修改原本 fabricjs 提供的方法來達到需求。

缺點是若版本不同,作法可能會不同,因為不是正規提供的方法,且要複寫必須要深入 fabricjs 原始碼了解原本寫法,較麻煩。

但有人提供了一個良好的解決方案 - fabricjs-customise-controls-extension
來解決這個問題,明天再來介紹。

價值 $150 的問題

Ability to customize controls $150
有人懸賞了 150 美元為了解決這個問題,其實一些 fabricjs 主要的貢獻者也有想要修改此問題,加入能夠讓開發者好用的 api,從 2015 年就開始討論此問題 (客製化 controls icon & 功能),不過到現在還沒有被完美的被解決。

貢獻者表示他還有其他更重要的事情要做 XD

最新的進展是寫上面那個 extension 的人想協助將套件加入 fabricjs 核心,不過因為一直在更新似乎還沒有看到結果。

寫這篇文章時版本為 2.4.3

參考連結


上一篇
Day 25 - Fabricjs 實作: 拼貼圖片
下一篇
Day 27 - 使用插件客製控制項
系列文
Fabricjs 筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
吹雪
iT邦新手 5 級 ‧ 2023-03-03 15:48:37

有个疑问 当我在某个控件上使用了setControlsVisibility设置这个控件某个Control的可见性后,其它的控件也被修改了,有什么好的解决办法嘛?

我要留言

立即登入留言