tutorial library example create html5-canvas photo fabricjs image-clipping

html5-canvas - library - fabricjs documentation



Múltiples áreas de recorte en el lienzo de Fabric.js. (7)

Para hacer Photo Collage Maker , utilizo JS de tejido que tiene una función de recorte basada en objetos. Esta función es excelente, pero la imagen dentro de esa región de recorte no se puede escalar, mover o rotar. Quiero una región de recorte de posición fija y la imagen se puede colocar dentro del área de recorte fija como el usuario desee.

Busqué en Google y encontré una solución muy cercana.

var canvas = new fabric.Canvas(''c''); var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.rect(10,10,150,150); ctx.rect(180,10,200,200); ctx.closePath(); ctx.stroke(); ctx.clip();

Múltiples áreas de recorte en tela js lienzo

donde la imagen de una región de recorte aparece en otra región de recorte. ¿Cómo puedo evitar esto o hay otra forma de lograrlo utilizando fabric js.


Actualizar a Promlnc respuesta.

Debe reemplazar el orden de las transformaciones de contexto para realizar el recorte adecuado.

  1. traducción
  2. escalada
  3. rotación

De lo contrario, obtendrá un objeto recortado incorrectamente: al escalar sin mantener la relación de aspecto (cambiando solo una dimensión).

Código (69-72):

ctx.translate( ctxLeft, ctxTop ); ctx.rotate(degToRad(this.angle * -1)); ctx.scale(scaleXTo1, scaleYTo1);

Reemplazar a:

ctx.translate( ctxLeft, ctxTop ); ctx.scale(scaleXTo1, scaleYTo1); ctx.rotate(degToRad(this.angle * -1));

Vea esto: http://jsfiddle.net/ZxYCP/185/

Resultado adecuado:

ACTUALIZACIÓN 1:

He desarrollado la función de clip por polígono. http://jsfiddle.net/ZxYCP/198/


Actualizar a las respuestas anteriores de chicos.

ctx.rect( clipRect.oCoords.tl.x - this.oCoords.tl.x - clipRect.strokeWidth, clipRect.oCoords.tl.y - this.oCoords.tl.y - clipRect.strokeWidth, clipRect.oCoords.tr.x - clipRect.oCoords.tl.x, clipRect.oCoords.bl.y - clipRect.oCoords.tl.y );

Ahora podemos escalar el área de recorte sin lugar a dudas.


Como probé todos los violines arriba tienen un error. Es cuando se invierten los valores X e Y juntos, los límites de recorte serán incorrectos. Además, para no realizar todos los cálculos para colocar las imágenes en la posición correcta, debe especificar originX = ''center'' y originY = ''center'' para ellas.

Aquí hay una actualización de la función de recorte al código original de @natchiketa

var clipByName = function (ctx) { var clipRect = findByClipName(this.clipName); var scaleXTo1 = (1 / this.scaleX); var scaleYTo1 = (1 / this.scaleY); ctx.save(); ctx.translate(0,0); //logic for correct scaling if (this.getFlipY() && !this.getFlipX()){ ctx.scale(scaleXTo1, -scaleYTo1); } else if (this.getFlipX() && !this.getFlipY()){ ctx.scale(-scaleXTo1, scaleYTo1); } else if (this.getFlipX() && this.getFlipY()){ ctx.scale(-scaleXTo1, -scaleYTo1); } else { ctx.scale(scaleXTo1, scaleYTo1); } //IMPORTANT!!! do rotation after scaling ctx.rotate(degToRad(this.angle * -1)); ctx.beginPath(); ctx.rect( clipRect.left - this.left, clipRect.top - this.top, clipRect.width, clipRect.height ); ctx.closePath(); ctx.restore(); }

Por favor revise el fiddle actualizado


Con la última actualización en el tejido 1.6.0-rc.1, puede sesgar la imagen manteniendo presionada la tecla Mayús y arrastre el eje central.

Tengo problemas sobre cómo revertir la inclinación para que el área de recorte permanezca igual. He intentado seguir el código para intentar revertirlo de nuevo, pero no funcionó.

var skewXReverse = - this.skewX; var skewYReverse = - this.skewY; ctx.translate( ctxLeft, ctxTop ); ctx.scale(scaleXTo1, scaleYTo1); ctx.transform(1, skewXReverse, skewYReverse, 1, 0, 0); ctx.rotate(degToRad(this.angle * -1));

Demostración: http://jsfiddle.net/uimos/bntepzLL/5/


Esto se puede hacer mucho más fácilmente. Fabric proporciona método de render para recortar por otro contexto de objetos.

Echa un fiddle este fiddle . Vi esto en un comentario here .

obj.clipTo = function(ctx) { ctx.save(); ctx.setTransform(1, 0, 0, 1, 0, 0); clippingRect.render(ctx); ctx.restore(); };


Esto se puede lograr con Fabric utilizando la propiedad clipTo , pero tiene que "revertir" las transformaciones (escala y rotación), en la función clipTo .

Cuando utiliza la propiedad clipTo en Fabric, la escala y la rotación se aplican después del recorte, lo que significa que el recorte se escala y gira con la imagen. clipTo contrarrestar esto aplicando el reverso exacto de las transformaciones en la función de propiedad clipTo .

Mi solución consiste en tener un Fabric.Rect sirva como ''marcador de posición'' para la región del clip (esto tiene ventajas, ya que puede usar Fabric para mover el objeto y, por lo tanto, la región del clip).

Tenga en cuenta que mi solución utiliza la biblioteca de utilidades Lo-Dash , particularmente para _.bind() (ver código para el contexto).

Ejemplo de violín

Descompostura

1. Inicializar la tela

Primero queremos nuestro lienzo, por supuesto:

var canvas = new fabric.Canvas(''c'');

2. Región de Clip

var clipRect1 = new fabric.Rect({ originX: ''left'', originY: ''top'', left: 180, top: 10, width: 200, height: 200, fill: ''none'', stroke: ''black'', strokeWidth: 2, selectable: false });

Le damos a estos objetos de Rect una propiedad de nombre, clipFor , para que las funciones clipTo puedan encontrar aquella por la que desean ser recortados:

clipRect1.set({ clipFor: ''pug'' }); canvas.add(clipRect1);

No tiene que haber un objeto real para la región del clip, pero hace que sea más fácil de administrar, ya que puede moverlo utilizando Fabric.

3. Función de recorte

Definimos la función que será utilizada por las propiedades clipTo las imágenes por separado para evitar la duplicación de código:

Dado que la propiedad de angle del objeto Imagen se almacena en grados, usaremos esto para convertirla en radianes.

function degToRad(degrees) { return degrees * (Math.PI / 180); }

findByClipName() es una función de conveniencia, que utiliza Lo-Dash , para encontrar la propiedad clipFor para el objeto de imagen que se recortará (por ejemplo, en la imagen de abajo, el name será ''pug'' ):

function findByClipName(name) { return _(canvas.getObjects()).where({ clipFor: name }).first() }

Y esta es la parte que hace el trabajo:

var clipByName = function (ctx) { var clipRect = findByClipName(this.clipName); var scaleXTo1 = (1 / this.scaleX); var scaleYTo1 = (1 / this.scaleY); ctx.save(); ctx.translate(0,0); ctx.rotate(degToRad(this.angle * -1)); ctx.scale(scaleXTo1, scaleYTo1); ctx.beginPath(); ctx.rect( clipRect.left - this.left, clipRect.top - this.top, clipRect.width, clipRect.height ); ctx.closePath(); ctx.restore(); }

NOTA : Consulte más abajo para obtener una explicación del uso de this en la función anterior.

4. fabric.Image Objeto de imagen usando clipByName()

Finalmente, se puede crear una instancia de la imagen y usar la función clipByName esta manera:

var pugImg = new Image(); pugImg.onload = function (img) { var pug = new fabric.Image(pugImg, { angle: 45, width: 500, height: 500, left: 230, top: 170, scaleX: 0.3, scaleY: 0.3, clipName: ''pug'', clipTo: function(ctx) { return _.bind(clipByName, pug)(ctx) } }); canvas.add(pug); }; pugImg.src = ''http://fabricjs.com/lib/pug.jpg'';

¿Qué hace _.bind() ?

Tenga en cuenta que la referencia está envuelta en la función _.bind() .

Estoy usando _.bind() por las siguientes dos razones:

  1. Necesitamos pasar un objeto de Image referencia a clipByName()
  2. La propiedad clipTo pasa el contexto del lienzo, no el objeto.

Básicamente, _.bind() te permite crear una versión de la función que usa el objeto que especificas en this contexto.

Fuentes
  1. _.bind()
  2. http://fabricjs.com/docs/fabric.Object.html#clipTo
  3. http://html5.litten.com/understanding-save-and-restore-for-the-canvas-context/

He ajustado la solución por @natchiketa ya que la posición de la región del clip no se posicionó correctamente y todo fue torpe al girar. Pero todo parece estar bien ahora. Echa un vistazo a este fiddle modificado: http://jsfiddle.net/PromInc/ZxYCP/

Los únicos cambios reales se realizaron en la función clibByName del paso 3 del código proporcionado por @natchiketa. Esta es la función actualizada:

var clipByName = function (ctx) { this.setCoords(); var clipRect = findByClipName(this.clipName); var scaleXTo1 = (1 / this.scaleX); var scaleYTo1 = (1 / this.scaleY); ctx.save(); var ctxLeft = -( this.width / 2 ) + clipRect.strokeWidth; var ctxTop = -( this.height / 2 ) + clipRect.strokeWidth; var ctxWidth = clipRect.width - clipRect.strokeWidth + 1; var ctxHeight = clipRect.height - clipRect.strokeWidth + 1; ctx.translate( ctxLeft, ctxTop ); ctx.rotate(degToRad(this.angle * -1)); ctx.scale(scaleXTo1, scaleYTo1); ctx.beginPath(); ctx.rect( clipRect.left - this.oCoords.tl.x, clipRect.top - this.oCoords.tl.y, ctxWidth, ctxHeight ); ctx.closePath(); ctx.restore(); }

Dos capturas menores que encontré:

  1. Agregar un trazo al objeto de recorte parece despegar las cosas unos pocos píxeles. Intenté compensar el posicionamiento, pero luego de la rotación agregaría 2 píxeles a los lados inferior y derecho. Así que he optado por eliminarlo por completo.
  2. De vez en cuando, cuando gire la imagen, terminará con un espacio de 1 píxel en los lados aleatorios del recorte.