Canvas - 如何使用 HTML5/CSS/JS 改变图片的颜色?

9
有没有一种方法可以像Flash/ActionScript那样只使用HTML5/CSS/JavaScript来改变图像的颜色?
以下是Flash中的示例:http://www.kirupa.com/developer/actionscript/color.htm 编辑:我希望复制这个过程。 我正在尝试实现类似于这个(改变颜色,保留灯光和阴影):http://www.acura.com/ExteriorColor.aspx?model=TL,但不要每次加载并替换新图像; 我想能够简单地改变颜色调子(即执行纯HTML5/CSS/JS方法)。编辑:这是我希望实现的结果,而不是过程。 基本上我想改变图像的调色板/色调,同时保留图像的其他方面(如渐变、灯光等)。我已经接近了目标,但还不够。我已经使用了一个图像掩模,并将其合成到原始图像中(就像透明图像覆盖层一样)。下面是一些示例代码(请注意这只是一小部分代码,可能会出现问题;我只是显示示例代码,以便你了解我的意图):
<div id='mask'>
    <canvas id='canvas' width='100' height='100'></canvas>
</div>
<button data-rgba='255,0,0,0.5'>red</button>
<button data-rgba='0,255,0,0.5'>green</button>

<script>
foo = {};
foo.ctx = jQuery('#canvas')[0];
foo.ctx_context = foo.ctx.getContext('2d');
foo.mask = jQuery('#mask')[0];
foo.img = new Image();
foo.img_path = '/path/to/image_mask.png'; // 100px x 100px image

foo.changeColor = function(rgba){
    foo.ctx_context.clearRect(0, 0, 100, 100);
    foo.ctx_context.fillStyle = 'rgba(' + rgba + ')';
    foo.ctx_context.fillRect(0, 0, 100, 100);
    foo.ctx_context.globalCompositeOperation = 'destination-atop';
    foo.ctx_context.drawImage(foo.img, 0, 0);
    foo.ctx_context.css({'background-image': "url(" + foo.ctx.toDataURL("image/png") + ")"});
};

jQuery("button").click(function(){
    foo.changeColor(jQuery(this).attr('data-rgba'));
});
</script>

使用这种方法的主要问题是图像看起来非常平淡。还缺少其他东西,或者我使用的遮罩(它是一个实心白色的不规则形状图像)可能是错误的方法。

请在您得到答案后发布一个完成的fiddle。谢谢! - iambriansreed
3个回答

14

这个函数需要加载图片,并且它必须来自同一个域(由于跨域政策)

function tintImage(imgElement,tintColor) {
    // create hidden canvas (using image dimensions)
    var canvas = document.createElement("canvas");
    canvas.width = imgElement.offsetWidth;
    canvas.height = imgElement.offsetHeight;

    var ctx = canvas.getContext("2d");
    ctx.drawImage(imgElement,0,0);

    var map = ctx.getImageData(0,0,320,240);
    var imdata = map.data;

    // convert image to grayscale
    var r,g,b,avg;
    for(var p = 0, len = imdata.length; p < len; p+=4) {
        r = imdata[p]
        g = imdata[p+1];
        b = imdata[p+2];
        // alpha channel (p+3) is ignored           

        avg = Math.floor((r+g+b)/3);

        imdata[p] = imdata[p+1] = imdata[p+2] = avg;
    }

    ctx.putImageData(map,0,0);

    // overlay filled rectangle using lighter composition
    ctx.globalCompositeOperation = "lighter";
    ctx.globalAlpha = 0.5;
    ctx.fillStyle=tintColor;
    ctx.fillRect(0,0,canvas.width,canvas.height);

    // replace image source with canvas data
    imgElement.src = canvas.toDataURL();
}
假设您的标记看起来像这样:
<img id='myimage' src='image.jpg'/>

您可以使用以下参数调用它:

tintImage(
    document.getElementById('myImage'),
    "#550000" // for a redish tint
);

这里有一个可工作的示例:http://jsfiddle.net/pHwmL/1/


2
请注意,当您说跨域时,使用Chrome进行本地开发的人也无法看到其工作情况(我总是在本地通过Web服务器运行以验证它是否有效)。我喜欢你的方法,但发现它覆盖了任何Alpha通道...所以我添加了一个alpha数组,然后在之后将它们写回。效果很好。代码位于:https://github.com/jaycrossler/django-gamification/blob/master/gamification/static/themes/camping/badges.js#L711-L733 - JayCrossler

3
当然,你可以获取像素数据,并对其进行HSV调整。你需要的方法是getImageData(返回一个UInt8Array,而不是普通的js数组)。
这里有一篇不错的文章介绍了如何开始:链接,你可以在彼此之上叠加透明画布元素,这应该有助于避免调整基础组件时出现的问题(例如你的汽车示例中的轮胎、窗户等)。

-2
通常像逐像素更改图像这样的操作是在服务器端完成的,因为它对浏览器来说非常繁重。在Acura网站的情况下,没有什么棘手的问题 - 他们只有一堆不同的JPG文件。您还可以尝试查看Processing.js。

我不是逐像素地绘制任何东西;我正在将图像掩模合成到现有图像上,这比每次查询后端进行颜色更改要快得多。Acura所做的就是我想要实现的结果,但不是过程。Flash可以很好地改变颜色/色调(它是即时的),但令人遗憾的是,你不能在iPad上使用Flash,这就是为什么我正在寻找一个HTML5解决方案的原因。 - James Nine

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接