为什么80%的码农都做不了架构师?>>>
写这篇文章的时候我就要说一下了,这个遮罩和裁剪耗费了我真的是九牛二虎之力
裁剪的插件很多,无论你用原生JS,JQuery, Vue还是React.总能找到一款裁剪插件。今天我要说的是自己写一个截图功能,直接就在操作的画布中操作需要裁剪的图片,而不是跳一个新页面裁剪后再挪过来。Fabricjs 提供了很多方式的裁剪,最常见的是Object属性的上的clipTo,然而有弊端.
比如google一下比较好的一个demo: http://jsfiddle.net/hellomaya/kNEaX/1/
- 发现没有,裁剪后的选框依然和原图片一样大,无论用啥操作都没法使其变小(或许有办法我没试出来)
- 看源码,会发现他的clipTo一方面不完善,一方面只能是矩形的裁剪。没法实现遮罩。比如如下图:
这个时候发现官方demo有个其他方式的遮罩。 http://fabricjs.com/patterns fabric.Pattern 这个捏,文档写的倒是挺含含糊糊,网上的例子也是很少,不过功能是很强大,无论字体,形状,图片都统统可以给你遮罩上去。但是离我们最后要实现的功能还是有点距离
首先理清一下思路,
- 一个是选择图片的时候触发遮罩功能
- 添加遮罩形状,往遮罩形状里面添加图片,相当于一个图层
- 隐藏选中的图片,添加一个备份的图片放在下面当一个底部的图层,作为参考。这里加一个备份图片有其道理,看了我后面的代码就会明白,这一步很重要
- 调整裁剪(遮罩)形状和位置,裁剪
- 确定裁剪,隐藏备份图片。这个时候要注意,假如裁剪后的图片移动或者放大缩小,备份的图片也要跟着改变
- 再次点击显示备份的图片
是不是我直接可以贴代码了,里面主要涉及了如何移动,放大缩小的逻辑。吼吼。。。
首先是触发裁剪遮罩。这里只是随意定义了一个react 矩形的遮罩形状,后面你可以自己自定义。
clipImage(state) {
let activeObject = state.canvas.getActiveObject();
state.isClipping = true
if (activeObject.type === 'image') {
let clipBox = new fabric.Rect({
left: activeObject.left,
top: activeObject.top,
width: activeObject.width,
height: activeObject.height,
stroke: '#F5A623',
strokeWidth: 1,
fill: 'rgba(255, 255, 255, 0)',
objectCaching: false,
scaleX: activeObject.scaleX,
scaleY: activeObject.scaleY,
selectionBackgroundColor: 'rgba(255, 255, 255, 0)',
padding: 0,
angle: activeObject.angle
});
state.clipBox = clipBox
state.clipActiveObj = activeObject;
// 区分是svg的img还是普通img
let url = activeObject.src ? activeObject.src : activeObject['xlink:href']
fabric.util.loadImage(url, function(img) {
clipBox.fill = new fabric.Pattern({
source: img,
repeat: 'no-repeat',
offsetX: 0,
offsetY: 0,
});
state.canvas.add(clipBox);
activeObject.set({
selectable: false,
hoverCursor: 'default',
evented: false,
hasControls: false,
perPixelTargetFind: false,
})
activeObject.clone(function (clonedObj) {
state.canvas.discardActiveObject();
clonedObj.set({
left: clonedObj.left,
top: clonedObj.top,
evented: false,
opacity: 0.8
});
clipBox.clipClone = clonedObj;
state.canvas.add(clonedObj);
});
activeObject.visible = false;
state.canvas.renderAll();
state.clipBox.on({
'moving': () => {
if (!state.isClipping) {
clipBox.clipClone.left = clipBox.left - state.clipLeft
clipBox.clipClone.top = clipBox.top - state.clipTop
state.canvas.renderAll()
return
}
let left =clipBox.left - clipBox.clipClone.left;
let top = clipBox.top - clipBox.clipClone.top;
state.clipLeft = left
state.clipTop = top
clipBox.fill.offsetX = -left / clipBox.clipClone.scaleX
clipBox.fill.offsetY = -top / clipBox.clipClone.scaleY
state.canvas.renderAll();
},
'scaling': () => {
if (!state.isClipping) {
clipBox.clipClone.left = clipBox.left - state.clipLeft
clipBox.clipClone.top = clipBox.top - state.clipTop
clipBox.clipClone.scaleX = clipBox.scaleX
clipBox.clipClone.scaleY = clipBox.scaleY
state.canvas.renderAll()
return
}
// let _width = clipBox.width / clipBox.
let _width = clipBox.width * clipBox.scaleX / clipBox.clipClone.scaleX
let _height = clipBox.height * clipBox.scaleY / clipBox.clipClone.scaleY
let left =clipBox.left - clipBox.clipClone.left;
let top = clipBox.top - clipBox.clipClone.top;
state.clipLeft = clipBox.left
state.clipTop = clipBox.top
clipBox.fill.offsetX = -left / clipBox.clipClone.scaleX
clipBox.fill.offsetY = -top / clipBox.clipClone.scaleX
clipBox.scaleX = clipBox.clipClone.scaleX
clipBox.scaleY = clipBox.clipClone.scaleY
clipBox.width = _width
clipBox.height = _height
state.canvas.renderAll();
}
})
setTimeout(() => {
state.canvas.setActiveObject(state.clipBox);
state.canvas.renderAll();
}, 300)
})
} else {
activeObject.clipClone.visible = true;
state.canvas.renderAll();
}
}
接着是确定裁剪
let activeObject = state.canvas.getActiveObject();
state.isClipping = false
activeObject.clipClone.visible = false
state.canvas.remove(state.clipActiveObj);
这样你差不多也能明白个差不多了,让你单独自己写个裁剪插件适应其他项目也是没问题了。