Manipulación de imágenes: agregue imágenes con esquinas en posiciones exactas

Tengo una imagen que es un fondo que contiene un área en caja como esta:

Trapecio distorsionado

Conozco las posiciones exactas de las esquinas de esa forma, y ​​me gustaría colocar otra imagen dentro de ella. (Por lo que parece estar dentro de la caja).

Soy consciente del método drawImage para el canvas HTML5, pero parece que solo admite los parámetros x, y, width, height en lugar de las coordenadas exactas. ¿Cómo podría dibujar una imagen en un canvas en un conjunto específico de coordenadas, y lo ideal sería que el propio navegador maneje la imagen?

3 Solutions collect form web for “Manipulación de imágenes: agregue imágenes con esquinas en posiciones exactas”

Transformada cuadrilátera

Una forma de hacerlo es usar transformaciones cuadriláteras. Son diferentes a las transformaciones 3D y le permitirían dibujar en un canvas en caso de que quiera exportar el resultado.

El ejemplo que se muestra aquí es simplificado y utiliza la subdivisión básica y los “trucos” en el renderizado en sí, es decir, dibuja en un pequeño cuadrado en lugar de la forma de la celda subdividida, pero debido al pequeño tamaño y la superposición, Puede salirse con la suya en muchos casos no extremos.

La forma adecuada sería dividir la forma en dos triangularjs, luego escanear los píxeles en el bitmap de destino, asignar el punto desde el triángulo de destino al triángulo de origen. Si el valor de posición fuera fraccional, lo usaría para determinar la interpolación de píxeles (por ejemplo, 2×2 bi-lineal o 4×4 bi-cúbico).

No tengo la intención de cubrir todo esto en esta respuesta ya que quedaría rápidamente fuera del scope del formato SO, pero el método probablemente sería adecuado en este caso a menos que necesite animarlo (no es lo suficientemente eficaz para eso si quiero alta resolución).

Método

Vamos a empezar con una forma de cuadrilátero inicial:

Quad inicial

El primer paso es interpolar las posiciones Y en cada barra C1-C4 y C2-C3. Vamos a necesitar la posición actual, así como la siguiente posición. Usaremos interpolación lineal (“lerp”) para esto usando un valor normalizado para t :

 y1current = lerp( C1, C4, y / height) y2current = lerp( C2, C3, y / height) y1next = lerp(C1, C4, (y + step) / height) y2next = lerp(C2, C3, (y + step) / height) 

Esto nos da una nueva línea entre y a lo largo de las barras verticales exteriores.

http://i.imgur.com/qAWUK4B.png

A continuación necesitamos las posiciones X en esa línea, tanto la actual como la siguiente. Esto nos dará cuatro posiciones que llenaremos con el píxel actual, ya sea como está o interpolarlo (no se muestra aquí):

 p1 = lerp(y1current, y2current, x / width) p2 = lerp(y1current, y2current, (x + step) / width) p3 = lerp(y1next, y2next, (x + step) / width) p4 = lerp(y1next, y2next, x / width) 

x e y serán la posición en la imagen de origen utilizando valores enteros.

Iterando x / y

Podemos usar esta configuración dentro de un bucle que iterará sobre cada píxel en el bitmap de origen.

Manifestación

La demostración se puede encontrar en la parte inferior de la respuesta. Mueva los mangos circulares para transformar y juegue con el valor del paso para ver su impacto en el rendimiento y el resultado.

La demostración tendrá moire y otros artefactos, pero como se mencionó anteriormente, eso sería un tema para otro día.

Instantánea de la demo:

Instantánea demo

Metodos alternativos

También puede usar WebGL o Three.js para configurar un entorno 3D y renderizar en canvas. Aquí hay un enlace a la última solución:

  • Tres.js

y un ejemplo de cómo usar la superficie mapeada de textura:

  • Texturizado de Three.js (en lugar de definir un cubo, simplemente defina un lugar / cara).

El uso de este enfoque le permitirá exportar el resultado a un canvas o una imagen también, pero para el rendimiento se requiere una GPU en el cliente.

Si no necesita exportar o manipular el resultado, sugeriría usar la transformación simple 3D de CSS como se muestra en las otras respuestas.

 /* Quadrilateral Transform - (c) Ken Nilsen, CC3.0-Attr */ var img = new Image(); img.onload = go; img.src = "https://i.imgur.com/EWoZkZm.jpg"; function go() { var me = this, stepEl = document.querySelector("input"), stepTxt = document.querySelector("span"), c = document.querySelector("canvas"), ctx = c.getContext("2d"), corners = [ {x: 100, y: 20}, // ul {x: 520, y: 20}, // ur {x: 520, y: 380}, // br {x: 100, y: 380} // bl ], radius = 10, cPoint, timer, // for mouse handling step = 4; // resolution update(); // render image to quad using current settings function render() { var p1, p2, p3, p4, y1c, y2c, y1n, y2n, w = img.width - 1, // -1 to give room for the "next" points h = img.height - 1; ctx.clearRect(0, 0, c.width, c.height); for(y = 0; y < h; y += step) { for(x = 0; x < w; x += step) { y1c = lerp(corners[0], corners[3], y / h); y2c = lerp(corners[1], corners[2], y / h); y1n = lerp(corners[0], corners[3], (y + step) / h); y2n = lerp(corners[1], corners[2], (y + step) / h); // corners of the new sub-divided cell p1 (ul) -> p2 (ur) -> p3 (br) -> p4 (bl) p1 = lerp(y1c, y2c, x / w); p2 = lerp(y1c, y2c, (x + step) / w); p3 = lerp(y1n, y2n, (x + step) / w); p4 = lerp(y1n, y2n, x / w); ctx.drawImage(img, x, y, step, step, p1.x, p1.y, // get most coverage for w/h: Math.ceil(Math.max(step, Math.abs(p2.x - p1.x), Math.abs(p4.x - p3.x))) + 1, Math.ceil(Math.max(step, Math.abs(p1.y - p4.y), Math.abs(p2.y - p3.y))) + 1) } } } function lerp(p1, p2, t) { return { x: p1.x + (p2.x - p1.x) * t, y: p1.y + (p2.y - p1.y) * t} } /* Stuff for demo: -----------------*/ function drawCorners() { ctx.strokeStyle = "#09f"; ctx.lineWidth = 2; ctx.beginPath(); // border for(var i = 0, p; p = corners[i++];) ctx[i ? "lineTo" : "moveTo"](px, py); ctx.closePath(); // circular handles for(i = 0; p = corners[i++];) { ctx.moveTo(px + radius, py); ctx.arc(px, py, radius, 0, 6.28); } ctx.stroke() } function getXY(e) { var r = c.getBoundingClientRect(); return {x: e.clientX - r.left, y: e.clientY - r.top} } function inCircle(p, pos) { var dx = pos.x - px, dy = pos.y - py; return dx*dx + dy*dy < = radius * radius } // handle mouse c.onmousedown = function(e) { var pos = getXY(e); for(var i = 0, p; p = corners[i++];) {if (inCircle(p, pos)) {cPoint = p; break}} } window.onmousemove = function(e) { if (cPoint) { var pos = getXY(e); cPoint.x = pos.x; cPoint.y = pos.y; cancelAnimationFrame(timer); timer = requestAnimationFrame(update.bind(me)) } } window.onmouseup = function() {cPoint = null} stepEl.oninput = function() { stepTxt.innerHTML = (step = Math.pow(2, +this.value)); update(); } function update() {render(); drawCorners()} } 
 body {margin:20px;font:16px sans-serif} canvas {border:1px solid #000;margin-top:10px} 
 4

Puedes usar Transformaciones CSS para hacer que tu imagen se vea como esa caja. Por ejemplo:

 img { margin: 50px; transform: perspective(500px) rotateY(20deg) rotateX(20deg); } 
  

Esta solución se basa en el navegador que realiza la composición. Coloca la imagen que desea distorsionada en un elemento separado, superponiendo el fondo usando position: absolute .

Luego use la propiedad de transform CSS para aplicar cualquier transformación de perspectiva al elemento de superposición.

Para encontrar la matriz de transformación, puede utilizar la respuesta de: Cómo hacer coincidir la perspectiva 3D de la foto real y el objeto en CSS3 Transformaciones 3D

  • ¿Cómo hacer que el map RPG se extienda a medida que el jugador alcanza el límite? Javascript HTML5
  • Paquete de tamaño para latencia baja websocket html 5 juegos
  • Cómo cancelar popState en ciertas condiciones
  • HTML5 File API bloquea Chrome al usar readAsDataURL para cargar una image seleccionada
  • recorte de image html5
  • usando html5 para upload files con ajax y jquery
  • ¿Cómo puedo implementar la function del borrador en SVG?
  • Cadenas y numbers de conversión de tipo de atributo de datos HTML5 *.
  • Javascript tiene muchos buenos JS marco (como Node.js AngularJS Vue.js React.js) es el mejor lenguaje de script.