Fabric.js高级点的教程4--内嵌工作台画布

原文链接:https://my.oschina.net/xmqywx/blog/3047028

额。。这是我自己创造的名词,内嵌工作台画布

要实现什么效果

如图(http://fabricjs.com/kitchensink):

首先想元素脱出画布外一部分也能选中,如图上面就没法选择超出框外的内容。我想到的是如何在画布中间加个画布。周围隐藏掉。

实现思路

  1. 首先创建一个足够大的画布
  2. 设置一个padding
  3. 通过padding 计算中间白色区域四个角的坐标
  4. 在周围画上四个矩形盖住(也可以画一个大矩形,把中间的挖掉,由于各种需求和测试问题没有采用这种方式)
  5. 每次添加新的元素在上面的时候都保持周边四个区域在最上面

其他思路

  1. 使用clipPath,画一个通道给中间区域。
  2. 画两个画布一个再上面一个在下面 其实这些方法可能都能实现,但是问题的关键不是实现,而是如何兼容之后的诸多行为,比如zoom,loadJson,redo, undo

代码

1.先定义一个想要的外边距padding,然后计算内部工作台处于外部画布的四个点的位置。

// 获取中间画布的四个点的位置
function setXY(state, obj = {}) {
  // 最外边整个画布的宽 高
  const w = obj.currentWidth || state.currentWidth 
  const h = obj.currentHeight || state.currentHeight
  // 内部工作台画布的宽pannelW 高pannelH
  if (
    state.currentWidth / state.currentHeight >
    state.pannelW / state.pannelH
  ) {
    const height = h - state.padding
    // 缩放画布,因为pannelW为实际宽度,比如一张A4纸显示在屏幕上,需要缩放一下,zoom后获取到屏幕的宽度
    state.zoom = height / state.pannelH
    this.commit('zoomCanvas')
    const x2 = 0
    const y2 = 0

    const y0 = state.padding / state.zoom / 2 + y2
    const y1 = state.pannelH + y0
    const x0 = w / state.zoom / 2 - state.pannelW / 2 + x2
    const x1 = state.pannelW + x0
    state.arrX = [x0, x1, x2, w / state.zoom + x2]
    state.arrY = [y0, y1, y2, h / state.zoom + y2]
  } else {
    const width = w - state.padding
    state.zoom = width / state.pannelW
    this.commit('zoomCanvas')
    const x2 = 0
    const y2 = 0

    const x0 = state.padding / state.zoom / 2 + x2
    const x1 = state.pannelW + x0
    const y0 = h / state.zoom / 2 - state.pannelH / 2 + y2
    const y1 = state.pannelH + y0
    state.arrX = [x0, x1, x2, w / state.zoom]
    state.arrY = [y0, y1, y2, h / state.zoom]
  }
}

2.创建四周的遮罩,给中间的区域,画个背景

function createMask(state) {

  const arrX = state.arrX
  const arrY = state.arrY

  const pathOption = {
    selectable: false,
    fill: '#ebeced',
    hoverCursor: 'default',
    evented: false,
    excludeFromExport: true,
    hasControls: false,
    perPixelTargetFind: false,
    strokeWidth: 0,
    stroke: null
  }

  const rect1 = new fabric.Rect({
    width: arrX[0] - arrX[2],
    height: arrY[3] - arrY[2]
  })
  const rect2 = new fabric.Rect({
    width: arrX[3] - arrX[0] + 2,
    height: arrY[0] - arrY[2]
  })
  const rect3 = new fabric.Rect({
    width: arrX[3] - arrX[1],
    height: arrY[1] - arrY[0] + 2
  })
  const rect4 = new fabric.Rect({
    width: arrX[3] - arrX[0],
    height: arrY[3] - arrY[1]
  })
  rect1.set({
    left: arrX[2],
    top: arrY[2],
    name: 'mask1',
    ...pathOption
  })
  rect2.set({
    left: arrX[0] - 1,
    top: arrY[2],
    name: 'mask2',
    ...pathOption
  })
  rect3.set({
    left: arrX[1],
    top: arrY[0] - 1,
    name: 'mask3',
    ...pathOption
  })
  rect4.set({
    left: arrX[0],
    top: arrY[1],
    name: 'mask4',
    ...pathOption
  })

  state.maskPath = new fabric.Group([rect1, rect2, rect3, rect4], {
    selectable: false,
    excludeFromExport: true,
    lockMovementX: true,
    lockMovementY: true,
    lockRotation: true,
    lockScalingX: true,
    lockScalingY: true,
    lockUniScaling: true,
    hoverCursor: 'auto',
    name: 'grid',
    left: arrX[2],
    top: arrY[2],
    type: 'sMask',
    evented: false
  })
  // 创建中间画布的背景
  state.backgroundPanel = new fabric.Rect({
    left: arrX[0],
    top: arrY[0],
    selectable: false,
    evented: false,
    width: state.pannelW,
    height: state.pannelH,
    strokeWidth: 0,
    fill: !state.activePage ? '#ffffff' : 'rgba(0, 0, 0, 0)',
    objectCaching: false,
    hoverCursor: 'default',
    excludeFromExport: true,
    hasControls: false,
    type: 'sBg',
    perPixelTargetFind: false
  })
  state.canvas.remove(...state.canvas.getObjects('sMask'))
  state.canvas.remove(...state.canvas.getObjects('sBg'))
  state.canvas.add(state.maskPath)
  state.canvas.add(state.backgroundPanel)
  state.canvas.sendToBack(state.backgroundPanel)

  state.canvas.renderAll()
}

3.刷新画布,或者加载数据的时候

 jsonDemo.objects.forEach((item) => {
    item.left += state.arrX[0]
    item.top += state.arrY[0]
 })

4.保存json

function saveJson(state) {

  // state.toJSONProperties 为要保存的额外属性
  const jsonStr = JSON.stringify(state.canvas.toJSON(state.toJSONProperties))
  const jsonObj = JSON.parse(jsonStr)

  jsonObj.objects.forEach((v) => {
    v.left = v.left - state.arrX[0]
    v.top = v.top - state.arrY[0]
  })

}

 

转载于:https://my.oschina.net/xmqywx/blog/3047028

展开阅读全文
博主设置当前文章不允许评论。

没有更多推荐了,返回首页