Fabric.js实作网格系统
2018-11-27 23:45:20
2533 次阅读
0 个评论
今天用 Fabricjs 製作网格系统,能够让使用者自订间距。
并且再让使用者缩放矩形时,能够让矩形的大小和移动间距可以照著网格。
先让大家看看结果
移动、缩放限制
实作过程总览
让使用者指定间距后,使用 fabric.Line 画出把网格做出来。
撰写新增矩形的函数
倾听移动时事件 (moving),当矩形被移动时动态计算当时离最近的线
倾听变更后事件 (modified),当矩形被移动后计算当时离最近的线以及适当的大小
画出网格
这边可以透过 fabricjs.Line 类别配合迴圈快速的将格线画出来,这边我们网格只是要当作背景无须被使用这操作,所以我们将物件的 selectable 属性设為 false。因為是要画成整个网页的网格,所以先从页面长宽取得较长的数值。
新增前先判断每五条线画一条较黑的线。
矩形控制
这边不只要能新增一个矩形,还要做矩形移动和缩放的限制。
新增矩形用每个网格的间隔当作单位来新增。
再来要倾听新增出来的矩形物件,先来做移动的限制。
当矩形被移动时候,会执行他的 callback function,fabricjs 移动时的单位是小数,这边我们自己设定移动的间隔。
做完这件事情后,我们移动的矩形就会贴著最近的网格囉。
这时我们缩放矩形还是没办法跟著网格移动。
必须等待使用者缩放后做一些事,所以设定物件的 on scaled。
因為透过 Fabricjs 的控制项缩放是靠 scaleX、scaleY,来 render 画面出来的,而不是靠 width 和 height 属性。
举例来说今天把矩形利用控制项缩放到 2 倍大小,scaleX 和 scaleY 就都会是 2。
所以要透过 target.getBoundingRect() 来取得目前缩放后的长宽和座标。
但我们必须在每次变更后计算长度和宽度,所以这边每次计算完长宽后,重新将缩放比例 (scaleX、scaleY) 设回 1 倍。
最后因為我们改变长宽,所以要呼叫 setCoords() 重新 render 控制项座标。
最后将旋转控制项隐藏起来,不让使用者旋转,再将我们刚刚设定玩得矩形加入 canvas 裡面。
本日小结
实作常用的网格系统,不过网格通常不会再被控制,通常我自己弄会分成两个 canvas,一个是背景网格的 canvas 用 fabric.StaticCanvas,需要常常操控物件的在新增到 fabric.Canvas。
参考 Day 3 - 画布设置
透过物件的事件,动态更改物件的属性,若不想要每次新增物件时都要绑定事件,可将事件倾听绑定在 canvas.on('object:scaled') 、 canvas.on('object:moving')
并且再让使用者缩放矩形时,能够让矩形的大小和移动间距可以照著网格。
先让大家看看结果

移动、缩放限制

实作过程总览
让使用者指定间距后,使用 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