我想改变这个图像的背景颜色,同时保持形状,效果和轮廓 the image
<canvas id="canvas01" width="1200" height="800"></canvas>
<script>
function drawImage(imageObj,x, y, width, height){
var canvas = document.getElementById('canvas01');
var context = canvas.getContext('2d');
context.drawImage(imageObj, x, y, width, height);
}
var image = new Image();
image.onload = function(){
drawImage(this, 400, 100, 320, 450);
};
image.src ="images/658FFBC6.png";
</script>
答案 0 :(得分:6)
冒着类似于现有答案的风险,我想用略微不同的方法指出一个小但重要的区别。
关键是保留图像中的亮度分量(即阴影细节,皱纹等),因此需要两个步骤来通过globalCompositeOperation
使用混合模式控制外观(或者,如果必须支持旧浏览器,则使用RGB和HSL颜色空间之间的转换手动方法):
saturation
”:将改变下一个绘制元素的色度(强度,饱和度)并将其应用于画布上的现有内容,但保留亮度和色调。hue
”:将从源获取色度和亮度,但如果愿意,可根据下一个绘制的元素改变色调或颜色。由于这些是混合模式(忽略alpha通道),我们还需要使用合成剪辑结果作为最后一步。
color
混合模式也可以使用,但它会改变亮度,这可能是也可能不是。在许多情况下,差异可能很微妙,但也会非常明显,具体取决于失去亮度/阴影定义的目标色度和色调。
因此,要获得保持亮度和色度的高质量结果,这些或多或少是主要步骤(假设空白画布):
// step 1: draw in original image
ctx.globalCompositeOperation = "source-over";
ctx.drawImage(img, 0, 0);
// step 2: adjust saturation (chroma, intensity)
ctx.globalCompositeOperation = "saturation";
ctx.fillStyle = "hsl(0," + sat + "%, 50%)"; // hue doesn't matter here
ctx.fillRect(0, 0);
// step 3: adjust hue, preserve luma and chroma
ctx.globalCompositeOperation = "hue";
ctx.fillStyle = "hsl(" + hue + ",1%, 50%)"; // sat must be > 0, otherwise won't matter
ctx.fillRect(0, 0, c.width, c.height);
// step 4: in our case, we need to clip as we filled the entire area
ctx.globalCompositeOperation = "destination-in";
ctx.drawImage(img, 0, 0);
// step 5: reset comp mode to default
ctx.globalCompositeOperation = "source-over";
50%亮度(L)将保持原始亮度值。
单击复选框以查看对结果的影响。然后使用不同的色度和色调设置进行测试。
var ctx = c.getContext("2d");
var img = new Image(); img.onload = demo; img.src = "//i.stack.imgur.com/Kk1qd.png";
function demo() {c.width = this.width>>1; c.height = this.height>>1; render()}
function render() {
var hue = +rHue.value, sat = +rSat.value, l = +rL.value;
ctx.clearRect(0, 0, c.width, c.height);
ctx.globalCompositeOperation = "source-over";
ctx.drawImage(img, 0, 0, c.width, c.height);
if (!!cColor.checked) {
// use color blending mode
ctx.globalCompositeOperation = "color";
ctx.fillStyle = "hsl(" + hue + "," + sat + "%, 50%)";
ctx.fillRect(0, 0, c.width, c.height);
}
else {
// adjust "lightness"
ctx.globalCompositeOperation = l < 100 ? "color-burn" : "color-dodge";
// for common slider, to produce a valid value for both directions
l = l >= 100 ? l - 100 : 100 - (100 - l);
ctx.fillStyle = "hsl(0, 50%, " + l + "%)";
ctx.fillRect(0, 0, c.width, c.height);
// adjust saturation
ctx.globalCompositeOperation = "saturation";
ctx.fillStyle = "hsl(0," + sat + "%, 50%)";
ctx.fillRect(0, 0, c.width, c.height);
// adjust hue
ctx.globalCompositeOperation = "hue";
ctx.fillStyle = "hsl(" + hue + ",1%, 50%)";
ctx.fillRect(0, 0, c.width, c.height);
}
// clip
ctx.globalCompositeOperation = "destination-in";
ctx.drawImage(img, 0, 0, c.width, c.height);
// reset comp. mode to default
ctx.globalCompositeOperation = "source-over";
}
rHue.oninput = rSat.oninput = rL.oninput = cColor.onchange = render;
body {font:16px sans-serif}
<div>
<label>Hue: <input type=range id=rHue max=359 value=0></label>
<label>Saturation: <input type=range id=rSat value=100></label>
<label>Lightness: <input type=range id=rL max=200 value=100></label>
<label>Use "color" instead: <input type=checkbox id=cColor></label>
</div>
<canvas id=c></canvas>
答案 1 :(得分:5)
2D上下文属性ctx.globalCompositeOperation
对于各种图像处理任务非常有用。有关globalCompositeOperation at MDN
您可以将图像转换为画布,这样就可以对其进行编辑。
function imageToCanvas(image){
const c = document.createElement("canvas");
c.width = image.width;
c.height = image.height;
c.ctx = c.getContext("2d"); // attach context to the canvas for eaasy reference
c.ctx.drawImage(image,0,0);
return c;
}
您可以使用globalCompositeOperation = "color"
为图像着色
function colorImage(image,color){ // image is a canvas image
image.ctx.fillStyle = color;
image.ctx.globalCompositeOperation = "color";
image.ctx.fillRect(0,0,image.width,image.height);
image.ctx.globalCompositeOperation = "source-over";
return image;
}
不幸的是,这也会覆盖alpha像素,因此您需要使用原始图像作为遮罩来恢复alpha像素。
function maskImage(dest,source){
dest.ctx.globalCompositeOperation = "destination-in";
dest.ctx.drawImage(source,0,0);
dest.ctx.globalCompositeOperation = "source-over";
return dest;
}
然后你有一个彩色图像
在他的例子中,我用一系列颜色为图像着色,并添加了一个功能,将图像的画布副本恢复为原始图像。如果您将页面中的图片作为元素获取,则使用naturalWidth
和naturalHeight
,因为width
和height
属性可能与图像分辨率不匹配。
const ctx = canvas.getContext("2d");
const image = new Image;
var colCopy;
image.src = "https://i.stack.imgur.com/Kk1qd.png";
image.onload = () => {
colCopy = imageToCanvas(image);
const scale = canvas.height / image.naturalHeight;
ctx.scale(scale, scale);
ctx.drawImage(colCopy, 0, 0);
for (var i = 32; i < 360; i += 32) {
restoreImage(colCopy, image);
colorImage(colCopy, "hsl(" + i + ",100%,50%)");
maskImage(colCopy, image);
ctx.drawImage(colCopy, 150 * i / 16, 0);
}
}
function imageToCanvas(image) {
const c = document.createElement("canvas");
c.width = image.naturalWidth;
c.height = image.naturalHeight;
c.ctx = c.getContext("2d"); // attach context to the canvas for easy reference
c.ctx.drawImage(image, 0, 0);
return c;
}
function restoreImage(dest, source) {
dest.ctx.clearRect(0, 0, dest.width, dest.height);
dest.ctx.drawImage(source, 0, 0);
return dest;
}
function colorImage(dest, color) { // image is a canvas image
dest.ctx.fillStyle = color;
dest.ctx.globalCompositeOperation = "color";
dest.ctx.fillRect(0, 0, dest.width, dest.height);
dest.ctx.globalCompositeOperation = "source-over";
return dest;
}
function maskImage(dest, source) {
dest.ctx.globalCompositeOperation = "destination-in";
dest.ctx.drawImage(source, 0, 0);
dest.ctx.globalCompositeOperation = "source-over";
return dest;
}
canvas {
border: 2px solid black;
}
<canvas id="canvas" width=600></canvas>
在某些情况下,图像会稍微褪色,您可以使用类似于上面所示的复合操作将图像转换为更高对比度的黑白图像,并使用高对比度图像作为模板进行着色。
现在大多数常见浏览器都支持具有色调偏移滤镜的画布滤镜。您可以使用它将色调移动到您想要的值,但首先您需要知道图像原始色调是什么。 (参见下面关于如何找到HUE的示例)
有关兼容性以及如何使用画布过滤器,请参阅Canvas filters at MDN。
以下功能将保持饱和度并仅移动色调。
// dest canvas to hold the resulting image
// source the original image
// hue The hue to set the dest image to
// sourceHue the hue reference point of the original image.
function colorImage(dest,source, hue , sourceHue) { // image is a canvas image
dest.ctx.clearRect(0,0,dest.width, dest.height);
dest.ctx.filter="hue-rotate("+((hue - sourceHue) | 0)+"deg)";
dest.ctx.drawImage(source,0, 0, dest.width, dest.height);
return dest;
}
以下使用ctx.filter = "hue-rotate(30deg)"
来旋转色调。我没有包含任何代码来查找图像原始色调,因此请手动将其设置为120.
const ctx = canvas.getContext("2d");
const image = new Image;
var colCopy;
const sourceHue = 120;
image.src = "https://i.stack.imgur.com/Kk1qd.png";
image.onload = () => {
colCopy = imageToCanvas(image);
const scale = canvas.height / image.naturalHeight;
ctx.scale(scale, scale);
ctx.drawImage(colCopy, 0, 0);
for (var i = 32; i < 360; i += 32) {
colorImage(colCopy,image,i,sourceHue);
ctx.drawImage(colCopy, 150 * i / 16, 0);
}
}
function imageToCanvas(image) {
const c = document.createElement("canvas");
c.width = image.naturalWidth;
c.height = image.naturalHeight;
c.ctx = c.getContext("2d"); // attach context to the canvas for easy reference
c.ctx.drawImage(image, 0, 0);
return c;
}
function colorImage(dest,source, hueRotate , sourceHue) { // image is a canvas image
dest.ctx.clearRect(0,0,dest.width, dest.height);
dest.ctx.filter="hue-rotate("+((hueRotate - sourceHue) | 0)+"deg)";
dest.ctx.drawImage(source,0, 0, dest.width, dest.height);
return dest;
}
canvas {
border: 2px solid black;
}
<canvas id="canvas" width=600></canvas>
有很多答案可以帮助在SO上找到像素的色调。这是一个特别详细的RGB to HSL conversion如果你发现它有用,不要忘记给出答案。
以下使用ctx.filter = "grayscale(100%)"
删除饱和度,然后使用ctx.filter = "brightness(amount%)"
更改亮度。这提供了从黑色到白色的一系列灰色。您也可以通过减少灰度量来对颜色执行相同的操作。
const ctx = canvas.getContext("2d");
const image = new Image;
var colCopy;
const sourceHue = 120;
image.src = "https://i.stack.imgur.com/Kk1qd.png";
image.onload = () => {
colCopy = imageToCanvas(image);
const scale = canvas.height / image.naturalHeight;
ctx.scale(scale, scale);
ctx.drawImage(colCopy, 0, 0);
for (var i = 40; i < 240; i += 20) {
grayImage(colCopy,image,i);
ctx.drawImage(colCopy, 150 * ((i-40) / 12), 0);
}
}
function imageToCanvas(image) {
const c = document.createElement("canvas");
c.width = image.naturalWidth;
c.height = image.naturalHeight;
c.ctx = c.getContext("2d"); // attach context to the canvas for easy reference
c.ctx.drawImage(image, 0, 0);
return c;
}
function grayImage(dest,source, brightness) { // image is a canvas image
dest.ctx.clearRect(0,0,dest.width, dest.height);
dest.ctx.filter = "grayscale(100%)";
dest.ctx.drawImage(source,0, 0, dest.width, dest.height);
dest.ctx.filter = "brightness(" + brightness +"%)";
dest.ctx.drawImage(dest,0, 0, dest.width, dest.height);
return dest;
}
canvas {
border: 2px solid black;
}
<canvas id="canvas" width=600></canvas>
答案 2 :(得分:-1)
您可以在执行绘制操作之前将过滤器组合在一行代码上,如下所示:
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const image = document.getElementById('source');
ctx.filter = 'hue-rotate(120deg) grayscale(10%) brightness(150%)';
ctx.drawImage(image, 10, 10, 180, 120);
<canvas id="canvas"></canvas>
<div style="display:none;">
<img id="source"
src="https://interactive-examples.mdn.mozilla.net/media/examples/gecko-320-213.jpg">
</div>