web-dev-qa-db-ja.com

Fabric.jsでキャンバスパンを実装する方法

私はFabric.jsキャンバスを持っており、ソフトウェアパッケージが通常「手動」ツールで実行するフルキャンバスパンを実装したいと考えています。いずれかのマウスボタンを押してから、マウスボタンを押しながらキャンバス上を移動すると、キャンバスの表示部分が変化します。

あなたは このビデオで 私が達成したいことを見ることができます。

この機能を実装するために、次のコードを記述しました。

$(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. イベントハンドラーがイベントを消費するには?これにより、ユーザーがマウスの右ボタンを押すか離したときにコンテキストメニューが表示されなくなります。

18

マウスの動きに応じてファブリックキャンバスをパンする簡単な方法は、マウスイベント間のカーソルの変位を計算してrelativePanに渡すことです。

前のマウスイベントのscreenXプロパティとscreenYプロパティを使用して、現在のマウスイベントの相対位置を計算する方法を確認します。

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/

ファブリックキャンバス上に3つの画像が表示されます(画像の読み込みには1〜2時間がかかる場合があります)。標準のファブリック機能を使用できるようになります。画像を左クリックすると、画像を移動したり、拡大したり、回転したりできます。ただし、キャンバスコンテナー内で右クリックすると、ファブリックキャンバス全体をマウスでパンします。

14
Michael Laszlo

Fabric.js Canvas panningを使用したGithubの例があります: 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 = 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);
});
<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>
4
Sabatino

FabricJSについてはわかりませんが、次のように考えられます。

  1. それが最初のビデオのように機能するために:

    CSSのcursorプロパティを利用することで、JavaScriptを使用してmousedownおよびmouseupイベントでプロパティを切り替えることができます。

  2. イベントハンドラーがイベントを消費します(ユーザーがマウスの右ボタンを離したときにコンテキストメニューが表示されないようにします)。

    JavaScriptを使用して、falseイベントでcontextmenuを返します

コード:少し問題あり (*)

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 and IE11-それをEdgeまたはSafariでチェックしなかった-私はChromeおよび= IE grabbingカーソル形状をサポートしないため、上記のコードスニペットで、カーソル行を次のように変更します。

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

生のjavascript:_this.style.cursor = 'move';_ JS Fiddle 4

これで作業コードができましたが、ハンドカーソルはありません。しかし、次の解決策があります:-

解決策:

  1. ChromeとSafariは、grabgrabbingをサポートし、次のような_-webkit-_プレフィックスを付けます。

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

    ただし、最初にブラウザのスニッフィングを行う必要があります。Firefoxの場合はデフォルトの標準コード、ChromeおよびSafariの場合は_-webkit-_接頭辞を付けると、IEゲーム外です。

  2. this example を見て、Chrome、Safari、Firefox、Opera and IE cursor: url(foo.bar)が機能し、[〜#〜]すべての[〜#〜]ブラウザでサポートされていることがわかります。Chrome、Safari、 FirefoxおよびOperaは黄色の笑顔の画像_smiley.gif_を示しますが、IEは赤いボールカーソルurl(myBall.cur)を示します。

    ですから、これを使って、この手の画像をつかむことができると思います enter image description here

    またはこれ:

    enter image description here

    上記のようなpngまたはgif形式の画像は、IEが_.cur_をサポートする)を除くすべてのブラウザで使用できるため、 _.cur_に変換する方法を見つけてください。Google検索では convert image to 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');を使用しますが、使用できません。

したがって、2番目のソリューションを実行する場合でも、両方のソリューションのハイブリッドフィックスを実行する場合でも、ブラウザをスニッフィングする必要があります これは私のコードです これは私ですブラウザを検出するために数回使用しましたが、この投稿の時点ではうまく機能していましたが、「モバイル」と「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);_

リソース:

2
Mi-Creativity

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:

    a。キャンバスを作成します。

    b。デフォルトのカーソルを作成し、キャンバス上にあるとき、手を開きます

    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イベントをオーバーライドして、カーソルをオープンハンドに戻します。

    _   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ステートメントを追加して、アクティブなオブジェクトまたはグループがあるかどうかを確認します。trueの場合、キャンバスのドラッグはありません。

    _   $($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&left position

    _  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ライブラリのみを受け入れるため、 ' https://rawgit.com/kangax/fabric.js/master/dist/fabric。 js 'ですが、やはり何度かブロックされます(少なくともmy chrome and mozilla)。

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

あなたは私よりもあなたのブラウザーでより幸運を持っているかもしれませんが、それは間違いなくあなたのライブアプリで動作します。

とにかく、私は、幸運を助けることを願っています。

1
Theo Itzaris