Fabric.js实作网格系统
2018-11-27 23:45:20
2014次阅读
0个评论
今天用 Fabricjs 製作网格系统,能够让使用者自订间距。

并且再让使用者缩放矩形时,能够让矩形的大小和移动间距可以照著网格。

先让大家看看结果

移动、缩放限制


实作过程总览

让使用者指定间距后,使用 fabric.Line 画出把网格做出来。
撰写新增矩形的函数
倾听移动时事件 (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 画面出来的,而不是靠 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')
收藏00

登录 后评论。没有帐号? 注册 一个。