iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 20
2
Modern Web

Fabricjs 筆記系列 第 20

Day 20 - Fabricjs 實作網格系統

  • 分享至 

  • xImage
  •  

今天用 Fabricjs 製作網格系統,能夠讓使用者自訂間距。

並且再讓使用者縮放矩形時,能夠讓矩形的大小和移動間距可以照著網格。

先讓大家看看結果

移動、縮放限制

實作過程總覽

  1. 讓使用者指定間距後,使用 fabric.Line 畫出把網格做出來。
  2. 撰寫新增矩形的函數
    • 傾聽移動時事件 (moving),當矩形被移動時動態計算當時離最近的線
    • 傾聽變更後事件 (modified),當矩形被移動後計算當時離最近的線以及適當的大小

畫出網格

這邊可以透過 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 畫面出來的,而不是靠 widthheight 屬性。

舉例來說今天把矩形利用控制項縮放到 2 倍大小,scaleX 和 scaleY 就都會是 2。

所以要透過 target.getBoundingRect() 來取得目前縮放後的長寬和座標。

但我們必須在每次變更後計算長度和寬度,所以這邊每次計算完長寬後,重新將縮放比例 (scaleXscaleY) 設回 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 - 事件

本日完整程式 - codepen


上一篇
Day 19 - 實作 Node.js 上傳圖片及操作 Fabricjs 為圖片加上浮水印
下一篇
Day 21 - Fabricjs Zoom in & Zoom out
系列文
Fabricjs 筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言