簡體   English   中英

如何使用 Fabric.js 實現 canvas 平移

[英]How to implement canvas panning with Fabric.js

我有一個 Fabric.js canvas,我想實現軟件包通常使用“手動”工具執行的全畫布平移。 當您按下鼠標按鈕之一,然后在按住鼠標按鈕的同時在 canvas 上移動時,canvas 的可見部分會相應地發生變化。

您可以在此視頻中看到我想要實現的目標。

為了實現此功能,我編寫了以下代碼:

$(canvas.wrapperEl).on('mousemove', function(evt) {
    if (evt.button == 2) { // 2 is the right mouse button
        canvas.absolutePan({
            x: evt.clientX,
            y: evt.clientY
        });
    }
});

但它不起作用。 你可以在這個視頻中看到發生了什么。

如何按順序修改我的代碼:

  1. 像第一個視頻那樣平移工作?

  2. 讓事件處理程序消費事件? 它應該防止在用戶按下或釋放鼠標右鍵時出現上下文菜單。

平移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 -cancelling函數本身。

右鍵單擊菜單(也稱為上下文菜單)將通過返回false來取消。 菜單取消功能也會取消。 因此,如果隨后在畫布包裝器外單擊,則上下文菜單將起作用。

這是一個展示這種方法的頁面:

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

您將在Fabric畫布上看到三個圖像(可能需要一兩個時間才能加載圖像)。 您將能夠使用標准Fabric功能。 您可以左鍵單擊圖像以移動它們,拉伸它們並旋轉它們。 但是,當您在畫布容器中右鍵單擊時,可以使用鼠標平移整個Fabric畫布。

不確定FabricJS,但它可能是這樣的:

  1. 它像第一個視頻一樣工作:

    通過使用CSS cursor屬性,使用javascript在mousedownmouseup事件上切換它。

  2. 事件處理程序使用事件(當用戶釋放鼠標右鍵時,阻止上下文菜單出現):

    使用javascript我們在contextmenu事件上返回false

代碼: 有一點問題 (*)

使用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小提琴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小提琴3

原始javascript: this.style.cursor = 'move'; JS小提琴4

現在我們有一個工作代碼但沒有手形光標。 但有以下解決方案: -

解決方案:

  1. Chrome和Safari支持使用-webkit-前綴grabgrabbing ,如:

     $(this).css({'cursor': '-webkit-grabbing'}); 

    但是你需要首先進行瀏覽器嗅探,如果Firefox然后是默認和標准代碼,如果Chrome和Safari然后使用-webkit-前綴,這仍然會使IE退出游戲。

  2. 看一下這個例子 ,用Chrome,Safari,Firefox,Opera和IE測試你可以看到cursor: url(foo.bar)所有瀏覽器中都可以工作和支持。 Chrome,Safari,Firefox和Opera顯示黃色微笑圖像smiley.gif ,但IE顯示紅色球光標url(myBall.cur)

    所以我認為你可以利用這個,並抓住像這樣的抓手形象 在此輸入圖像描述

    或這個:

    在此輸入圖像描述

    您可以使用上面的圖像, pnggif格式,除IE以外的所有瀏覽器都支持.cur ,因此您需要找到將其轉換為.cur 谷歌搜索顯示許多轉換圖像到cur的結果

注意 ,雖然這個cursor:url(smiley.gif),url(myBall.cur),auto; - 以逗號分隔的后備支持 ,在上面顯示的W3Schools示例中運行良好,我無法在javascript中以相同的方式工作,我嘗試了$(this).css({'cursor': 'grabbing, move'}); 但它不起作用。 我也試過把它作為CSS類

.myCursor{ cursor: grabbing, -webkit-grabbing, move; }

然后使用jQuery $(this).addClass('myCursor'); 但也無濟於事。

所以你仍然需要讓瀏覽器嗅探你是第二個解決方案還是兩個解決方案的混合修復程序, 這是我用了幾次檢測瀏覽器的代碼,它在這篇文章的時候效果很好但是你porbabely不需要“手機”和“Kindle”部件。

 // Detecting browsers $UA = navigator.userAgent; if ($UA.match(/firefox/i)) { $browser = 'Firefox'; } else if ($UA.indexOf('Trident') != -1 && $UA.indexOf('MSIE') == -1) { $browser = 'MSIE'; } else if ($UA.indexOf('MSIE') != -1) { $browser = 'MSIE'; } else if ($UA.indexOf('OPR/') != -1) { $browser = 'Opera'; } else if ($UA.indexOf("Chrome") != -1) { $browser = 'Chrome'; } else if ($UA.indexOf("Safari")!=-1) { $browser = 'Safari'; } if($UA.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Nokia|Mobile|Opera Mini/i)) { $browser = 'Mobile'; }else if($UA.match(/KFAPWI/i)){ $browser = 'Kindle'; } console.log($browser); 


資源:

我在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上的一個簡單擴展,它允許您在畫布上切換“拖動模式”,如下所示:

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

請查看以下代碼段,整個代碼中都提供了文檔。

 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 = eeclientX; lastClientY = eeclientY; }); // When the mouse moves, and we're panning (mouse down), we continue this.on('mouse:move', (e) => { if (state === STATE_PANNING && e && ee) { // let delta = new fabric.Point(eemovementX, eemovementY); // 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 = eeclientX - lastClientX; } if (lastClientY) { deltaY = eeclientY - lastClientY; } // Update the last X and Y values lastClientX = eeclientX; lastClientY = eeclientY; 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); }); 
 <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> 

我在jsfiddle上做了一個例子,我們實際上可以將整個畫布及其所有對象拖動到父div中,就像圖片一樣,我將嘗試逐步解釋它。

在此輸入圖像描述

  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';

    C。 覆蓋__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)並獲取頂部和左側位置

      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,除非你從' https://rawgit.com/kangax/fabric.js/master/dist/fabric.js '添加它,但是再次它仍然阻止它一些(至少在我的chrome和mozilla上)。

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

你可能在瀏覽器上有比我更好的運氣,但它肯定會在你的實時應用程序上運行。

無論如何,我希望有所幫助,祝你好運。

我知道這已經得到回答,但我使用新版本的 fabricjs (4.5.0) 重新編輯了在此答案中創建的筆

筆: https://codepen.io/201flaviosilva/pen/GROLbQa

在這種情況下,我使用鼠標中鍵進行平移:)

// Enable mouse middle button
canvas.fireMiddleClick = true;

// Mouse Up Event
if (e.button === 2) currentState = STATE_IDLE;

// Mouse Down Event
if (e.button === 2) currentState = STATE_PANNING;

:)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM