Me gustaría dibujar un rectángulo de selección (arrastrando y soltando con el mouse, como con MS Paint) en un lienzo HTML.
El siguiente fragmento funciona, pero con dos lienzos diferentes.
Pregunta: ¿Cómo hacer esto en un solo lienzo, de modo que la foto de la imagen del lienzo se pueda cambiar más tarde sin eliminar el rectángulo de selección?
¿Deberíamos mantener 2 lienzos diferentes (si es así, cómo colocar el n. ° 2 exactamente encima del primero, sin posicionamiento absolute para ambos)?
¿O es posible de una forma más sencilla con un solo lienzo? (y algo como ctx.globalCompositeOperation = "source-over"; o un método similar)
var c1 = document.getElementById("c1"), c2 = document.getElementById("c2"); var ctx1 = c1.getContext("2d"), ctx2 = c2.getContext("2d"); ctx2.setLineDash([5, 5]); var origin = null; window.onload = () => { ctx1.drawImage(document.getElementById("img"), 0, 0); } c1.onmousedown = e => { origin = {x: e.offsetX, y: e.offsetY}; }; window.onmouseup = e => { origin = null; }; c1.onmousemove = e => { if (!!origin) { ctx2.strokeStyle = "#ff0000"; ctx2.clearRect(0, 0, c2.width, c2.height); ctx2.beginPath(); ctx2.rect(origin.x, origin.y, e.offsetX - origin.x, e.offsetY - origin.y); ctx2.stroke(); } }; #img { display: none; } #c1 { border: 1px solid green; } #c2 { border: 1px solid blue; } <img id="img" src="https://i.imgur.com/okSIKkW.jpg"> <canvas id="c1" height="200" width="200"></canvas> <canvas id="c2" height="200" width="200"></canvas>Puede haber una solución mejor con un solo <canvas> y varias capas (si es así, no dude en publicar otra respuesta), pero finalmente usé un método similar a html5 - elemento de lienzo - Capas múltiples con una superposición de 2 lienzos:
var c1 = document.getElementById("c1"), c2 = document.getElementById("c2"); var ctx1 = c1.getContext("2d"), ctx2 = c2.getContext("2d"); ctx2.setLineDash([5, 5]); var origin = null; window.onload = () => { ctx1.drawImage(document.getElementById("img"), 0, 0); } c2.onmousedown = e => { origin = {x: e.offsetX, y: e.offsetY}; }; window.onmouseup = e => { origin = null; }; c2.onmousemove = e => { if (!!origin) { ctx2.strokeStyle = "#ff0000"; ctx2.clearRect(0, 0, c2.width, c2.height); ctx2.beginPath(); ctx2.rect(origin.x, origin.y, e.offsetX - origin.x, e.offsetY - origin.y); ctx2.stroke(); } }; #img { display: none; } #canvas-container { position: relative; } canvas { position: absolute; left: 0; top: 0; } #c1 { z-index: 0; } #c2 { z-index: 1; } <img id="img" src="https://i.imgur.com/okSIKkW.jpg"> <div id="canvas-container"> <canvas id="c1" height="200" width="200"></canvas> <canvas id="c2" height="200" width="200"></canvas> </div>La siguiente solución utiliza un solo lienzo y se vuelve a renderizar con el movimiento del mouse.
https://jsfiddle.net/uhtdv0mj/3/
const c1 = document.getElementById("c1"); const ctx1 = c1.getContext("2d"); const drawImage = () => { ctx1.drawImage(document.getElementById("img"), 0, 0); }; const drawSelection = (e) => { ctx1.strokeStyle = "#000"; ctx1.beginPath(); ctx1.rect(origin.x, origin.y, e.offsetX - origin.x, e.offsetY - origin.y); ctx1.stroke(); }; const clear = () => { ctx1.clearRect(0, 0, c1.width, c1.height); }; const render = (e) => { clear(); drawImage(); if (origin) drawSelection(e); }; window.onload = drawImage; let origin = null; c1.onmousedown = e => { origin = {x: e.offsetX, y: e.offsetY}; }; c1.onmouseup = e => { origin = null; render(e); }; c1.onmousemove = render;