
今天要來做更進階的自訂控制項,不只能夠修改內建的一些樣式,夠能夠讓我們使用自己想要的圖示來做控制項,甚至變更每個控制項的功能。
今日就來做把左上控制項改成刪除功能。
物件控制一些基礎樣式調整可以參考
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 原始碼,大概了解了前因後果。
__onMouseDown 當在 canvas 中點下後觸發。其中就判斷一些你是否點在物件上,若點選在物件上則觸發 _setupCurrentTransform 來設定接下來要轉變資訊。_setupCurrentTransform 設定轉變資訊,其中有一個 _getActionFromCorner 方法判斷點選的控制項使用什麼方法。_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 對使用自訂控制項,並沒有提供簡易的 api 來變換控制項。
但還是能透過修改原本 fabricjs 提供的方法來達到需求。
缺點是若版本不同,作法可能會不同,因為不是正規提供的方法,且要複寫必須要深入 fabricjs 原始碼了解原本寫法,較麻煩。
但有人提供了一個良好的解決方案 - fabricjs-customise-controls-extension
來解決這個問題,明天再來介紹。
Ability to customize controls $150
有人懸賞了 150 美元為了解決這個問題,其實一些 fabricjs 主要的貢獻者也有想要修改此問題,加入能夠讓開發者好用的 api,從 2015 年就開始討論此問題 (客製化 controls icon & 功能),不過到現在還沒有被完美的被解決。
貢獻者表示他還有其他更重要的事情要做 XD

最新的進展是寫上面那個 extension 的人想協助將套件加入 fabricjs 核心,不過因為一直在更新似乎還沒有看到結果。
寫這篇文章時版本為
2.4.3
有个疑问 当我在某个控件上使用了setControlsVisibility设置这个控件某个Control的可见性后,其它的控件也被修改了,有什么好的解决办法嘛?