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

Moh*_*sin 6 javascript html5 transparency canvas 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);
Run Code Online (Sandbox Code Playgroud)

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

mar*_*rkE 7

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

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

在此输入图像描述

这是示例代码和演示: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>
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用线性渐变检查以更直接地执行效果.

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


pop*_*rap 6

一些让我绊倒的东西可能有用......我对此遇到了问题,因为我认为并且putImageData()drawImage()以相同的方式工作,但似乎它们没有。putImageData()将用自己的透明数据覆盖现有像素,同时drawImage()保持它们不变。

当研究这个时,我只是浏览了CanvasRenderingContext2D.globalCompositeOperation的文档(应该更仔细地阅读),看到这source-over是默认值,并没有意识到这不适用于putImageData()

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


Jos*_*ers 5

问题

如您所知,您的声明

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

将使图像右半部分的像素透明,以便可以通过画布在该像素位置看到画布后面页面上的任何其他对象。但是,您仍然用 覆盖画布本身的像素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);
Run Code Online (Sandbox Code Playgroud)