Luma保存
冒着类似于现有答案的风险,我想指出一个小而重要的差异,使用略有不同的方法.
关键是保留图像中的亮度分量(即阴影细节,皱纹等),因此需要两个步骤来通过globalCompositeOperation使用混合模式控制外观(或者,使用RGB和RGB之间的转换的手动方法)如果必须支持旧浏览器,则为HSL颜色空间):
>“饱和度”:将改变下一个绘制元素的色度(强度,饱和度)并将其应用于画布上的现有内容,但保留亮度和色调.
>“hue”:将从源获取色度和亮度,但如果愿意,可根据下一个绘制的元素改变色调或颜色.
由于这些是混合模式(忽略alpha通道),我们还需要使用合成作为最后一步来剪切结果.
颜色混合模式也可以使用,但它会改变亮度,这可能是也可能不是.在许多情况下,差异可能很微妙,但也很明显,这取决于失去亮度/阴影定义的目标色度和色调.
因此,为了获得保持亮度和色度的高质量结果,这些或多或少是主要步骤(假设空白画布):
// 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}
Hue:
Saturation:
Lightness:
Use "color" instead: