如何使用fabric.js在画布上加载的图像上实现裁剪工具? 我在画布上加载了一个图像。现在我想实现裁剪工具,允许用户裁剪图像并在完成后将其重新加载到画布上。
(这个答案是对 Tom's 答案中小提琴的迭代。谢谢你,汤姆,让我走上这条路。)
您可以使用 fabric.Object.clipTo() 或 fabric.Object.toDataURL() 在 Fabric.js 中进行裁剪。
clipTo()
方法保留原始图像并通过蒙版显示裁剪。 toDataURL()
方法创建一个新图像。
我的完整示例使用
clipTo()
方法。我在最后添加了一小块代码,显示了 toDataURL()
方法。
解决方案1
总结
与汤姆的答案不同
在汤姆的回答中,有一些小事情我想改变。所以在我的例子中,差异是
裁剪框从左到右和从右到左工作(汤姆的工作仅从右到左)
您有不止一次机会绘制裁剪框(尝试在汤姆的中重新绘制裁剪框会导致框跳跃)
适用于 Fabric.js v1.5.0
更少的代码。
代码
// set to the event when the user pressed the mouse button down
var mouseDown;
// only allow one crop. turn it off after that
var disabled = false;
var rectangle = new fabric.Rect({
fill: 'transparent',
stroke: '#ccc',
strokeDashArray: [2, 2],
visible: false
});
var container = document.getElementById('canvas').getBoundingClientRect();
var canvas = new fabric.Canvas('canvas');
canvas.add(rectangle);
var image;
fabric.util.loadImage("http://fabricjs.com/lib/pug.jpg", function(img) {
image = new fabric.Image(img);
image.selectable = false;
canvas.setWidth(image.getWidth());
canvas.setHeight(image.getHeight());
canvas.add(image);
canvas.centerObject(image);
canvas.renderAll();
});
// capture the event when the user clicks the mouse button down
canvas.on("mouse:down", function(event) {
if(!disabled) {
rectangle.width = 2;
rectangle.height = 2;
rectangle.left = event.e.pageX - container.left;
rectangle.top = event.e.pageY - container.top;
rectangle.visible = true;
mouseDown = event.e;
canvas.bringToFront(rectangle);
}
});
// draw the rectangle as the mouse is moved after a down click
canvas.on("mouse:move", function(event) {
if(mouseDown && !disabled) {
rectangle.width = event.e.pageX - mouseDown.pageX;
rectangle.height = event.e.pageY - mouseDown.pageY;
canvas.renderAll();
}
});
// when mouse click is released, end cropping mode
canvas.on("mouse:up", function() {
mouseDown = null;
});
$('#cropB').on('click', function() {
image.clipTo = function(ctx) {
// origin is the center of the image
var x = rectangle.left - image.getWidth() / 2;
var y = rectangle.top - image.getHeight() / 2;
ctx.rect(x, y, rectangle.width, rectangle.height);
};
image.selectable = true;
disabled = true;
rectangle.visible = false;
canvas.renderAll();
});
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js" type="text/javascript"></script>
</head>
<body>
<canvas style="border: 1px solid black" id="canvas"></canvas>
<button id=cropB>crop</button>
</body>
解决方案2
或者,我们可以使用
clipTo()
生成新图像,而不是像上面那样使用 toDataURL()
。像这样的东西
$('#cropB').on('click', function() {
image.selectable = true;
disabled = true;
rectangle.visible = false;
var cropped = new Image();
cropped.src = canvas.toDataURL({
left: rectangle.left,
top: rectangle.top,
width: rectangle.width,
height: rectangle.height
});
cropped.onload = function() {
canvas.clear();
image = new fabric.Image(cropped);
image.left = rectangle.left;
image.top = rectangle.top;
image.setCoords();
canvas.add(image);
canvas.renderAll();
};
});
总结
el.selectable = false
抱歉,让我解释一下。
ctx.rect
将从对象的中心点裁剪图像。还应考虑 scaleX
因素。
x = select_el.left - object.left;
y = select_el.top - object.top;
x *= 1 / scale;
y *= 1 / scale;
width = select_el.width * 1 / scale;
heigh = select_el.height * 1 / scale;
object.clipTo = function (ctx) {
ctx.rect (x, y, width, height);
}
完整示例:http://jsfiddle.net/hellomaya/kNEaX/1/
并查看此 http://jsfiddle.net/hellomaya/hzqrM/ 用于生成选择框。 以及 Fabric 事件的参考:https://github.com/kangax/fabric.js/wiki/Working-with-events
因为这个问题/答案已经很老了,而且随着 FABRIC.js 库的发展,大部分内容在今天已经不相关了。
有 100 篇帖子,人们试图在使用 Fabric.js 时实现可靠的裁剪,所以我认为添加一些我一直在努力帮助社区的小提琴是相关的。
如果有人可以帮助我改进版本 2,使其能够实现与 canva.com 类似的裁剪,那就太好了。我知道我已经很接近了...
1) 版本 1(移动裁剪区域)(工作中) 这个有效,您可以双击,缩小裁剪区域,移动它,然后取消选择以完成裁剪
var canvas = new fabric.Canvas('c');
canvas.background = '#FFFFFF';
canvas.renderAll();
var imageUrl = "https://placekitten.com/200/200";
fabric.Image.fromURL(imageUrl, function (oImg) {
canvas.add(oImg);
});
canvas.on({
'mouse:dblclick': function(obj){
var target = obj.target ? obj.target : null;
if(target && target.type === 'image'){
prepareCrop(target);
}
}
});
function prepareCrop(e){
var i = new fabric.Rect({
id: "crop-rect",
top: e.top,
left: e.left,
angle: e.angle,
width: e.getScaledWidth(),
height: e.getScaledHeight(),
stroke: "rgb(42, 67, 101)",
strokeWidth: 2,
strokeDashArray: [5, 5],
fill: "rgba(255, 255, 255, 1)",
globalCompositeOperation: "overlay",
lockRotation: true,
});
var a = new fabric.Rect({
id: "overlay-rect",
top: e.top,
left: e.left,
angle: e.angle,
width: e.getScaledWidth(),
height: e.getScaledHeight(),
selectable: !1,
selection: !1,
fill: "rgba(0, 0, 0, 0.5)",
lockRotation: true,
});
var s = e.cropX,
o = e.cropY,
c = e.width,
l = e.height;
e.set({
cropX: null,
cropY: null,
left: e.left - s * e.scaleX,
top: e.top - o * e.scaleY,
width: e._originalElement.naturalWidth,
height: e._originalElement.naturalHeight,
dirty: false
});
i.set({
left: e.left + s * e.scaleX,
top: e.top + o * e.scaleY,
width: c * e.scaleX,
height: l * e.scaleY,
dirty: false
});
a.set({
left: e.left,
top: e.top,
width: e.width * e.scaleX,
height: e.height * e.scaleY,
dirty: false
});
i.oldScaleX = i.scaleX;
i.oldScaleY = i.scaleY;
canvas.add(a),
canvas.add(i),
canvas.discardActiveObject(),
canvas.setActiveObject(i),
canvas.renderAll(),
//
i.on("moving", function () {
(i.top < e.top || i.left < e.left) &&
((i.left = i.left < e.left ? e.left : i.left),
(i.top = i.top < e.top ? e.top : i.top)),
(i.top + i.getScaledHeight() > e.top + e.getScaledHeight() ||
i.left + i.getScaledWidth() > e.left + e.getScaledWidth()) &&
((i.top =
i.top + i.getScaledHeight() > e.top + e.getScaledHeight()
? e.top + e.getScaledHeight() - i.getScaledHeight()
: i.top),
(i.left =
i.left + i.getScaledWidth() > e.left + e.getScaledWidth()
? e.left + e.getScaledWidth() - i.getScaledWidth()
: i.left));
});
i.on("scaling", function () {
});
//
i.on("deselected", function () {
cropImage(i, e);
canvas.remove(a);
});
}
function cropImage(i, e){
// remove plaeholder
canvas.remove(i);
//
var s = (i.left - e.left) / e.scaleX,
o = (i.top - e.top) / e.scaleY,
c = (i.width * i.scaleX) / e.scaleX,
l = (i.height * i.scaleY) / e.scaleY;
// crop
e.set({
cropX: s,
cropY: o,
width: c,
height: l,
top: e.top + o * e.scaleY,
left: e.left + s * e.scaleX,
selectable: true,
cropped: 1
});
canvas.renderAll();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
<canvas id="c" width="600" height="600" style=" border: 2px solid #5d9eab;"></canvas>
2)版本 2(移动图像(canva.com 风格))(尚未工作) 这个不太有效,目标是实现与 canva.com 类似的裁剪。
var canvas = new fabric.Canvas('c');
canvas.background = '#FFFFFF';
canvas.renderAll();
var imageUrl = "https://placekitten.com/200/200";
fabric.Image.fromURL(imageUrl, function (oImg) {
canvas.add(oImg);
});
canvas.on({
'mouse:dblclick': function(obj){
var target = obj.target ? obj.target : null;
if(target && target.type === 'image'){
prepareCrop(target);
}
}
});
function prepareCrop(img){
var s = img.cropX,
o = img.cropY,
p = img.getScaledWidth(),
q = img.getScaledHeight();
// overlay bloc (gray)
var i = new fabric.Rect({
id: "crop-rect",
top: img.top + s * img.scaleY,
left: img.left + s * img.scaleX,
angle: img.angle,
width: p,
height: q,
stroke: "rgb(42, 67, 101)",
strokeWidth: 1,
strokeDashArray: [5, 5],
fill: "rgba(255, 255, 255, 1)",
lockRotation: true,
selectable: false
});
img.set({
clipPath: null,
cropX: null,
cropY: null,
left: img.left - s * img.scaleX,
top: img.top - o * img.scaleY,
width: img._originalElement.naturalWidth,
height: img._originalElement.naturalHeight,
dirty: false,
opacity: .5,
lockRotation: true,
selectable: true,
});
canvas.add(i);
canvas.bringToFront(img);
canvas.renderAll();
//
img.on("moving", function () {
// should constrain image in crop area
// similar to canva.com crop function
// we can still increase image scale during this phase
/*
(i.top < img.top || i.left < e.left) &&
((i.left = i.left < img.left ? img.left : i.left),
(i.top = i.top < img.top ? img.top : i.top)),
(i.top + i.getScaledHeight() > img.top + img.getScaledHeight() ||
i.left + i.getScaledWidth() > img.left + img.getScaledWidth()) &&
((i.top =
i.top + i.getScaledHeight() > img.top + img.getScaledHeight()
? img.top + img.getScaledHeight() - i.getScaledHeight()
: i.top),
(i.left =
i.left + i.getScaledWidth() > img.left + img.getScaledWidth()
? img.left + img.getScaledWidth() - i.getScaledWidth()
: i.left));*/
});
img.on("scaling", function () {
});
//
img.on("deselected", function () {
cropImage(i, img);
});
}
function cropImage(i, e){
// i is the crop bloc
// e is the full image
//
var s = (i.left - e.left) * e.scaleX,
o = (e.top - i.top) * e.scaleY,
c = (i.width * i.scaleX) / e.scaleX,
l = (i.height * i.scaleY) / e.scaleY;
// crop
e.set({
cropX: s,
cropY: o,
width: c,
height: l,
top: i.top + o * e.scaleY,
left: i.left + s * e.scaleX,
selectable: true,
cropped: 1,
opacity: 1,
});
canvas.remove(i);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
<canvas id="c" width="600" height="600" style=" border: 2px solid #5d9eab;"></canvas>
欢迎任何帮助/反馈,
干杯, 猎户座
我使用最新的 Fabric js 版本开发了简单、更快的图像裁剪 https://github.com/kpomservices/Fabric-JS-Image-CROP
演示:http://kpomservices.com/imagecropdemo/index.html
我使用cropx、cropy来维护作物。