使用FabricJs进行裁剪功能

22

如何使用fabric.js在画布上加载的图像上实现裁剪工具? 我在画布上加载了一张图片。现在我想要实现裁剪工具,让用户可以裁剪图片,并在完成后重新加载到画布上。


您想在相同的角度裁剪图像,还是带有旋转支持? - kangax
@kangax:我们可以使用 toDataURL 裁剪功能来裁剪加载在画布上的图像并重新加载回来吗? - John
是的,你可以使用 img.toDataURL(...) - kangax
@John,你是否成功地获得了像Pain一样的裁剪功能? - anam
是的...使用img.toDataURL(...)完成了它。 - John
显示剩余2条评论
3个回答

18

(此答案是对 Tom 答案中的 fiddle 进行了迭代。感谢 Tom 帮我走上这条路。)

您可以使用 Fabric.js 中的 fabric.Object.clipTo()fabric.Object.toDataURL() 进行裁剪。 clipTo() 方法保留原始图像,并通过掩模显示剪裁。 toDataURL() 方法创建一个新的图像。

我的完整示例使用了 clipTo() 方法。我在末尾包含了一小段代码,展示了 toDataURL() 方法。

解决方案 1

概述

  1. 准备一个不可见的矩形。当用户在画布上单击并拖动鼠标时,绘制它。
  2. 当用户释放鼠标单击时,裁剪底层图像

与 Tom 答案的区别

在 Tom 答案的 fiddle 中,有几个我想要改变的小细节。所以在我的示例中,区别如下

  1. 裁剪框从左到右和从右到左都可以工作 (Tom 的只能从右到左)

  2. 您有多次机会绘制裁剪框 (在 Tom 的尝试重新绘制裁剪框会导致框跳动)

  3. 适用于 Fabric.js v1.5.0

  4. 代码更少。

代码

 // 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

或者,我们可以使用toDataURL()生成一个新的图片,而不是像上面那样使用clipTo()。代码如下:

$('#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();
    };
});

为什么当我注释掉 onload 时它仍然起作用?而且当我在 toDataURL 后加上警报时,这个警报从未触发? - jose920405
当画布应用缩放时,canvas.toDataURL返回不同的图像。是否有任何方法可以在缩放视图中裁剪图像? - Anurag pareek
1
在您的解决方案2中,由于toDataUrl(),图像的质量非常差。是否有任何替代方案可以保持质量(与原始图像相同)并且与解决方案2相同? - Neeraj Rathod
如果是大图像,则无法工作,我已经添加了scaleToWidth和scaleToHeight以适应fabric画布上的大图像,但它不起作用。 - Pavan Hukerikar

13

总结一下:

  1. el.selectable = false
  2. 通过鼠标事件在其上绘制矩形
  3. 获取矩形的左侧/顶部和宽度/高度
  4. 然后使用以下函数

很抱歉,让我解释一下。 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


我该如何在图像对象上绘制下拉框?我只需要将图像设置为背景,然后裁剪吗?还是说如何在图像对象上绘制矩形?其他对象上的事件? - John
更新了代码片段,您能否检查一下我哪里出错了?http://jsfiddle.net/hzqrM/5/ 另外只有剪辑功能可用,图片无法显示。 - John
我们可以使用 toDataURL 的裁剪功能来裁剪加载在画布上的图像并重新加载它吗? - John
请注意,如果原始内容已经被旋转过,则此答案将无效。 - J.T.
如果画布大小与图像太大,则无法正常工作。我已经将scaleToWidth和scaleToHeight添加到画布图像中。我正在尝试添加您的代码,但它没有正确地剪切。我的代码fabric.util.loadImage(src,function(img){ object = new fabric.Image(img);object.set({ left: 100, top: 100, selectable: false }); object.hasRotatingPoint = true; object.scaleX = object.scaleY = 0.25; object.scaleToWidth(“400”); object.scaleToHeight(“400”); canvas.add(object); canvas.renderAll();}); - Pavan Hukerikar
大家好,我又遇到一个问题。请问有人知道如何在矩形内部绘图,并且当鼠标移出矩形时停止绘制吗?如何实现这个功能呢? - Faizan

-2

请不要仅仅将某个工具或库作为答案发布。至少在答案中演示它是如何解决问题的。 - double-beep
这个功能运行得相当不错,但是当你保存布料并返回到裁剪后的图像时,点击图像会使其消失。 - bjacob596

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接