今天用 Fabricjs 製作網格系統,能夠讓使用者自訂間距。
並且再讓使用者縮放矩形時,能夠讓矩形的大小和移動間距可以照著網格。
先讓大家看看結果
移動、縮放限制
這邊可以透過 fabricjs.Line
類別配合迴圈快速的將格線畫出來,這邊我們網格只是要當作背景無須被使用這操作,所以我們將物件的 selectable
屬性設為 false
。因為是要畫成整個網頁的網格,所以先從頁面長寬取得較長的數值。
新增前先判斷每五條線畫一條較黑的線。
function drawGrid () {
canvas.clear()
const longer = window.innerWidth > window.innerHeight ? window.innerWidth : window.innerHeight
let vLine
let hLine
// get input value || default 40
distance = +distanceInput.value || 40
for (let i = 1; i * distance < longer; i++) {
const lineDef = {
fill: 'black',
stroke: 'rgba(0, 0, 0, 0.1)',
strokeWidth: 1,
selectable: false
}
// draw vLine
vLine = new fabric.Line([i * distance, 0, i * distance, canvas.height], lineDef)
// draw hLine
hLine = new fabric.Line([0, i * distance, canvas.width, i * distance], lineDef)
if (i % 5 === 0) {
vLine.stroke = 'rgba(0, 0, 0, 0.7)'
hLine.stroke = 'rgba(0, 0, 0, 0.7)'
}
canvas.add(vLine, hLine)
}
}
這邊不只要能新增一個矩形,還要做矩形移動和縮放的限制。
新增矩形用每個網格的間隔當作單位來新增。
const rect = new fabric.Rect({
width: distance,
height: distance,
top: distance * 5,
left: distance * 5,
centeredRotation: false,
cornerSize: 8,
transparentCorners: false
})
再來要傾聽新增出來的矩形物件,先來做移動的限制。
當矩形被移動時候,會執行他的 callback function,fabricjs 移動時的單位是小數,這邊我們自己設定移動的間隔。
做完這件事情後,我們移動的矩形就會貼著最近的網格囉。
rect.on('moving', (e) => {
const target = e.target
// 設定移動間隔為格線間隔
target.left = Math.round(target.left / distance) * distance
target.top = Math.round(target.top / distance) * distance
})
這時我們縮放矩形還是沒辦法跟著網格移動。
必須等待使用者縮放後做一些事,所以設定物件的 on scaled。
因為透過 Fabricjs 的控制項縮放是靠 scaleX、scaleY,來 render 畫面出來的,而不是靠 width
和 height
屬性。
舉例來說今天把矩形利用控制項縮放到 2 倍大小,scaleX 和 scaleY 就都會是 2。
所以要透過 target.getBoundingRect()
來取得目前縮放後的長寬和座標。
但我們必須在每次變更後計算長度和寬度,所以這邊每次計算完長寬後,重新將縮放比例 (scaleX
、scaleY
) 設回 1 倍。
最後因為我們改變長寬,所以要呼叫 setCoords()
重新 render 控制項座標。
rect.on('scaled', (e) => {
const target = e.target
const newRect = target.getBoundingRect()
for (let key in newRect) {
newRect[key] = Math.round(newRect[key] / distance) * distance
}
target.set({
scaleX: 1,
scaleY: 1,
width: newRect.width,
height: newRect.height,
left: newRect.left,
top: newRect.top
})
target.setCoords()
})
最後將旋轉控制項隱藏起來,不讓使用者旋轉,再將我們剛剛設定玩得矩形加入 canvas 裡面。
rect.setControlVisible('mtr', false)
canvas.add(rect)
}
實作常用的網格系統,不過網格通常不會再被控制,通常我自己弄會分成兩個 canvas,一個是背景網格的 canvas 用 fabric.StaticCanvas
,需要常常操控物件的在新增到 fabric.Canvas
。
參考 Day 3 - 畫布設置
透過物件的事件,動態更改物件的屬性,若不想要每次新增物件時都要綁定事件,可將事件傾聽綁定在 canvas.on('object:scaled')
、 canvas.on('object:moving')
參考 Day 8 - 事件