使用画布作为背景,使用遮罩覆盖元素

问题描述 投票:0回答:1

我有一个全屏背景画布,我想在该画布上绘制用作遮罩的元素。因此画布只能通过覆盖的元素可见。

这是我想要实现的目标的图片:

左图中是我目前拥有的,其中渐变背景充当画布元素。右图是我试图实现的目标,其中有 2 个 div(掩码,掩码 2)。此外,2 个元素(蒙版、蒙版 2)应该是可拖动的,因此如果元素移动,背景仍将与画布显示的内容匹配。

我尝试使用遮罩、剪辑路径来做到这一点,但我似乎无法让它工作。 获得所需结果的最佳方法是什么?

html css canvas clip-path css-mask
1个回答
0
投票

最简单的可能是使用 CSS 来屏蔽你的输出

<canvas>

您可以使用
mask
属性
,并为每个元素使用一个
linear-gradient
作为
mask-image
来执行此操作。

const canvas = document.querySelector("canvas");
const elements = [...document.querySelectorAll(".resizeable")];
// Set one mask image per element
const image = elements.map(() => `linear-gradient(black, black)`).join(",");
canvas.style.setProperty("-webkit-mask-image", image);
canvas.style.setProperty("mask-image", image);

const updateMask = () => {
  // Get the updated bounding rect of every elements
  const rects = elements.map((el) => el.getBoundingClientRect());
  const position = rects.map(({left, top}) => `${left}px ${top}px`).join(",");
  const size = rects.map(({width, height}) => `${width}px ${height}px`).join(",");
  // Update the mask properties
  canvas.style.setProperty("-webkit-mask-position", position);
  canvas.style.setProperty("mask-position", position);
  canvas.style.setProperty("-webkit-mask-size", size);
  canvas.style.setProperty("mask-size", size);
};

// Handle resizing of our elements
const observer = new ResizeObserver(() => updateMask());
elements.forEach((el) => observer.observe(el));
// Handle dragging of our elements
let dragged = null;
const offset = { x: 0, y: 0 };
document.addEventListener("mousedown", (evt) => {
  if (evt.target.matches(".resizeable")) {
    return;
  }
  dragged = evt.target.closest(".resizeable");
  offset.x = evt.offsetX;
  offset.y = evt.offsetY;
  if (dragged) {
    evt.preventDefault();
  }
});
document.addEventListener("mousemove", (evt) => {
  if (dragged) {
    dragged.style.setProperty("--left", evt.clientX - offset.x);
    dragged.style.setProperty("--top", evt.clientY - offset.y);
    updateMask();
  }
});
document.addEventListener("mouseup", (evt) => {
  dragged = null;
});
// Render some noise on the canvas
const ctx = canvas.getContext("2d");
const img = new ImageData(1000, 1000);
const data = new Uint32Array(img.data.buffer);
ctx.fillStyle = ctx.createLinearGradient(0, 0, 1000, 0);
ctx.fillStyle.addColorStop(0, "orange");
ctx.fillStyle.addColorStop(0.2, "blue");
ctx.fillStyle.addColorStop(0.4, "green");
ctx.fillStyle.addColorStop(0.6, "orange");
ctx.fillStyle.addColorStop(0.8, "#F0F");
ctx.globalCompositeOperation = "color";
const anim = () => {
  for (let i = 0; i<data.length; i++) {
    data[i] = (Math.random() * 0xFFFFFF) + 0xFF000000;
  }
  ctx.putImageData(img, 0, 0);
  ctx.fillRect(0, 0, 1000, 1000);
  requestAnimationFrame(anim);
};
requestAnimationFrame(anim);
.resizeable {
  position: absolute;
  border: 1px solid;
  overflow: hidden;
  resize: both;
  width: 300px;
  height: 250px;
  left: calc(var(--left) * 1px);
  top: calc(var(--top) * 1px);
}
.resizeable:nth-of-type(2) {
  --left: 350;
  --top: 120;
}
.draggable {
  width: 100%;
  height: 100%;
}
canvas {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  /* Do not forget to disable the mask-repeat */
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}
<canvas width=1000 height=1000></canvas>

<div class=resizeable><div class=draggable>foo bar</div></div>
<div class=resizeable><div class=draggable>baz bla</div></div>

© www.soinside.com 2019 - 2024. All rights reserved.