canvas使用更多ctx元素

发布于 2025-01-13 12:52:10 字数 1985 浏览 1 评论 0原文

我的任务是创建两个画布(大和小)。在大一颗中创建 5 颗不同颜色的星星。

任务:通过单击星星,将小画布的颜色更改为星星的颜色。现在的问题是 addEventListener 仅适用于最后一个元素。有什么想法吗?

.html 模板:

<canvas style="background-color:rgb(255, 255, 255); border: 1px solid black" id='big'>Обновите браузер</canvas>
<canvas style="background-color:rgb(255, 255, 255); border: 1px solid black" id='small'>Обновите браузер</canvas>

.js 脚本:

const big = document.getElementById("big");
const small = document.getElementById("small");

big.height = 600;
big.width  = 600;
small.height = 600;
small.width = 50;

function createStar(moveToX, moveToY, lineToX1, lineToY1, lineToX2, lineToY2, lineToX3, lineToY3, lineToX4, lineToY4, color) {
            ctx = big.getContext('2d');
            ctx.beginPath();
            ctx.moveTo(moveToX, moveToY);
            ctx.lineTo(lineToX1, lineToY1);
            ctx.lineTo(lineToX2, lineToY2);
            ctx.lineTo(lineToX3, lineToY3);
            ctx.lineTo(lineToX4, lineToY4);
            ctx.closePath();
            ctx.strokeStyle = color;
            ctx.fillStyle = color;
            ctx.fill();
            ctx.stroke();
};

const red = new createStar(20, 60, 100, 60, 35, 110, 60, 25, 85, 110, 'red');
const blue = new createStar(120, 60, 200, 60, 135, 110, 160, 25, 185, 110, 'blue');
const green = new createStar(120, 160, 200, 160, 135, 210, 160, 125, 185, 210, 'green');
const black = new createStar(220, 460, 400, 460, 235, 560, 300, 400, 385, 560, 'black');
const yellow = new createStar(220, 260, 300, 260, 235, 310, 260, 225, 285, 310, 'yellow');

big.addEventListener('click', function(e){
            if(ctx.isPointInPath(e.offsetX, e.offsetY)) {
                small.style.backgroundColor = 'red';
            } else {
                small.style.backgroundColor = 'white';
            }
});

My task is to create two canvases (big and small). In big one create 5 stars of different colors.

Task: by clicking on the star, change the color of the small canvas to the color of the star. Now the problem is that addEventListener only works on the last element. Any ideas?

.html template:

<canvas style="background-color:rgb(255, 255, 255); border: 1px solid black" id='big'>Обновите браузер</canvas>
<canvas style="background-color:rgb(255, 255, 255); border: 1px solid black" id='small'>Обновите браузер</canvas>

.js script:

const big = document.getElementById("big");
const small = document.getElementById("small");

big.height = 600;
big.width  = 600;
small.height = 600;
small.width = 50;

function createStar(moveToX, moveToY, lineToX1, lineToY1, lineToX2, lineToY2, lineToX3, lineToY3, lineToX4, lineToY4, color) {
            ctx = big.getContext('2d');
            ctx.beginPath();
            ctx.moveTo(moveToX, moveToY);
            ctx.lineTo(lineToX1, lineToY1);
            ctx.lineTo(lineToX2, lineToY2);
            ctx.lineTo(lineToX3, lineToY3);
            ctx.lineTo(lineToX4, lineToY4);
            ctx.closePath();
            ctx.strokeStyle = color;
            ctx.fillStyle = color;
            ctx.fill();
            ctx.stroke();
};

const red = new createStar(20, 60, 100, 60, 35, 110, 60, 25, 85, 110, 'red');
const blue = new createStar(120, 60, 200, 60, 135, 110, 160, 25, 185, 110, 'blue');
const green = new createStar(120, 160, 200, 160, 135, 210, 160, 125, 185, 210, 'green');
const black = new createStar(220, 460, 400, 460, 235, 560, 300, 400, 385, 560, 'black');
const yellow = new createStar(220, 260, 300, 260, 235, 310, 260, 225, 285, 310, 'yellow');

big.addEventListener('click', function(e){
            if(ctx.isPointInPath(e.offsetX, e.offsetY)) {
                small.style.backgroundColor = 'red';
            } else {
                small.style.backgroundColor = 'white';
            }
});

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

context.isPointInPath(x, y)将检查坐标 xy 处的点是否位于上下文中正在跟踪的当前子路径内。

每次调用 ctx.beginPath() 时,该子路径都会被清除。

要解决此问题,您必须重新追踪整个星星列表,并逐一检查该点是否落在其中。
不过,您还可以使用 Path2D 接口

该接口允许将此类子路径保存为自己的对象,并且可以由上下文使用,而不是内部上下文的路径,例如 ctx.fill(path) 、 ctx.lines(路径),ctx.clip(路径)ctx.scrollPathIntoView(路径)ctx.drawFocusIfNeeded(路径,元素) , 和您可能已经猜到了,ctx.isPointInPath(path, x, y)ctx.isPointInStroke(path, x, y)

因此,您可以重写代码,以便每颗星星都存储在其自己的 Path2D 实例中,将所有这些实例存储在一个数组中,然后遍历该数组以检查其中是否有任何一个被该点击中:

const stars = [];
const big = document.getElementById("big");
const small = document.getElementById("small");

big.height = 600;
big.width = 500;
small.height = 600;
small.width = 50;

const ctx = big.getContext("2d");

function createStar(moveToX, moveToY, lineToX1, lineToY1, lineToX2, lineToY2, lineToX3, lineToY3, lineToX4, lineToY4, color) {
  const path = new Path2D();
  // store our path in an object also hodling the color
  stars.push({path, color});
  path.moveTo(moveToX, moveToY);
  path.lineTo(lineToX1, lineToY1);
  path.lineTo(lineToX2, lineToY2);
  path.lineTo(lineToX3, lineToY3);
  path.lineTo(lineToX4, lineToY4);
  ctx.strokeStyle = color;
  ctx.fillStyle = color;
  ctx.fill(path);
  ctx.stroke(path);
};

const red = new createStar(20, 60, 100, 60, 35, 110, 60, 25, 85, 110, 'red');
const blue = new createStar(120, 60, 200, 60, 135, 110, 160, 25, 185, 110, 'blue');
const green = new createStar(120, 160, 200, 160, 135, 210, 160, 125, 185, 210, 'green');
const black = new createStar(220, 460, 400, 460, 235, 560, 300, 400, 385, 560, 'black');
const yellow = new createStar(220, 260, 300, 260, 235, 310, 260, 225, 285, 310, 'yellow');

big.addEventListener('click', function(e) {
  // find the first star that got clicked
  const touched = stars.find((star) =>
    ctx.isPointInPath(star.path, e.offsetX, e.offsetY)
  );
  if (touched) {
    small.style.backgroundColor = touched.color;
  } else {
    small.style.backgroundColor = 'white';
  }
});
canvas {
  background-color: rgb(255, 255, 255);
  border: 1px solid black;
}
<canvas id='big'>Обновите браузер</canvas>
<canvas id='small'>Обновите браузер</canvas>

context.isPointInPath(x, y) will check if the point at coords x and y is inside the current sub-path that's being traced in the context.

Every time you call ctx.beginPath(), this sub-path is cleared.

To workaround that you would have to retrace the whole list of stars and check one by one if the point falls in there.
However, there is also a Path2D interface that you can use.

This interface allows to hold such sub-path as their own object, and can be used by the context instead of the internal context's path by e.g ctx.fill(path), ctx.stroke(path), ctx.clip(path), ctx.scrollPathIntoView(path), ctx.drawFocusIfNeeded(path, element), and you probably guessed it, ctx.isPointInPath(path, x, y), or ctx.isPointInStroke(path, x, y).

So you can rewrite your code so that each star is stored in its own Path2D instance, store all these instances in an Array and then traverse that Array to check if any of these was hit by the point:

const stars = [];
const big = document.getElementById("big");
const small = document.getElementById("small");

big.height = 600;
big.width = 500;
small.height = 600;
small.width = 50;

const ctx = big.getContext("2d");

function createStar(moveToX, moveToY, lineToX1, lineToY1, lineToX2, lineToY2, lineToX3, lineToY3, lineToX4, lineToY4, color) {
  const path = new Path2D();
  // store our path in an object also hodling the color
  stars.push({path, color});
  path.moveTo(moveToX, moveToY);
  path.lineTo(lineToX1, lineToY1);
  path.lineTo(lineToX2, lineToY2);
  path.lineTo(lineToX3, lineToY3);
  path.lineTo(lineToX4, lineToY4);
  ctx.strokeStyle = color;
  ctx.fillStyle = color;
  ctx.fill(path);
  ctx.stroke(path);
};

const red = new createStar(20, 60, 100, 60, 35, 110, 60, 25, 85, 110, 'red');
const blue = new createStar(120, 60, 200, 60, 135, 110, 160, 25, 185, 110, 'blue');
const green = new createStar(120, 160, 200, 160, 135, 210, 160, 125, 185, 210, 'green');
const black = new createStar(220, 460, 400, 460, 235, 560, 300, 400, 385, 560, 'black');
const yellow = new createStar(220, 260, 300, 260, 235, 310, 260, 225, 285, 310, 'yellow');

big.addEventListener('click', function(e) {
  // find the first star that got clicked
  const touched = stars.find((star) =>
    ctx.isPointInPath(star.path, e.offsetX, e.offsetY)
  );
  if (touched) {
    small.style.backgroundColor = touched.color;
  } else {
    small.style.backgroundColor = 'white';
  }
});
canvas {
  background-color: rgb(255, 255, 255);
  border: 1px solid black;
}
<canvas id='big'>Обновите браузер</canvas>
<canvas id='small'>Обновите браузер</canvas>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文