如何使用Fabric.js实现画布平移

时间:2015-12-22 20:18:31

标签: javascript canvas fabricjs panning

我有一个Fabric.js画布,我想实现软件包通常使用“手动”工具进行的全画布平移。当你按下其中一个鼠标按钮,然后在按住鼠标按钮的同时在画布上移动,画布的可见部分也会相应地改变。

您可以看到in this video我想要实现的目标。

为了实现这个功能,我编写了以下代码:

deal_id

但它不起作用。你可以看到in this video会发生什么。

如何按顺序修改代码:

  1. 要像第一个视频一样进行平移工作吗?

  2. 让事件处理程序使用该事件?当用户按下或释放鼠标右键时,它应该阻止上下文菜单出现。

4 个答案:

答案 0 :(得分:12)

平移Fabric画布以响应鼠标移动的一种简单方法是计算鼠标事件之间的光标位移并将其传递给relativePan

观察我们如何使用上一个鼠标事件的screenXscreenY属性来计算当前鼠标事件的相对位置:

function startPan(event) {
  if (event.button != 2) {
    return;
  }
  var x0 = event.screenX,
      y0 = event.screenY;
  function continuePan(event) {
    var x = event.screenX,
        y = event.screenY;
    fc.relativePan({ x: x - x0, y: y - y0 });
    x0 = x;
    y0 = y;
  }
  function stopPan(event) {
    $(window).off('mousemove', continuePan);
    $(window).off('mouseup', stopPan);
  };
  $(window).mousemove(continuePan);
  $(window).mouseup(stopPan);
  $(window).contextmenu(cancelMenu);
};
function cancelMenu() {
  $(window).off('contextmenu', cancelMenu);
  return false;
}
$(canvasWrapper).mousedown(startPan);

我们开始在mousedown上进行平移并继续在mousemove上进行平移。在mouseup,我们取消平移;我们也取消了mouseup - 取消函数本身。

右键菜单(也称为上下文菜单)将通过返回false取消。菜单取消功能也会取消。因此,如果您随后在画布包装器外部单击,则上下文菜单将起作用。

以下是展示此方法的页面:

http://michaellaszlo.com/so/fabric-pan/

您将在Fabric画布上看到三个图像(可能需要一两个时间才能加载图像)。您将能够使用标准Fabric功能。您可以左键单击图像以移动它们,拉伸它们并旋转它们。但是,当您在画布容器中右键单击时,可以使用鼠标平移整个Fabric画布。

答案 1 :(得分:2)

不确定FabricJS,但它可能是这样的:

  1.   

    让它像第一个视频一样工作:

    通过使用CSS cursor属性,使用javascript在mousedownmouseup事件上切换它。

  2.   

    事件处理程序使用事件(当用户释放鼠标右键时,阻止上下文菜单出现):

    使用javascript,我们会在false事件

  3. 上返回contextmenu

    CODE: 有一点问题 (*)

    使用jQuery JS Fiddle 1

    $('#test').on('mousedown', function(e){
        if (e.button == 2) {
            // if right-click, set cursor shape to grabbing
            $(this).css({'cursor':'grabbing'});
        }
    }).on('mouseup', function(){
        // set cursor shape to default
        $(this).css({'cursor':'default'});
    }).on('contextmenu', function(){ 
        //disable context menu on right click
        return false;
    });
    

    使用原始javascript JS Fiddle 2

    
    
    var test = document.getElementById('test');
    
    test.addEventListener('mousedown', function(e){
        if (e.button == 2) {
            // if right-click, set cursor shape to grabbing
            this.style.cursor = 'grabbing';
        }
    });
    test.addEventListener('mouseup', function(){
        // set cursor shape to default
        this.style.cursor = 'default';
    });
    test.oncontextmenu = function(){ 
        //disable context menu on right click
        return false;
    }
    
    
    

    (*) 问题:

    上面的代码段可以正常工作但是存在跨浏览器问题,如果你在Firefox中检查上面的小提琴 - 或Opera - 你会看到正确的行为,在Chrome和IE11中检查时 - 没有使用Edge或Safari检查 - 我发现Chrome和IE不支持grabbing光标形状,所以在在上面的代码片段中,将光标线更改为:

    jQuery:$(this).css({'cursor':'move'}); JS Fiddle 3

    原始javascript:this.style.cursor = 'move'; JS Fiddle 4

    现在我们有一个工作代码,但没有手形光标。但有以下解决方案: -

    <强>解:

    1. Chrome和Safari支持grabgrabbing的{​​{1}}前缀如下:

      -webkit-

      但你需要首先进行浏览器嗅探,如果Firefox是默认和标准代码,如果Chrome和Safari然后使用$(this).css({'cursor': '-webkit-grabbing'}); 前缀,这仍然会使IE退出游戏。

      < / LI>
    2. 查看this example,使用Chrome,Safari,Firefox,Opera和IE进行测试,您可以看到-webkit-所有浏览器中正常运行。 Chrome,Safari,Firefox和Opera显示黄色微笑图像cursor: url(foo.bar),但IE显示红色球状光标smiley.gif

      所以我认为你可以利用这个,并抓住像这样的抓手形象 enter image description here

      或者这个:

      enter image description here

      您可以使用上述图片,url(myBall.cur)png格式,除了支持gif的IE以外的所有浏览器,因此您需要找到一种方法将其转换为.cur。 Google搜索显示了convert image to cur

    3. 的许多结果

      请注意 ,尽管这个.cur - 的回退支持用逗号分隔,但在上面显示的W3Schools示例中效果很好,我无法在javascript中以相同的方式使用它,我尝试cursor:url(smiley.gif),url(myBall.cur),auto;但它没有工作。   我也试过把它作为CSS类

      $(this).css({'cursor': 'grabbing, move'});

      然后使用jQuery .myCursor{ cursor: grabbing, -webkit-grabbing, move; } 但无效。

      所以你仍然需要让浏览器嗅探你是否要进行第二个解决方案或两个解决方案的混合修复,this is my code我已经用了几次来检测浏览器并且它在这篇文章的时间,但你并不需要&#34; Mobile&#34;和&#34; Kindle&#34;部分。

      &#13;
      &#13;
      $(this).addClass('myCursor');
      &#13;
      &#13;
      &#13;

      <强>资源:

答案 2 :(得分:2)

我在Github上有一个使用fabric.js Canvas平移的示例:https://sabatinomasala.github.io/fabric-clipping-demo/

负责平移行为的代码如下:https://github.com/SabatinoMasala/fabric-clipping-demo/blob/master/src/classes/Panning.js

它是fabric.Canvas.prototype上的一个简单扩展程序,可让您切换“拖动模式”&#39;在画布上如下:

canvas.toggleDragMode(true); // Start panning
canvas.toggleDragMode(false); // Stop panning

请查看以下代码段,整个代码中都提供了文档。

&#13;
&#13;
const STATE_IDLE = 'idle';
const STATE_PANNING = 'panning';
fabric.Canvas.prototype.toggleDragMode = function(dragMode) {
  // Remember the previous X and Y coordinates for delta calculations
  let lastClientX;
  let lastClientY;
  // Keep track of the state
  let state = STATE_IDLE;
  // We're entering dragmode
  if (dragMode) {
    // Discard any active object
    this.discardActiveObject();
    // Set the cursor to 'move'
    this.defaultCursor = 'move';
    // Loop over all objects and disable events / selectable. We remember its value in a temp variable stored on each object
    this.forEachObject(function(object) {
      object.prevEvented = object.evented;
      object.prevSelectable = object.selectable;
      object.evented = false;
      object.selectable = false;
    });
    // Remove selection ability on the canvas
    this.selection = false;
    // When MouseUp fires, we set the state to idle
    this.on('mouse:up', function(e) {
      state = STATE_IDLE;
    });
    // When MouseDown fires, we set the state to panning
    this.on('mouse:down', (e) => {
      state = STATE_PANNING;
      lastClientX = e.e.clientX;
      lastClientY = e.e.clientY;
    });
    // When the mouse moves, and we're panning (mouse down), we continue
    this.on('mouse:move', (e) => {
      if (state === STATE_PANNING && e && e.e) {
        // let delta = new fabric.Point(e.e.movementX, e.e.movementY); // No Safari support for movementX and movementY
        // For cross-browser compatibility, I had to manually keep track of the delta

        // Calculate deltas
        let deltaX = 0;
        let deltaY = 0;
        if (lastClientX) {
          deltaX = e.e.clientX - lastClientX;
        }
        if (lastClientY) {
          deltaY = e.e.clientY - lastClientY;
        }
        // Update the last X and Y values
        lastClientX = e.e.clientX;
        lastClientY = e.e.clientY;

        let delta = new fabric.Point(deltaX, deltaY);
        this.relativePan(delta);
        this.trigger('moved');
      }
    });
  } else {
    // When we exit dragmode, we restore the previous values on all objects
    this.forEachObject(function(object) {
      object.evented = (object.prevEvented !== undefined) ? object.prevEvented : object.evented;
      object.selectable = (object.prevSelectable !== undefined) ? object.prevSelectable : object.selectable;
    });
    // Reset the cursor
    this.defaultCursor = 'default';
    // Remove the event listeners
    this.off('mouse:up');
    this.off('mouse:down');
    this.off('mouse:move');
    // Restore selection ability on the canvas
    this.selection = true;
  }
};

// Create the canvas

let canvas = new fabric.Canvas('fabric')
canvas.backgroundColor = '#f1f1f1';

// Add a couple of rects

let rect = new fabric.Rect({
  width: 100,
  height: 100,
  fill: '#f00'
});
canvas.add(rect)

rect = new fabric.Rect({
  width: 200,
  height: 200,
  top: 200,
  left: 200,
  fill: '#f00'
});
canvas.add(rect)

// Handle dragmode change

let dragMode = false;
$('#dragmode').change(_ => {
  dragMode = !dragMode;
  canvas.toggleDragMode(dragMode);
});
&#13;
<div>
  <label for="dragmode">
    Enable panning
    <input type="checkbox" id="dragmode" name="dragmode" />
  </label>
</div>
<canvas width="300" height="300" id="fabric"></canvas>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.15/fabric.min.js"></script>
&#13;
&#13;
&#13;

答案 3 :(得分:1)

我在jsfiddle上做了一个例子,我们实际上可以将整个画布及其所有对象拖动到父div中,就像图片一样,我将尝试逐步解释它。

enter image description here

  1. 首先我下载了​​拖动库jquery.dradscroll.js,你可以在网上找到它。这是一个小的js文件,只需很少的更改就可以帮助我们完成任务。 下载链接:http://www.java2s.com/Open-Source/Javascript_Free_Code/jQuery_Scroll/Download_jquery_dragscroll_Free_Java_Code.htm

  2. 创建保存画布的容器。

      <div class="content">
        <canvas id="c" width="600" height="700" ></canvas>    
      </div>
    
  3. 小css

    .content{
       overflow:auto;
       width:400px;
       height:400px;
    }
    
  4. 的javascript:

    一个。创建画布。

    湾制作默认光标,当它在画布上时,打开手

    canvas.defaultCursor = 'url(" http://maps.gstatic.com/intl/en_us/mapfiles/openhand_8_8.cur") 15 15, crosshair';

    ℃。覆盖__onMouseDown函数,以更改 closedhand 光标(结束时)。

         fabric.Canvas.prototype.__onMouseDown = function(e){
      // accept only left clicks
      var isLeftClick  = 'which' in e ? e.which === 1 : e.button === 1;
      if (!isLeftClick && !fabric.isTouchSupported) {
        return;
      }
    
    
      if (this.isDrawingMode) {
        this._onMouseDownInDrawingMode(e);
        return;
      }
    
      // ignore if some object is being transformed at this moment
      if (this._currentTransform) {
        return;
      }
    
      var target = this.findTarget(e),
          pointer = this.getPointer(e, true);
    
      // save pointer for check in __onMouseUp event
      this._previousPointer = pointer;
    
      var shouldRender = this._shouldRender(target, pointer),
          shouldGroup = this._shouldGroup(e, target);
    
      if (this._shouldClearSelection(e, target)) {
        this._clearSelection(e, target, pointer);
      }
      else if (shouldGroup) {
        this._handleGrouping(e, target);
        target = this.getActiveGroup();
      }
    
      if (target && target.selectable && !shouldGroup) {
        this._beforeTransform(e, target);
        this._setupCurrentTransform(e, target);
      }
      // we must renderAll so that active image is placed on the top canvas
      shouldRender && this.renderAll();
    
      this.fire('mouse:down', { target: target, e: e });
      target && target.fire('mousedown', { e: e });
    
      if(!canvas.getActiveObject() || !canvas.getActiveGroup()){
        flag=true;
        //change cursor to closedhand.cur
        canvas.defaultCursor = 'url("http://maps.gstatic.com/intl/en_us/mapfiles/closedhand_8_8.cur") 15 15, crosshair';     
    }//end if
    
  5. 覆盖__onMouseUp事件,将光标更改为openhand。

       fabric.Canvas.prototype.__onMouseUp = function(e){
           if(flag){
              canvas.defaultCursor = 'url(" http://maps.gstatic.com/intl/en_us/mapfiles/openhand_8_8.cur") 15 15, crosshair';
              flag=false;
           }
        };
    
  6. 初始化dragScroll()以处理托管画布的内容:

        $('.content').dragScroll({});
    
  7. 对jquery.dragScroll.js文件进行一些小的更改,以便了解何时拖动画布以及何时不。在mousedown()事件中,我们添加一个if语句来检查我们是否有一个活动对象或组。如果是,则不拖动画布。

       $($scrollArea).mousedown(function (e) {
        if (canvas.getActiveObject() || canvas.getActiveGroup()) {
            console.log('no drag');return;
        } else {                                             
           console.log($('body'));
            if (typeof options.limitTo == "object") {
                for (var i = 0; i < options.limitTo.length; i++) {
                    if ($(e.target).hasClass(options.limitTo[i])) {
                        doMousedown(e);
                    }
                }
            } else {
                doMousedown(e);
            }
        }
    });
    
  8. mousedown 事件中,我们抓取DOM元素(.content)并获取 top&amp;左侧位置

      function doMousedown(e) {
    
            e.preventDefault();
            down = true;
            x = e.pageX;
            y = e.pageY;        
            top = e.target.parentElement.parentElement.scrollTop; // .content
            left = e.target.parentElement.parentElement.scrollLeft;// .content
        }
    
  9. 如果我们不想让滚动条可见:

        .content{
       overflow:hidden;
       width:400px;
       height:400px;
    

    }

  10. 但是有一个小问题,jsfiddle只接受https库,所以它会阻止fabricjs,除非你从&#39; https://rawgit.com/kangax/fabric.js/master/dist/fabric.js&#39;添加它,但它仍然会阻止它有时(至少在我的chrome和mozilla上)。

  11. jsfiddle示例:https://jsfiddle.net/tornado1979/up48rxLs/

    在您的浏览器上,您可能会比我更好运,但它确实可以在您的实时应用上运行。

    反正   我希望有所帮助,祝你好运。