putImageData(),如果新像素是透明的,如何保留旧像素?

时间:2014-03-05 09:56:56

标签: javascript html5 canvas transparency pixel

在html5中,当您使用putImageData()绘制到画布时,如果您绘制的某些像素是透明的(或半透明),那么如何保持画布中的旧像素不受影响?

例如:

var imgData = context.createImageData(30,30);
for(var i=0; i<imgData.data.length; i+=4)
{
imgData.data[i]=255;
imgData.data[i+1]=0;
imgData.data[i+2]=0;
imgData.data[i+3]=255;
if((i/4)%30 > 15)imgData.data[i+3] = 0;
}
context.putImageData(imgData,0,0);

30x30 rect的右半部分是透明的。 如果在画布上绘制某些内容,则会删除右半部分后面的像素(或变为透明)。我该如何保留它们?

4 个答案:

答案 0 :(得分:6)

您可以使用getImageData创建半透明叠加层:

  • 创建临时屏幕外画布
  • getImageData从屏幕外画布获取像素数据
  • 根据需要修改像素
  • putImageData将像素重新放回屏幕外的画布
  • 使用drawImage将屏幕外画布绘制到屏幕画布

enter image description here

以下是示例代码和演示:http://jsfiddle.net/m1erickson/CM7uY/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
    body{ background-color: ivory; }
    canvas{border:1px solid red;}
</style>
<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var context=canvas.getContext("2d");

    // draw an image on the canvas
    var img=new Image();
    img.onload=start;
    img.src="https://dl.dropboxusercontent.com/u/139992952/stack1/landscape1.jpg";
    function start(){
        canvas.width=img.width;
        canvas.height=img.height;
        context.drawImage(img,0,0);

        // overlay a red gradient 
        drawSemiTransparentOverlay(canvas.width/2,canvas.height)

    }

    function drawSemiTransparentOverlay(w,h){

        // create a temporary canvas to hold the gradient overlay
        var canvas2=document.createElement("canvas");
        canvas2.width=w;
        canvas2.height=h
        var ctx2=canvas2.getContext("2d");

        // make gradient using ImageData
        var imgData = ctx2.getImageData(0,0,w,h);
        var data=imgData.data;
        for(var y=0; y<h; y++) {
            for(var x=0; x<w; x++) {
                var n=((w*y)+x)*4;
                data[n]=255;
                data[n+1]=0;
                data[n+2]=0;
                data[n+3]=255;
                if(x>w/2){
                    data[n+3]=255*(1-((x-w/2)/(w/2)));
                }
            }
        }

        // put the modified pixels on the temporary canvas
        ctx2.putImageData(imgData,0,0);

        // draw the temporary gradient canvas on the visible canvas
        context.drawImage(canvas2,0,0);

    }


}); // end $(function(){});
</script>
</head>
<body>
    <canvas id="canvas" width=200 height=200></canvas>
</body>
</html>

或者,您可以使用线性渐变检查以更直接地发挥效果。

http://jsfiddle.net/m1erickson/j6wLR/

答案 1 :(得分:2)

问题

如您所知,您的陈述

if((i/4)%30 > 15)imgData.data[i+3] = 0;

将使图像右半部分的像素变为透明,以便可以通过画布在该像素位置看到画布后面页面上的任何其他对象。但是,您仍然使用context.putImageData覆盖画布本身的像素,这将取代之前的所有像素。您添加的透明度不会导致前一个像素显示,因为putImageData的结果不是画布中前一个像素顶部的第二组像素,而是替换现有像素。

解决方案

我建议您不要使用createImageData来开始代码,而getImageData将以一组空白数据开头,而是var imgData = context.getImageData(30,30); for(var i=0; i<imgData.data.length; i+=4) { if((i/4)%30 > 15) continue; imgData.data[i]=255; imgData.data[i+1]=0; imgData.data[i+2]=0; imgData.data[i+3]=255; } context.putImageData(imgData,0,0); ,它会为您提供要使用的现有数据的副本。然后,您可以使用条件语句来避免覆盖要保留的图像部分。这也将使您的功能更有效。

{{1}}

答案 2 :(得分:0)

我想复制一个CRISP,未经修改的画布版本。我最终想出了这个适用的解决方案。

https://jsfiddle.net/4Le454ak/1/

复制部分在此代码中:

var imageData = canvas.toDataURL(0, 0, w, h);
var tmp = document.createElement('img');
tmp.style.display = 'none'
tmp.src = imageData;
document.body.appendChild(tmp);
ctx.drawImage(tmp, 30, 30);

发生了什么:

  • 从画布复制图像数据
  • 将图像数据设置为看不见的(必须在dom中)
  • 将该图像绘制回画布
  • 您可以删除或重复使用

答案 3 :(得分:0)

使我绊倒的可能有用的东西……我对此有疑问,因为我假设putImageData()drawImage()可以以相同的方式工作,但看起来却没有。 putImageData()将使用其自己的透明数据覆盖现有像素,而drawImage()将使它们保持不变。

查看此内容时,我浏览了CanvasRenderingContext2D.globalCompositeOperation的文档(应该更仔细地阅读),发现source-over是默认设置,但没有意识到这不适用于{{1 }}

然后绘制到临时画布中并使用putImageData()将临时画布添加到主上下文是我需要的解决方案,因此为此感到高兴。