Fabric.js实作: 拼贴图片

2018-11-28 12:50:55
1120次阅读
2个评论
今天来实作一个图片编辑常用的功能: 拼贴。

顾名思义就是能把自己图片拼到已经设定好的框框中,而图片可以在框框中调整要显示的部分,也就是固定位置的裁切功能。

配合之前练习的拖拉图片进入 canvas 来做到让使用者自行上传图片,再将图片拖曳进去 canvas 拼贴。



拖曳部分相关可参考 Day 18 - Fabricjs 实作: 图片上传并透过拖曳进入 canvas
这边就不在重复解释了
建立区域让使用者知道哪边可以匯入图片

我的作法是想要直接让之后设定的 clipPath 直接依据一开始所设定好的 Layout 做裁切。

首先先建出虚线矩形区块让使用者填入,并且将 selectable 设定為 false,只是要让使用者观看框框的范围,所以就不需要让使用者移动。

自订 isClipFrame 属性之后用配合 drop 事件,判断使用者拖曳到的是不是我们设定的虚线框。

let clipPathTop = new fabric.Rect({
  // ...略
  fill: 'transparent',
  selectable: false,
  isClipFrame: true
})

let clipPathBottom = new fabric.Rect({
  width: 490,
  height:240,
  left: 5,
  top: 255,
  stroke: 'red',
  strokeWidth: 1,
  strokeDashArray: [5, 5],
  fill: 'transparent',
  selectable: false,
  isClipFrame: true
})

canvas.add(clipPathTop)
canvas.add(clipPathBottom)

拼贴动作函数

拖拉 Drap & drop 请参考 Day 18 - Fabricjs 实作: 图片上传并透过拖曳进入 canvas
这边要做的动作分几个步骤

判断拖曳到的 target 是否為矩形虚线框
新增一张照片到举行框内,并且设定裁切范围同等於目标框 target。
判断长宽是否為满版来做调整并锁定移动 X 或 Y。
absolutePositioned 属性

这边可以做到这个功能,主要是靠 absolutePositioned 这个属性,只要将裁切遮罩中的这属性设成 true,此物件这时候 left、top 属性和一般裁切不同的地方在於此物件起始会回到 (0,0),而不是被裁切物件中心。

你可以想像成在 canvas 上挖洞固定在某个位置,而只有那个位置能够显示被裁切的物件。

clone 复製物件

这边因為要做出裁切效果,之后為了更好扩充我们不直接写死,改成复製一份目标框物件,再将复製出来的 absolutePositioned 属性设定成 true。

参考 fabricjs - copy & parse
  
target.clone(cloned => clipPath = cloned)
  clipPath.absolutePositioned = true

判断长宽并锁定移动方向

这边做的事情是比对原始图片和框的大小是否适合,做出相对应的大小调整动作。

最后固定一轴的移动 X or Y,让使用者对齐自行调整。

 
// 判断长宽是否為满版来做调整并锁定 X Y
  image.scaleToWidth(target.width)
  const isFullHeight = image.getScaledHeight() < target.height
  if (isFullHeight) image.scaleToHeight(target.height)
  image.lockMovementY = isFullHeight
  image.lockMovementX = !isFullHeight

完整函数拼贴动作函数

function dropImg (e) {
  let target = e.target
  if (!target.isClipFrame) return
    // 设定匯入图块
  target.clone(cloned => clipPath = cloned)
  clipPath.absolutePositioned = true
  const image = new fabric.Image(movingImage, {
    width: movingImage.naturalWidth,
    height: movingImage.naturalHeight,
    left: target.left,
    top: target.top,
    clipPath,
  })
  // 判断长宽是否為满版来做调整并锁定 X Y
  image.scaleToWidth(target.width)
  const isFullHeight = image.getScaledHeight() < target.height
  if (isFullHeight) image.scaleToHeight(target.height)
  image.lockMovementY = isFullHeight
  image.lockMovementX = !isFullHeight
  
  image.clipPath = clipPath
  canvas.add(image)
}

最后再配合档案上传,让使用者能够上传自己想要拼贴的图片

档案上传部分相关可参考 Day 18 - Fabricjs 实作: 图片上传并透过拖曳进入 canvas
结果



更多形状填入

因為我们是直接写成复製虚线框物件来做裁切,这样一来,我们就能够更方便的拼贴图片,只要我们先将预设的虚线框建出来就可以了!

任何形状都能裁切!

接下来将预设好的 Layout 储存起来,包成函数让使用者可以透过按钮来切换预设 Layout 。



使用 SVG

也能够直接使用 SVG 来当作裁切的框,这样一来可以做的变化就更多了!

function setLayoutStyle3 () {
  canvas.clear()
  const URL = 'https://upload.wikimedia.org/wikipedia/commons/4/42/Love_Heart_SVG.svg'

  fabric.loadSVGFromURL(URL, (objects, options) => {
    console.log(options)
    let svgClip = fabric.util.groupSVGElements(objects, options)
      svgClip.scaleToWidth(300)
      svgClip.set({
        left: 100,
        top: 100,
        stroke: 'red',
        strokeWidth: 1,
        strokeDashArray: [5, 5],
        fill: 'transparent',
        selectable: false,
        isClipFrame: true,
      })
    canvas.add(new fabric.Rect({
      ...
    }))
    canvas.add(new fabric.Rect({
      ...
    }))
    canvas.add(svgClip).renderAll()
  })
}

svg form Wikimedia Commons


今日小结

利用设定 absolutePositioned 固定画布上的裁切位置。

并且基於这个方式实作出简易拼贴图片功能。

利用 SVG 做出更丰富的裁切框。
收藏11

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