html5 canvas沿着路径应用过滤器



我有一个图像,图像内部有一个对象。我只想增加图像中与物体相关的部分的亮度。我可以用path,或者其他一些画布上的东西来找到这个物体。是否有可能将CanvasRenderingContext2D.filter仅应用于图像的一部分?

我天真地尝试了以下基于全图像亮度控制的解决方案:

const imageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Carl_Friedrich_Gauss_1840_by_Jensen.jpg/800px-Carl_Friedrich_Gauss_1840_by_Jensen.jpg";
const ctx = canvas.getContext("2d");
ctx.fillText("Please wait loading image..",20,20);
const image = new Image;
image.src = imageUrl;
image.onload = () => {
canvas.width = image.width;
canvas.height = image.height;     
ctx.drawImage(image, 0, 0);
slider.addEventListener("mousemove", () => {
if (slider.value !== slideOldVal) {
setBrightness(Number(slider.value));
}
val.textContent = slider.value;
slideOldVal = Number(slider.value);
});
}
function setBrightness(value) {
ctx.drawImage(image, 0, 0);
ctx.filter = `brightness(${value + 100}%)`;
ctx.strokeStyle = `rgb(255,255,255,0)`;
ctx.lineWidth = 16;

let path2 = new Path2D();
path2.moveTo(220, 60);
path2.arc(170, 60, 50, 0, 2 * Math.PI);
ctx.stroke(path2);

ctx.filter = `brightness(${100}%)`;
}
var slideOldVal;
Brightness<input id="slider" type="range" min="-100" max="400" value="0" step="1"></input><span id="val"></span><br>
<canvas id="canvas"></canvas>

这是尝试增加位于图像左上角的圆形路径的亮度。这段代码实际上什么也没完成。

是否有一种方法可以使用HTML5画布增加图像的部分亮度?

滤镜将只适用于下一幅画,但在这里下一幅画将是透明的,给你的strokeStyle。你想要的是用那个滤镜再次绘制图像,但只在弧的笔画的地方。
对于这个问题,你有多种解决方案。

其中之一是使用合成,这将允许您使用描边作为蒙版,并仅在其上应用滤镜。即使这样,你也有各种方法来构建它,一些取决于你的原始图像是否具有透明度,一些使用屏幕外画布等。这是其中之一。

const imageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Carl_Friedrich_Gauss_1840_by_Jensen.jpg/800px-Carl_Friedrich_Gauss_1840_by_Jensen.jpg";
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const path2 = new Path2D();
// I moved a bit the arc so that the filter is better visible
path2.moveTo(320, 160);
path2.arc(270, 160, 50, 0, 2 * Math.PI);
ctx.fillText("Please wait loading image..",20,20);
const image = new Image;
image.src = imageUrl;
image.onload = () => {
canvas.width = image.width;
canvas.height = image.height;
ctx.lineWidth = 16;
ctx.drawImage(image, 0, 0);
slider.addEventListener("input", () => {
setBrightness(Number(slider.value));
val.textContent = slider.value;
});
}
function setBrightness(value) {
// start with only the arc stroke on the context
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.stroke(path2);
// use it as a mask so that the image is only visible where the stroke is
// with the expected filter applied
ctx.filter = `brightness(${value + 100}%)`;
ctx.globalCompositeOperation = "source-in";    
ctx.drawImage(image, 0, 0);
// draw the image again, only where we didn't draw yet,
// without filter
ctx.filter = "none";
ctx.globalCompositeOperation = "destination-atop";
ctx.drawImage(image, 0, 0);
// reset gCO
ctx.globalCompositeOperation = "source-over";
}
Brightness<input id="slider" type="range" min="-100" max="400" value="0" step=".1"><span id="val"></span><br>
<canvas id="canvas"></canvas>

另一个解决方案,因为你所做的是drawImage()资源,是从该资源创建一个CanvasPattern,并使用它作为strokeStyle,少代码,但可能不适合所有的用例。

const imageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Carl_Friedrich_Gauss_1840_by_Jensen.jpg/800px-Carl_Friedrich_Gauss_1840_by_Jensen.jpg";
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const path2 = new Path2D();
path2.moveTo(320, 160);
path2.arc(270, 160, 50, 0, 2 * Math.PI);
ctx.fillText("Please wait loading image..",20,20);
const image = new Image;
image.src = imageUrl;
image.onload = () => {
canvas.width = image.width;
canvas.height = image.height;
// since we don't change the strokeStyle we can store it directly here
// if you need to change the strokeStyle during your app's life
// store it in its own variable and set it as strokeStyle every time.
ctx.strokeStyle = ctx.createPattern(image, "no-repeat");
ctx.lineWidth = 16;
ctx.drawImage(image, 0, 0);
slider.addEventListener("input", () => {
setBrightness(Number(slider.value));
val.textContent = slider.value;
});
}
function setBrightness(value) {
ctx.drawImage(image, 0, 0);
ctx.filter = `brightness(${value + 100}%)`;
ctx.stroke(path2);
ctx.filter = "none";
}
Brightness<input id="slider" type="range" min="-100" max="400" value="0" step="0.1"><span id="val"></span><br>
<canvas id="canvas"></canvas>

最后,因为你只使用亮度滤镜,你可以使用混合模式来获得一个类似的效果,与CSS过滤器不完全相同,但在Safari浏览器中工作将有好处。

const imageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Carl_Friedrich_Gauss_1840_by_Jensen.jpg/800px-Carl_Friedrich_Gauss_1840_by_Jensen.jpg";
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const path2 = new Path2D();
path2.moveTo(320, 160);
path2.arc(270, 160, 50, 0, 2 * Math.PI);
ctx.fillText("Please wait loading image..",20,20);
const image = new Image;
image.src = imageUrl;
image.onload = () => {
canvas.width = image.width;
canvas.height = image.height;
ctx.lineWidth = 16;
ctx.drawImage(image, 0, 0);
slider.addEventListener("input", () => {
setBrightness(Number(slider.value));
val.textContent = slider.value;
});
}
function setBrightness(value) {
ctx.drawImage(image, 0, 0);
// kind of hackish multipass "overlay" blending to approach brightness()
// I'm sure there are better ways to do this
ctx.strokeStyle = value < 0 ? "black" : "white";
const numberOfPass = Math.abs(value) / 200;
ctx.globalCompositeOperation = "overlay";
for (let i = 0; i < numberOfPass - 1; i++) {
ctx.stroke(path2);
}
const lastPassAlpha = numberOfPass - Math.round(numberOfPass);
ctx.globalAlpha = lastPassAlpha || 1;
ctx.stroke(path2);
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
}
Brightness<input id="slider" type="range" min="-100" max="400" value="0" step="0.1"></input><span id="val"></span><br>
<canvas id="canvas"></canvas>

最新更新