如何解决描边/填充顺序以创建混合重叠
我有很多对象可以在画布上绘制。
对象的中心需要与标准alpha融合。
后面的对象的边界(笔触)需要显示出来,以删除所有潜在的边界,同时保持填充完整以进行混合。
一个示例代码示例,几次尝试均失败-请注意,“所需”结果是手动产生的。
该解决方案也需要扩展,因为这是针对requestAnimationFrame进行的,并且要迭代成千上万个对象,因此执行单独的beginPath()/ stroke()组合不太可能。
var canvas = document.getElementById('canvas');
canvas.width = 600;
canvas.height = 600;
var ctx = canvas.getContext('2d');
//set up control objects
let objects = [{
x: 20,y: 20,w: 60,h: 30,rgba: "rgba(255,.5)"
},{
x: 40,y: 30,rgba: "rgba(0,255,.5)"
}]
//manually produce desired outcome
ctx.beginPath();
for (let i = 0,l = objects.length; i < l; i++) {
let myObject = objects[i];
ctx.fillStyle = myObject.rgba;
ctx.fillRect(myObject.x,myObject.y,myObject.w,myObject.h);
}
ctx.beginPath();
ctx.moveTo(40,50);
ctx.lineTo(20,20);
ctx.lineTo(80,30);
ctx.rect(40,30,60,30);
ctx.stroke();
ctx.font = "15px Arial"
ctx.fillStyle = "black";
ctx.fillText("Desired outcome - (done manually for example)",120,50);
//Attempt one: fill on iterate,stroke on end
ctx.beginPath();
for (let i = 0,l = objects.length; i < l; i++) {
let myObject = objects[i];
ctx.rect(myObject.x,myObject.y + 70,myObject.h);
ctx.fillStyle = myObject.rgba;
ctx.fillRect(myObject.x,myObject.h);
}
ctx.stroke();
ctx.fillStyle = "black";
ctx.fillText("Attempt #1: inner corner of red box fully visible",120);
//Attempt two: fill and stroke on iterate
for (let i = 0,l = objects.length; i < l; i++) {
let myObject = objects[i];
ctx.beginPath();
ctx.rect(myObject.x,myObject.y + 140,myObject.h);
ctx.stroke();
}
ctx.fillStyle = "black";
ctx.fillText("Attempt #2: inner corner of red box partly visible",170);
ctx.fillText("(This also scales very badly into thousands of strokes)",190);
<canvas name="canvas" id="canvas" style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
解决方法
您可以通过两次绘制来实现此目的:
首先,您将通过迭代合成笔触
- 使用
globalCompositeOperation = "destination-out"
清除填充将落入的每个先前的笔划
- 绘制此 rect 的笔划
完成此操作后,您的画布将只剩下最后的笔触。
现在您必须绘制填充,但是由于我们希望笔触位于填充的前面,因此我们必须使用其他合成模式:"destination-over"
并迭代我们的 rects 以相反的顺序:
(async () => {
var canvas = document.getElementById('canvas');
canvas.width = 600;
canvas.height = 600;
var ctx = canvas.getContext('2d');
ctx.scale(2,2)
//set up control objects
let objects = [{
x: 20,y: 20,w: 60,h: 30,rgba: "rgba(255,.5)"
},{
x: 40,y: 30,rgba: "rgba(0,255,{
x: 10,y: 5,.5)"
}]
// first pass,composite the strokes
for (let i = 0,l = objects.length; i < l; i++) {
let myObject = objects[i];
ctx.beginPath();
ctx.rect(myObject.x,myObject.y,myObject.w,myObject.h);
// erase the previous strokes where our fill will be
ctx.globalCompositeOperation = "destination-out";
ctx.fillStyle = "#000"; // must be opaque
ctx.fill();
// draw our stroke
ctx.globalCompositeOperation = "source-over";
ctx.stroke();
}
await wait(1000);
// second pass,draw the colored fills
// we will draw from behind to keep the stroke at frontmost
// so we need to iterate our objects in reverse order
for (let i = objects.length- 1; i >= 0; i--) {
let myObject = objects[i];
// draw behind
ctx.globalCompositeOperation = "destination-over";
ctx.fillStyle = myObject.rgba;
ctx.fillRect(myObject.x,myObject.h);
}
})();
function wait(ms){
return new Promise(res => setTimeout(res,ms));
}
<canvas name="canvas" id="canvas" style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
当然,它也可以在动画中使用:
var canvas = document.getElementById('canvas');
canvas.width = 100;
canvas.height = 100;
var ctx = canvas.getContext('2d');
//set up control objects
let objects = [{
x: 20,.5)"
}]
objects.forEach( rect => {
rect.speedX = Math.random() * 2 - 1;
rect.speedY = Math.random() * 2 - 1;
});
requestAnimationFrame(anim);
onclick = anim
function anim() {
update();
draw();
requestAnimationFrame( anim );
}
function update() {
objects.forEach( rect => {
rect.x = rect.x + rect.speedX;
rect.y = rect.y + rect.speedY;
if(
rect.x + rect.w > canvas.width ||
rect.x < 0
) {
rect.speedX *= -1;
}
if(
rect.y + rect.h > canvas.height ||
rect.y < 0
) {
rect.speedY *= -1;
}
});
}
function draw() {
ctx.clearRect(0,canvas.width,canvas.height);
// first pass,composite the strokes
for (let i = 0,l = objects.length; i < l; i++) {
let myObject = objects[i];
ctx.beginPath();
ctx.rect(myObject.x,myObject.h);
// erase the previous strokes where our fill will be
ctx.globalCompositeOperation = "destination-out";
ctx.fillStyle = "#000"; // must be opaque
ctx.fill();
// draw our stroke
ctx.globalCompositeOperation = "source-over";
ctx.stroke();
}
// second pass,draw the colored fills
// we will draw from behind to keep the stroke at frontmost
// so we need to iterate our objects in reverse order
for (let i = objects.length- 1; i >= 0; i--) {
let myObject = objects[i];
// draw behind
ctx.globalCompositeOperation = "destination-over";
ctx.fillStyle = myObject.rgba;
ctx.fillRect(myObject.x,myObject.h);
}
ctx.globalCompositeOperation = "source-over";
}
<canvas name="canvas" id="canvas" style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。