如何在两个多边形的交叉点显示图像?

时间:2017-04-11 20:08:58

标签: javascript html5 canvas svg html5-canvas

我有一个想法,我正在尝试使用Javascript和html5 Canvas进行编码,但我不确定从哪里开始。

假设您有多个不同颜色的多边形(现在假设为矩形,但理想情况下是伪随机不规则多边形)。您可以通过单击并拖动来移动多边形。

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

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

3 个答案:

答案 0 :(得分: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>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

SVG有点简单。 :)

&#13;
&#13;
<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>
&#13;
&#13;
&#13;

动画版

&#13;
&#13;
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);
&#13;
<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>
&#13;
&#13;
&#13;

答案 2 :(得分:0)

对于那些不喜欢剪辑的人(比如我),你也可以通过合成实现它。

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

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

当您完成绘制形状时,只有交叉部分应保留在此隐藏的画布上。因此,使用此合成模式,您可以绘制图像,画布的大小。您的图片将被剪裁。

由于我也很懒,我无耻地接受了Blindman67的代码*,并且甚至不打算以更优雅的方式重写它以避免样板,而它应该可以完成。

&#13;
&#13;
/** 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 **/
&#13;
<canvas id="canvas"></canvas>
&#13;
&#13;
&#13;

*我希望他不会介意,如果他这样做,他只需告诉我。