首页 > 解决方案 > 在放大织物 js 时保持对象大小和位置

问题描述

我试图在缩放时保持对象大小,我试图从这个答案中获得灵感,其中编写它的人在这种情况下没有解决控件问题,因此你可以看到它们没有粘在对象上在此屏幕截图中缩放时。

但是我提出了这个解决方案来维护对象的位置和控件,方法是在基于倒置的viewportTransform计算它们之后更新它的左侧顶部,方法是使用fabric.util.transformPoint函数计算一个新的fabric.Point

fabric.Object.prototype.transform = function(ctx) {
    const obj = this;
    const {
        ignoreZoom,
        group,
        canvas,
        left,
        top
    } = obj;
    const {
        contextTop,
        viewportTransform,
        getZoom,
        requestRenderAll,
    } = canvas;

    var needFullTransform = (group && !group._transformDone) || (group && canvas && ctx === contextTop);

    if (ignoreZoom) {
        const oldP = new fabric.Point(left, top);
        const newP = fabric.util.transformPoint(oldP, fabric.util.invertTransform(viewportTransform));
        var zoom = 1 / getZoom();
        /* // here i tried to refresh the whole canvas with requestRenderAll()
        this.set({
            left: newP.x,
            top: newP.y,
            scaleX: zoom,
            scaleY: zoom,
        });
        this.setCoords();
        requestRenderAll();
        */
        // but here i try refresh the object only which is better i think
        this.left = newP.x;
        this.top = newP.y;
        this.scaleX = zoom;
        this.scaleY = zoom;
        this.drawObject(ctx);
    }
    var m = this.calcTransformMatrix(!needFullTransform);
    ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
}

我已经制作了这个代码框作为我的代码的演示。正如您在此屏幕截图中看到的那样,控件粘在对象周围,但它们整体并没有保持相对于背景的位置,有时它们会完全消失。我需要对象保持其相对于背景的位置。如何让它变得更好?

//编辑

我试图更好地理解缩放时会发生什么,我找到了用于缩放的fabric.Canvas.zoomToPoint() (如他们的教程中所示

   zoomToPoint: function (point, value) {
      // TODO: just change the scale, preserve other transformations
      var before = point, vpt = this.viewportTransform.slice(0);
      point = transformPoint(point, invertTransform(this.viewportTransform));
      vpt[0] = value;
      vpt[3] = value;
      var after = transformPoint(point, vpt);
      vpt[4] += before.x - after.x;
      vpt[5] += before.y - after.y;
      return this.setViewportTransform(vpt);
    },

我想相对于背景固定对象位置的最佳方法是应用应用于画布的逆变换以缩放到对象。所以我写了这个函数

function getNewVpt(point, value) {
  var before = point, 
  vpt = canvas.viewportTransform.slice(0);

  point = fabric.util.transformPoint(point, fabric.util.invertTransform(canvas.viewportTransform));

  vpt[0] = value;
  vpt[3] = value;
  var after = fabric.util.transformPoint(point, vpt);
  vpt[4] += before.x - after.x;
  vpt[5] += before.y - after.y;
  return vpt;
}

我用它重写了fabric.Object.prototype.transform

fabric.Object.prototype.transform = function (ctx) {
  const obj = this;
  const { ignoreZoom, group, canvas: objCanvas, left, top } = obj;
  const {
    contextTop,
    viewportTransform,
  } = objCanvas;
  var needFullTransform =
    (group && !group._transformDone) ||
    (group && objCanvas && ctx === contextTop);

  if (ignoreZoom && zoomingIsOn) {
    zoomingIsOn = false;
    var zoom = 1 / objCanvas.getZoom();
    const oldP = new fabric.Point(left, top);
    console.log('transform : oldP : ', oldP);
    const newVpt = getNewVpt(oldP, zoom)
    const newP = fabric.util.transformPoint(oldP, newVpt);
    console.log('transform : newP : ', newP);    

    // here i tried to refresh the whole canvas with requestRenderAll()
    this.set({
      left: newP.x,
      top: newP.y,
      scaleX: zoom,
      scaleY: zoom
    });
    this.setCoords();
    console.log('transform : CALLING objCanvas.requestRenderAll() ');
    objCanvas.requestRenderAll();

    // but here i try refresh the object only which is better i think
    // this.left = newP.x;
    // this.top = newP.y;
    // this.scaleX = zoom;
    // this.scaleY = zoom;
    // this.drawObject(ctx);
  }
  var m = this.calcTransformMatrix(!needFullTransform);
  ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
};

在这里,我为第二个解决方案创建了这个新的代码框,结果似乎比前一个解决方案更好,但仍然不完美。我可能仍然做错了什么?!

//编辑 2

我试图将 objCanvas.getZoom() 而不是 zoom 作为第二个参数传递给 getNewVpt() 函数。似乎还有一些改进,但仍然不完美

//编辑 3这个代码框里,我可能得到了最好的结果,我可以使用另一个直接返回新点的函数:

function getNewPt(point, value) {
  // TODO: just change the scale, preserve other transformations
  var vpt = canvas.viewportTransform.slice(0);

  point = fabric.util.transformPoint(point, fabric.util.invertTransform(canvas.viewportTransform));

  vpt[0] = value;
  vpt[3] = value;
  
  return fabric.util.transformPoint(point, vpt);;
}

我仍然希望任何人都可以告诉我是否有办法进一步改进它。如您所见,三角形在缩放/取消缩放后返回到其初始位置并返回到相同的初始缩放值,这很好,但在初始和最终状态之间,它似乎仍然不在正确的位置..

标签: fabricjs

解决方案


您只需调用 zoomToPoint 进行缩放,对象将保持其相对于背景的位置和比例。

尝试以下

canvas.on('mouse:wheel', function(opt) {

  // console.log(opt.e.deltaY)
  let zoomLevel = canvas.getZoom();
  // console.log('zoom Level: ', (zoomLevel * 100).toFixed(0), '%');
  zoomLevel += opt.e.deltaY * -0.01;

  // Restrict scale
  zoomLevel = Math.min(Math.max(.125, zoomLevel), 20);

  canvas.zoomToPoint(
      new fabric.Point(opt.e.offsetX, opt.e.offsetY),
      zoomLevel,
  );
  canvas.renderAll();

  })

推荐阅读