Faded image edges on canvas using getImageData
Mia Lopez
Using getImageData, I'm trying to create a radial fade effect on an image where point(0,0) has an alpha of 0 and point(img.width, img.height) has an alpha of 0.5 sort of like this:
(The last point has an alpha of 1.0, but imagine it's 0.5.)
I've been able to create a linear alpha increase in my for loop using
imgData.data[i+3]=i/imgData.length;but the left edge (obviously) becomes more defined as the loop iterates.
I've tried using two loops, one to create row "breaks" in the array, and the other to handle the alpha manipulation like so:
for(var j=0;j<imgData.height;j++){ for(var i=0;i<imgData.data.length;i+=4){ imgData.data[i+3] = 0 + 125 * i/imgData.data.length; } }but the performance of this is really slow and it doesn't achieve the fade that I'm looking for. I'm also not sure I'm doing that properly. I also tried Faster Canvas Pixel Manipulation with Typed Arrays as outlined on the Mozilla Hacks site, but the image wasn't even drawn to the canvas using that method. (I'm using Chrome - not sure if that's the issue or if it's my code.)
Any ideas?
EDIT: Thanks to markE for the great solution - it's beautifully quick. I mentioned this in my comment to him, but here's the full code for creating a greyscale image (part of my requirements) that has the fade pictured in mark's demo.
img_width = img_height = img_width * (9/16); //hidden_ctx is hidden behind a background layer var g1=hidden_ctx.createRadialGradient(500,500,550,500,500,650); g1.addColorStop(0.00,"rgba(0,0,0,0.7)"); g1.addColorStop(0.30,"rgba(0,0,0,0.35)"); g1.addColorStop(1.00,"rgba(0,0,0,0.00)"); hidden_ctx.fillStyle=g1; hidden_ctx.fillRect(0, 0, img_width, img_height); hidden_ctx.globalCompositeOperation="source-in"; //custom drawImage method where selected[num] contains //contains images & metadata drawIt(hidden_ctx, selected[num].image[0], 0, 0, img_width, img_height); imgData=hidden_ctx.getImageData(0, 0, img_width, img_height); // invert colors for (var i=0;i<imgData.data.length;i+=4) { grayscaled = 0.34 * imgData.data[i] + 0.5 * imgData.data[i + 1] + 0.16 * imgData.data[i + 2]; imgData.data[i]=grayscaled; imgData.data[i+1]=grayscaled; imgData.data[i+2]=grayscaled; } //places the converted image in the lower right corner where //WIDTH/HEIGHT is ctx.putImageData(imgData, WIDTH-img_width, HEIGHT-img_height); 1 1 Answer
You can use compositing to achieve a fade effect more efficiently.
draw a radial gradient that fades to transparent in the top-left corner.
Adjust the radial gradient to meet your design needs.
set compositing to
source-inwhich causes any subsequent draws to only appear over existing non-transparent pixels.draw your image (the image will fade the same way your radial gradient did)
Using compositing is especially efficient if the device has a GPU because compositing will use it.
Example code and a Demo:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src=""></script>
<style> canvas{ background-color: lightgray; } canvas{border:1px solid red;}
</style>
<script>
$(function(){ var canvas=document.getElementById("canvas"); var ctx=canvas.getContext("2d"); var cw=canvas.width; var ch=canvas.height; var img=new Image(); img.onload=start; img.src=""; function start(){ canvas.width=img.width; canvas.height=img.height; var g1=ctx.createRadialGradient(500,500,550,500,500,650); g1.addColorStop(0.00,"rgba(0,0,0,1.00)"); g1.addColorStop(0.30,"rgba(0,0,0,0.75)"); g1.addColorStop(1.00,"rgba(0,0,0,0.00)"); ctx.fillStyle=g1; ctx.fillRect(0,0,cw,ch); ctx.globalCompositeOperation="source-in"; ctx.drawImage(img,0,0); }
}); // end $(function(){});
</script>
</head>
<body> <h4>Fading an image using compositing</h4> <canvas width=300 height=300></canvas>
</body>
</html> 1