使用 FabricJs 的裁剪功能

问题描述 投票:0回答:4

如何使用fabric.js在画布上加载的图像上实现裁剪工具? 我在画布上加载了一个图像。现在我想实现裁剪工具,允许用户裁剪图像并在完成后将其重新加载到画布上。

javascript canvas fabricjs
4个回答
18
投票

(这个答案是对 Tom's 答案中小提琴的迭代。谢谢你,汤姆,让我走上这条路。)

您可以使用 fabric.Object.clipTo()fabric.Object.toDataURL() 在 Fabric.js 中进行裁剪。

clipTo()
方法保留原始图像并通过蒙版显示裁剪。
toDataURL()
方法创建一个新图像。

我的完整示例使用

clipTo()
方法。我在最后添加了一小块代码,显示了
toDataURL()
方法。

解决方案1

总结

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

与汤姆的答案不同

在汤姆的回答中,有一些小事情我想改变。所以在我的例子中,差异是

  1. 裁剪框从左到右和从右到左工作(汤姆的工作仅从右到左)

  2. 您有不止一次机会绘制裁剪框(尝试在汤姆的中重新绘制裁剪框会导致框跳跃)

  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

或者,我们可以使用

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

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


0
投票

因为这个问题/答案已经很老了,而且随着 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>

欢迎任何帮助/反馈,

干杯, 猎户座


-2
投票

我使用最新的 Fabric js 版本开发了简单、更快的图像裁剪 https://github.com/kpomservices/Fabric-JS-Image-CROP

演示:http://kpomservices.com/imagecropdemo/index.html

我使用cropx、cropy来维护作物。

© www.soinside.com 2019 - 2024. All rights reserved.