【问题标题】:How to Limit Object inside Transparent Rectangle Area - FabricJS?如何限制透明矩形区域内的对象 - FabricJS?
【发布时间】:2020-11-13 03:00:42
【问题描述】:

我正在使用 FabricJS 在我身边实现设计器功能。我的想法是使用fabric中的setBackgroundImage设置背景图像,我还添加了透明矩形的特定区域,其大小和位置来自JCrop。现在来问我的问题,我想将对象放置限制在透明矩形的特定区域内。假设我想添加应该在该有限区域内的文本/图像/形状,我能够实现背景图像、透明矩形的位置甚至圆形对象,但我无法找到限制对象的细节将其放置在透明矩形内并且只在那个地区。

这是我下面的代码和工作fiddle,如果您在需要选择裁剪部分的图像中看到它,并且在该画布背景下方使用透明矩形,这与裁剪选择相同。现在我想限制对象放置在透明矩形中,现在我可以将对象放置在画布中的任何位置。

HTML

   <img src="https://images.unsplash.com/photo-1595438337199-d50ba5072c7e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=330&q=80" id="target">

   <div class="canvas-container" style="position: relative; user-select: none;">
     <canvas id="c1" width="600" height="600" style="border:1px solid #ccc; position: absolute;  left: 0px; top: 0px; touch-action: none; user-select: none;"></canvas>
   </div>

JS

function calculateAspectRatioFit(srcWidth, srcHeight, maxWidth, maxHeight) {
  var ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight);
  return {
    width: srcWidth * ratio,
    height: srcHeight * ratio,
    aspectratio: ratio
  };
}

jQuery(function($) {
    //alert("Testing");
  var img = new Image();
  img.onload = function() {
    var data = calculateAspectRatioFit(this.width, this.height, '400', '600');
    console.log(data);
    jQuery('#target').attr('width', data.width);
    jQuery('#target').attr('height', data.height);
    jQuery('#pdr-drawing-area').html("Aspect Ratio: " + data.aspectratio);
    const stage = Jcrop.attach('target');
    stage.listen('crop.change', function(widget, e) {
      const pos = widget.pos;
      console.log(pos.x, pos.y, pos.w, pos.h);
      //fabric js
      var canvas = new fabric.Canvas('c1');
      var center = canvas.getCenter();
      var img = 'https://images.unsplash.com/photo-1595438337199-d50ba5072c7e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=330&q=80';
      canvas.setBackgroundImage(img, function() {
        canvas.backgroundImage && canvas.backgroundImage.scaleToWidth(data.width);
        canvas.backgroundImage && canvas.backgroundImage.scaleToHeight(data.height);
        //canvas.sendToBack(img);
        canvas.renderAll();
      });
      console.log(pos.x * data.aspectratio);
      var rect = new fabric.Rect({
        left: pos.x,
        top: pos.y,
        fill: 'transparent',
        width: (pos.w),
        height: (pos.h),
        strokeDashArray: [5, 5],
        stroke: "black",
        selectable: false,
        evented: false,
        //visible: false
      });



      canvas.add(new fabric.Circle({
        radius: 30,
        fill: '#f55',
        top: pos.y + 2,
        left: pos.x + 2
      }));

      canvas.add(rect);
      canvas.setHeight(data.height);
      canvas.setWidth(data.width);
      canvas.renderAll();
    });
  },
   img.src = 'https://images.unsplash.com/photo-1595438337199-d50ba5072c7e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=330&q=80';
});

【问题讨论】:

  • 您要求在这种情况下将 FabricJS 对象限制为 Circle 到某些边界...您可以将您的代码示例简化为这样吗?
  • 仔细查看您的代码...您真的没有尝试任何限制或检测画布中的移动,开始您需要canvas.on("object:moving" ...
  • 对象在画布上是如何放置的?用户是要在点击事件上添加一些东西,还是您只是有一个对象并需要计算放置它的位置?是放置问题还是您正在寻找解决方案以在有限的边界内移动对象?
  • @AdamM。看看小提琴,当用户从小提琴中设置“JCrop”中的区域时,对象会以编程方式添加到画布中,看起来一切都正确完成。 _ 问题似乎与“限制对象放置”部分有关……但 Vignesh 没有提供任何反馈
  • @HelderSepulveda 谢谢你的钥匙,因为我没有尝试过任何有限制的部分,我认为有一些本机可用的东西。您的回答对实现预期结果有很大帮助。

标签: javascript canvas html5-canvas fabricjs


【解决方案1】:

这是一个如何在 FabricJS 中限制移动的示例。
我正在使用画布的有状态属性,请参阅下面的function objectMoving

var canvas = new fabric.Canvas("canvas");
canvas.stateful = true;

function inside(p, vs) {
    var inside = false;
    for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) {
        var xi = vs[i].x, yi = vs[i].y;
        var xj = vs[j].x, yj = vs[j].y;
        var intersect = yi > p.y !== yj > p.y && p.x < ((xj - xi) * (p.y - yi)) / (yj - yi) + xi;
        if (intersect) inside = !inside;
    }
    return inside;
}

function getCoords(rect) {
    var coords = []
    coords.push(rect.aCoords.tl);
    coords.push(rect.aCoords.tr);
    coords.push(rect.aCoords.br);
    coords.push(rect.aCoords.bl);
    coords.push(rect.aCoords.tl);
    return coords;
}

function objectMoving(e) {
    var cCoords = getCoords(parent);
    var inBounds = inside({ x: e.target.left + 30, y: e.target.top + 30 }, cCoords);

    if (inBounds) {
        e.target.setCoords();
        e.target.saveState();
    } else {
        e.target.left = e.target._stateProperties.left;
        e.target.top = e.target._stateProperties.top;
    }
}

var boundary = new fabric.Rect({
    width: 310, height: 170,
    left: 5, top: 5,
    selectable: false,
    strokeDashArray: [5, 2],
    stroke: "blue",
    fill: "transparent"
});

var parent = new fabric.Rect({
    width: 250, height: 110,
    left: 35, top: 35,
    selectable: false,
    strokeDashArray: [2, 5],
    stroke: "black",
    fill: "transparent"
});

var child = new fabric.Circle({
    radius: 30,
    fill: "rgba(255,0,0,0.8)",
    top: 50, left: 50,
    hasControls: false,
});

canvas.add(boundary);
canvas.add(parent);
canvas.add(child);
canvas.on("object:moving", objectMoving);
<canvas id="canvas" width="400" height="180"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js"></script>

function inside 中,我使用的是光线投射算法,您可以在此处阅读更多相关信息: https://github.com/substack/point-in-polygon/blob/master/index.js
我更喜欢这种算法,因为它为以后允许更复杂的形状作为父边界打开了大门,它可以处理任何形状的多边形。

如果您需要对该代码的任何说明,请告诉我。


现在您确实需要将其集成到您的项目中,并随着用户在“JCrop”选择中的更改而动态更改父边界

【讨论】:

  • 圆可以完全放入透明区域吗?在您的情况下,它来自透明区域。如果它出来了,它可以被隐藏并只显示透明矩形内的内容吗?
  • 我的绘图中没有“透明区域”...有两个矩形。蓝色的是外部边界,即圆的边缘永远不会超过,黑色的矩形是相对于圆心的边界。 ...如果这是您的意思,您可以选择不画黑色
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-05
  • 1970-01-01
  • 2016-05-25
  • 1970-01-01
  • 2022-06-09
  • 1970-01-01
相关资源
最近更新 更多