2017-04-11 56 views
0

我有一个想法,我试图用Javascript和HTML5画布编码,但我不知道从哪里开始。如何在两个多边形之间的交集中显示图像?

假设你有多个不同颜色的多边形(假设为矩形,但现在最好的伪随机不规则多边形)。您可以通过单击和拖动来移动多边形。

当你拖动多边形对另一多边形的一个,我想在相交区域显示图像。想象一下,将蓝色多边形拖到红色多边形上以创建一个紫色区域,除了紫色是豹纹图案或照片或类似物。

任何帮助,将不胜感激!谢谢!

+0

寻找对象之间的交集是一个不平凡的任务 – Dummy

回答

1

使用2d上下文剪辑功能。正常绘制形状然后再次绘制它们,而不是在每个形状之后填充使用剪辑。

将每个剪辑应用于上一个剪辑。

当所有片段的形状已经设置然后画在上面的图像/形状,只有部分剪辑区域内会显示。

要删除剪辑,您需要使用保存和恢复(请参阅演示);

使用我刚刚写的另一个例子的代码,有点懒。

示例显示使用2D上下文的剪辑功能将3个框联合为蓝色。

/** SimpleUpdate.js begin **/ 
 
// short cut vars 
 
var ctx = canvas.getContext("2d"); 
 
var w = canvas.width; 
 
var h = canvas.height; 
 
ctx.font = "18px arial"; 
 
var cw = w/2; // center 
 
var ch = h/2; 
 
var angle = 0; 
 
var focused = false; 
 
var rotated = false; 
 

 
// Handle all key input 
 
const keys = { // key input object 
 
    ArrowLeft : false, // only add key names you want to listen to 
 
    ArrowRight : false, 
 
    keyEvent (event) { 
 
     if (keys[event.code] !== undefined) { // are we interested in this key 
 
      keys[event.code] = event.type === "keydown"; 
 
      rotated = true; // to turn off help 
 
     } 
 
    } 
 
} 
 
// add key listeners 
 
document.addEventListener("keydown", keys.keyEvent) 
 
document.addEventListener("keyup", keys.keyEvent) 
 

 
// check if focus click 
 
canvas.addEventListener("click",()=>focused = true); 
 

 
// main update function 
 
function update (timer) { 
 
    ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform 
 
    ctx.clearRect(0,0,w,h);  
 
    
 
    // draw outside box 
 
    ctx.fillStyle = "red" 
 
    ctx.fillRect(50, 50, w - 100, h - 100); 
 
    
 
    // rotate if input 
 
    angle += keys.ArrowLeft ? -0.1 : 0; 
 
    angle += keys.ArrowRight ? 0.1 : 0; 
 
    
 
    // set orgin to center of canvas 
 
    ctx.setTransform(1, 0, 0, 1, cw, ch); 
 
    
 
    // rotate 
 
    ctx.rotate(angle); 
 
    
 
    // draw rotated box 
 
    ctx.fillStyle = "Black" 
 
    ctx.fillRect(-50, -50, 100, 100); 
 
    
 
    // set transform to center 
 
    ctx.setTransform(1, 0, 0, 1, cw, ch); 
 
    // rotate 
 
    ctx.rotate(angle); 
 
    // move to corner 
 
    ctx.translate(50,50); 
 
    // rotate once more, Doubles the rotation 
 
    ctx.rotate(angle); 
 
    
 
    ctx.fillStyle = "yellow" 
 
    ctx.fillRect(-40, -40,80, 80); 
 

 
    ctx.setTransform(1, 0, 0, 1, 0, 0); // restore default 
 
    
 

 

 

 
    // set up the clip area 
 
    ctx.save(); // save the non cliped canvas state 
 
    ctx.beginPath(); 
 
    ctx.rect(50, 50, w - 100, h - 100); 
 
    ctx.clip(); // clip main box 
 

 
    // set orgin to center of canvas 
 
    ctx.setTransform(1, 0, 0, 1, cw, ch);  
 
    ctx.rotate(angle); 
 
    ctx.beginPath(); 
 
    ctx.rect(-50, -50, 100, 100); 
 
    ctx.clip(); // add to clip (reduces area 
 

 
    ctx.setTransform(1, 0, 0, 1, cw, ch); 
 
    ctx.rotate(angle); 
 
    ctx.translate(50,50); 
 
    ctx.rotate(angle); 
 
    ctx.beginPath(); 
 
    ctx.rect(-40, -40,80, 80);  
 
    ctx.clip(); 
 

 
    ctx.setTransform(1, 0, 0, 1, 0, 0); // restore default 
 
    
 
    ctx.fillStyle = "blue" 
 
    ctx.fillRect(0, 0, w, h); 
 
    
 
    ctx.restore(); // this removes the clip. It is the only way to remove it 
 
        // apart from reseting the context 
 
    
 
    
 
    
 
    ctx.fillStyle = "white" 
 
    ctx.lineWidth = 3; 
 
    if(!focused){ 
 
     ctx.strokeText("Click on canvas to get focus.",10,20); 
 
     ctx.fillText("Click on canvas to get focus.",10,20); 
 
    }else if(!rotated){ 
 
     ctx.strokeText("Left right arrow to rotate.",10,20); 
 
     ctx.fillText("Left right arrow to rotate.",10,20); 
 
    }else{ 
 
     ctx.strokeText("Blue is the union of the...",10,20); 
 
     ctx.fillText("Blue is the union of the...",10,20); 
 
     ctx.strokeText("...yellow, black, and red boxes.",10,h-5); 
 
     ctx.fillText("...yellow, black, and red boxes.",10,h-5); 
 
    } 
 

 
    requestAnimationFrame(update); 
 

 
} 
 
requestAnimationFrame(update); 
 

 

 
/** SimpleUpdate.js end **/
<canvas id = canvas></canvas>

+0

或者用合成:HTTPS: //jsfiddle.net/83Lj1b34/ – Kaiido

0

对于那些不喜欢削波(像我一样)中的那些,你也可以合成实现它。

基本上,你需要绘制形状两次:一次是在可视的画布,一旦上隐藏的一个,仅用于创建交叉区域。

绘制与正常合成模式'source-over'第一形状。
然后,使用合成模式'source-in'绘制所有形状。这将只保留与先前绘制的像素重叠的新像素。

当您完成绘制你的形状,只有交叉部分应保持这个隐藏的画布。所以仍然在这个合成模式下,你可以绘制你的图像,画布的大小。您的图片将被裁剪。

因为我也很懒,所以我无耻地拿着Blindman67的代码*,甚至没有打算用更优雅的方式重写它以避免样板化,而应该可以完成它。

/** SimpleUpdate.js begin **/ 
 
// short cut vars 
 
var ctx = canvas.getContext("2d"); 
 
// create an hidden canvas' context 
 
var ctx1 = canvas.cloneNode().getContext('2d'); 
 
var w = canvas.width; 
 
var h = canvas.height; 
 
ctx.font = "18px arial"; 
 
var cw = w/2; // center 
 
var ch = h/2; 
 
var angle = 0; 
 
var focused = false; 
 
var rotated = false; 
 
var img = new Image(); 
 
img.src = 'https://upload.wikimedia.org/wikipedia/commons/thumb/5/55/John_William_Waterhouse_A_Mermaid.jpg/100px-John_William_Waterhouse_A_Mermaid.jpg'; 
 

 

 
// Handle all key input 
 
const keys = { // key input object 
 
    ArrowLeft: false, // only add key names you want to listen to 
 
    ArrowRight: false, 
 
    keyEvent(event) { 
 
    if (keys[event.code] !== undefined) { // are we interested in this key 
 
     keys[event.code] = event.type === "keydown"; 
 
     rotated = true; // to turn off help 
 
    } 
 
    } 
 
} 
 
// add key listeners 
 
document.addEventListener("keydown", keys.keyEvent) 
 
document.addEventListener("keyup", keys.keyEvent) 
 

 
// check if focus click 
 
canvas.addEventListener("click",() => focused = true); 
 

 
// main update function 
 
function update(timer) { 
 
    ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform 
 
    ctx1.setTransform(1, 0, 0, 1, 0, 0); 
 
    ctx.clearRect(0, 0, w, h); 
 
    ctx1.clearRect(0, 0, w, h); 
 
    // draw outside box 
 
    ctx.fillStyle = "red" 
 
    ctx.fillRect(50, 50, w - 100, h - 100); 
 
    // reset default compositing mode 
 
    ctx1.globalCompositeOperation = 'source-over'; 
 
    // and draw the first shape 
 
    ctx1.fillRect(50, 50, w - 100, h - 100); 
 
    // now we will keep only the new pixels which overlap with existing ones 
 
    ctx1.globalCompositeOperation = 'source-in'; 
 
    // rotate if input 
 
    angle += keys.ArrowLeft ? -0.1 : 0; 
 
    angle += keys.ArrowRight ? 0.1 : 0; 
 

 
    // set origin to center of canvas 
 
    ctx.setTransform(1, 0, 0, 1, cw, ch); 
 
    // apply all the same transforms to the hidden canvas 
 
    ctx1.setTransform(1, 0, 0, 1, cw, ch); 
 
    // rotate 
 
    ctx.rotate(angle); 
 
    ctx1.rotate(angle); 
 
    // draw rotated box 
 
    ctx.fillStyle = "Black" 
 
    ctx.fillRect(-50, -50, 100, 100); 
 
    ctx1.fillRect(-50, -50, 100, 100); 
 

 
    // set transform to center 
 
    ctx.setTransform(1, 0, 0, 1, cw, ch); 
 
    ctx1.setTransform(1, 0, 0, 1, cw, ch); 
 
    // rotate 
 
    ctx.rotate(angle); 
 
    ctx1.rotate(angle); 
 
    // move to corner 
 
    ctx.translate(50, 50); 
 
    ctx1.translate(50, 50); 
 
    // rotate once more, Doubles the rotation 
 
    ctx.rotate(angle); 
 
    ctx1.rotate(angle); 
 

 
    ctx.fillStyle = "yellow" 
 
    ctx.fillRect(-40, -40, 80, 80); 
 
    ctx1.fillRect(-40, -40, 80, 80); 
 

 
    ctx.setTransform(1, 0, 0, 1, 0, 0); // restore default 
 
    ctx1.setTransform(1, 0, 0, 1, 0, 0); 
 

 
    // our hidden canvas only contains the overlapping of all our shapes 
 
    // we can draw our image on this intersection area 
 
    ctx1.drawImage(img, 0, 0, w, h); 
 
    // and draw this back on the main canvas 
 
    ctx.drawImage(ctx1.canvas, 0, 0); 
 

 
    ctx.fillStyle = "white" 
 
    ctx.lineWidth = 3; 
 
    if (!focused) { 
 
    ctx.strokeText("Click on canvas to get focus.", 10, 20); 
 
    ctx.fillText("Click on canvas to get focus.", 10, 20); 
 
    } else if (!rotated) { 
 
    ctx.strokeText("Left right arrow to rotate.", 10, 20); 
 
    ctx.fillText("Left right arrow to rotate.", 10, 20); 
 
    } else { 
 
    ctx.strokeText("Image is the union of the...", 10, 20); 
 
    ctx.fillText("Image is the union of the...", 10, 20); 
 
    ctx.strokeText("...yellow, black, and red boxes.", 10, h - 5); 
 
    ctx.fillText("...yellow, black, and red boxes.", 10, h - 5); 
 
    } 
 

 
    requestAnimationFrame(update); 
 

 
} 
 
requestAnimationFrame(update); 
 

 

 
/** SimpleUpdate.js end **/
<canvas id="canvas"></canvas>

*我希望他不会介意,如果他这样做,他只是让我知道。

1

这对SVG来说有点简单。:)

<svg viewBox="0 0 800 500"> 
 
    <defs> 
 
    <circle id="left" cx="250" cy="250" r="250"/> 
 
    <circle id="right" cx="550" cy="250" r="250"/> 
 
    <mask id="intersect"> 
 
     <rect width="100%" height="100%" fill="black"/> 
 
     <use xlink:href="#right" fill="white" mask="url(#maskleft)"/> 
 
    </mask> 
 
    <mask id="maskleft"> 
 
     <rect width="100%" height="100%" fill="black"/> 
 
     <use xlink:href="#left" fill="white"/> 
 
    </mask> 
 
    </defs> 
 

 
    <use xlink:href="#left" fill="red"/> 
 
    <use xlink:href="#right" fill="blue"/> 
 
    <image xlink:href="http://lorempixel.com/output/animals-q-c-500-500-8.jpg" 
 
     x="280" y="0" width="500" height="500" mask="url(#intersect)"/> 
 
</svg>

动画版

var start = null; 
 
var maskleftcircle = document.getElementById("maskleftcircle"); 
 
var puppygroup = document.getElementById("puppygroup"); 
 

 
function step(timestamp) { 
 
    if (!start) start = timestamp; 
 
    var angle = ((timestamp - start)/250) % 360; 
 

 
    var dx = 100 * Math.cos(angle); 
 
    var dy = -100 * Math.sin(angle); 
 

 
    puppygroup.setAttribute("transform", "translate("+dx+","+dy+")"); 
 
    maskleftcircle.setAttribute("transform", "translate("+(-dx)+","+(-dy)+")"); 
 

 
    window.requestAnimationFrame(step); 
 
} 
 

 
window.requestAnimationFrame(step);
<svg viewBox="0 0 800 500"> 
 
    <defs> 
 
    <circle id="left" cx="250" cy="250" r="250"/> 
 
    <circle id="right" cx="550" cy="250" r="250"/> 
 
    <mask id="intersect"> 
 
     <rect width="100%" height="100%" fill="black"/> 
 
     <use xlink:href="#right" fill="white" mask="url(#maskleft)"/> 
 
    </mask> 
 
    <mask id="maskleft"> 
 
     <rect width="100%" height="100%" fill="black"/> 
 
     <use xlink:href="#left" fill="white" id="maskleftcircle"/> 
 
    </mask> 
 
    </defs> 
 

 
    <use xlink:href="#left" fill="red"/> 
 
    <g id="puppygroup"> 
 
    <use xlink:href="#right" fill="blue"/> 
 
    <image xlink:href="http://lorempixel.com/output/animals-q-c-500-500-8.jpg" 
 
      x="300" y="0" width="500" height="500" mask="url(#intersect)"/> 
 
    </g> 
 
</svg>

+0

除了当你需要使所有这些形状动态地移动/出现/消失时;-) – Kaiido

+0

接受的挑战:) –

+0

我没有说不可能;-)只是没有从你的代码片段看起来那么简单。 Canvas代码也会更简单,与您的结果完全相同。 (Ps:没有libs允许这个挑战:-D) – Kaiido