tutorial javascript canvas fabricjs panning

javascript - tutorial - fabric js



Cómo implementar el paneo de lienzo con Fabric.js (4)

Tengo un lienzo Fabric.js y quiero implementar la panorámica de todo el lienzo que los paquetes de software suelen hacer con una herramienta "manual". Es cuando presiona uno de los botones del mouse, luego se mueve sobre el lienzo mientras mantiene presionado el botón del mouse y la parte visible del lienzo cambia en consecuencia.

Puedes ver en este video lo que quiero lograr.

Para implementar esta funcionalidad escribí el siguiente código:

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

Pero no funciona. Puedes ver en este video lo que pasa.

¿Cómo puedo modificar mi código en orden?

  1. ¿Para que la panorámica funcione como en el primer video?

  2. ¿Para que el manejador de eventos consuma el evento? Debe evitar que aparezca el menú contextual cuando el usuario presiona o suelta el botón derecho del mouse.


He hecho un ejemplo en jsfiddle, en el que podemos arrastrar todo el lienzo con todos sus objetos, a un div padre, como la imagen, e intentaré explicarlo paso a paso.

  1. En primer lugar descargo la biblioteca de arrastre jquery.dradscroll.js, puede encontrarla en la red. Este es un pequeño archivo js que con pequeños cambios puede ayudarnos a completar la tarea. enlace de descarga: http://www.java2s.com/Open-Source/Javascript_Free_Code/jQuery_Scroll/Download_jquery_dragscroll_Free_Java_Code.htm

  2. Crea el contenedor que contiene nuestro lienzo.

    <div class="content"> <canvas id="c" width="600" height="700" ></canvas> </div>

  3. pequeño css

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

  4. javascript:

    a. Crea el lienzo.

    segundo. hacer cursor por defecto, cuando está sobre lienzo, mano abierta

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

    do. anule la función __onMouseDown, para cambiar al cursor a mano cerrada (al final).

    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. anula el evento __onMouseUp, para volver a cambiar el cursor a 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. Inicializa dragScroll () para trabajar en el contenido que aloja el lienzo:

    $(''.content'').dragScroll({});

  7. Algunos pequeños cambios en el archivo jquery.dragScroll.js, para entender cuándo arrastrar el lienzo y cuándo no . En el evento mousedown () agregamos una sentencia if para verificar si tenemos un objeto o grupo activo. Si es cierto, no hay arrastre de lienzo.

    $($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. en el evento de mousedown tomamos el elemento DOM (.content) y obtenemos la posición superior e izquierda

    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. Si no queremos tener las barras de desplazamiento visibles:

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

    }

  10. Sin embargo, hay un pequeño problema, jsfiddle, acepta solo bibliotecas https, por lo que bloquea fabricjs, excepto si lo agrega desde '' https://rawgit.com/kangax/fabric.js/master/dist/fabric.js '', pero de nuevo, todavía lo bloquea algunas veces (al menos en mi chrome y mozilla).

Ejemplo de jsfiddle: https://jsfiddle.net/tornado1979/up48rxLs/

Puede que tengas más suerte que yo en tu navegador, pero definitivamente funcionará en tu aplicación en vivo.

De todos modos, espero que te ayude, buena suerte.


No estoy seguro de FabricJS, pero podría ser de esta manera:

  1. Para que funcione como en el primer video:

    Haciendo uso de la propiedad del cursor de CSS, mouseup eventos de mousedown y mouseup usando javascript.

  2. el controlador de eventos consume el evento (evita que aparezca el menú contextual, cuando el usuario suelta el botón derecho del mouse):

    Usando javascript devolvemos false en el evento de contextmenu

CÓDIGO: con un pequeño problema (*)

utilizando 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; });

Usando javascript en bruto 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; }

(*) Problema:

Los fragmentos de código anteriores funcionan como deberían, pero hay un problema en varios navegadores, si verificas los problemas anteriores en Firefox ( u Opera) verás el comportamiento correcto, cuando se verifique en Chrome e IE11, no lo verificaste con Edge o Safari : descubrí que Chrome e IE no admiten la forma del cursor de captura, por lo que en los fragmentos de código anteriores, cambie las líneas del cursor a esto:

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

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

Ahora tenemos un código de trabajo pero sin el cursor de la mano. Pero hay la siguiente solución:

SOLUCIONES:

  1. Chrome y Safari admiten la -webkit- y -webkit- con el prefijo -webkit- como:

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

    pero primero debes hacer que el navegador rastree, si Firefox es el código predeterminado y estándar, si Chrome y Safari luego con el prefijo -webkit- , y esto aún hace que IE salga del juego.

  2. Eche un vistazo a este ejemplo , pruébelo con Chrome, Safari, Firefox, Opera e IE. Puede ver que el cursor: url(foo.bar) funciona y es compatible con TODOS los navegadores. Chrome, Safari, Firefox y Opera muestran la imagen de la sonrisa amarilla smiley.gif , pero IE muestra la url(myBall.cur) cursor de la bola roja url(myBall.cur) .

    Así que creo que puedes hacer uso de esto, y una imagen de mano como esta

    O esto:

    Puedes usar una imagen como la anterior, un formato png o gif con todos los navegadores, excepto IE, que admite .cur , por lo que necesitas encontrar una forma de convertirla en .cur . La búsqueda de Google muestra muchos resultados de convertir la imagen a cur

Tenga en cuenta que , aunque este cursor:url(smiley.gif),url(myBall.cur),auto; - con soporte de respaldo separado por comas , funciona bien en el ejemplo de W3Schools que se muestra arriba, no pude hacer que funcionara de la misma manera en javascript, probé $(this).css({''cursor'': ''grabbing, move''}); pero no funcionó. También traté de hacerlo como clase CSS

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

Luego con jQuery $(this).addClass(''myCursor''); Pero tampoco sirve de nada.

Por lo tanto, aún debe hacer un rastreo del navegador, ya sea que esté utilizando la segunda solución o una solución híbrida de ambas soluciones, este es mi código que he usado dos veces para detectar el navegador y funcionó bien en el momento de esta publicación, pero usted Por lo que no necesitará las partes "Mobile" y "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);

Recursos:


Tengo un ejemplo en Github que usa el panorama de Canvas de fabric.js: https://sabatinomasala.github.io/fabric-clipping-demo/

El código responsable del comportamiento de la panorámica es el siguiente: https://github.com/SabatinoMasala/fabric-clipping-demo/blob/master/src/classes/Panning.js

Es una extensión simple en el fabric.Canvas.prototype , que le permite alternar ''modo de arrastre'' en el lienzo de la siguiente manera:

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

Eche un vistazo al siguiente fragmento de código, la documentación está disponible en todo el código.

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>


Una forma fácil de desplazar un lienzo de Fabric en respuesta al movimiento del mouse es calcular el desplazamiento del cursor entre los eventos del mouse y pasarlo a relativePan .

Observe cómo podemos usar las propiedades screenX y screenY del evento anterior del mouse para calcular la posición relativa del evento actual del mouse:

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

Comenzamos a desplazarnos en mousedown y continuamos a mousemove en mousemove . En mouseup , cancelamos la panorámica; También cancelamos la función de mouseup mouseup.

El menú del botón derecho, también conocido como menú contextual, se cancela al devolver false . La función de cancelación de menú también se cancela a sí misma. Por lo tanto, el menú contextual funcionará si posteriormente hace clic fuera del envoltorio del lienzo.

Aquí hay una página que demuestra este enfoque:

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

Verá tres imágenes en un lienzo de tela (puede tardar uno o dos minutos en cargar las imágenes). Podrás usar la funcionalidad estándar de Fabric. Puede hacer clic izquierdo en las imágenes para moverlas, estirarlas y rotarlas. Pero cuando hace clic con el botón derecho dentro del contenedor de lienzo, puede desplazar todo el lienzo de Fabric con el mouse.