问题
png 图片转 jpeg 时,透明区域被填充成黑色
问题复现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head>
<body> <p>Canvas:</p> <canvas id="canvas" style="border: 1px solid #ccc;"></canvas> <br /> <p>Base64转码后的图片:</p> <div id="base64Img"></div>
<script type="text/javascript"> let base64Img = document.getElementById('base64Img'), canvas = document.getElementById('canvas'), ctx = canvas.getContext('2d');
let img = new Image(); img.src = 'off.png';
img.addEventListener( 'load', function () { canvas.width = img.width; canvas.height = img.height;
ctx.drawImage(img, 0, 0);
getBase64(canvas, function (dataUrl) { const newImg = document.createElement('img'); newImg.src = dataUrl;
base64Img.appendChild(newImg); }); }, false );
function getBase64(canvas, callback) { const dataURL = canvas.toDataURL('image/jpeg');
if (typeof callback !== undefined) { callback(dataURL); } } </script> </body> </html>
|
问题原因
canvas 转为 jpeg
之前会移除 alpha
通道,所以透明区域被填充成了黑色。
期待效果
canvas
可以将 png
的透明区域填充成白色。
解决方案
1. 将透明的 pixel 设成白色
因为 png 图片的背景都是透明的,所以我们可以寻找透明的 pixel,然后将其全部设置成白色。
代码片段:
1 2 3 4 5 6 7 8 9 10 11 12
| var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); var data = imgData.data; for (var i = 0; i < data.length; i += 4) { if (data[i + 3] < 255) { data[i] = 255 - data[i]; data[i + 1] = 255 - data[i + 1]; data[i + 2] = 255 - data[i + 2]; data[i + 3] = 255 - data[i + 3]; } } ctx.putImageData(imgData, 0, 0);
|
完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head>
<body> <p>Canvas:</p> <canvas id="canvas" style="border: 1px solid #ccc;"></canvas> <br /> <p>Base64转码后的图片:</p> <div id="base64Img"></div>
<script type="text/javascript"> let base64Img = document.getElementById('base64Img'), canvas = document.getElementById('canvas'), ctx = canvas.getContext('2d');
let img = new Image(); img.src = 'off.png';
img.addEventListener( 'load', function () { canvas.width = img.width; canvas.height = img.height;
ctx.drawImage(img, 0, 0);
var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); var data = imgData.data; for (var i = 0; i < data.length; i += 4) { if (data[i + 3] < 255) { data[i] = 255 - data[i]; data[i + 1] = 255 - data[i + 1]; data[i + 2] = 255 - data[i + 2]; data[i + 3] = 255 - data[i + 3]; } } ctx.putImageData(imgData, 0, 0);
getBase64(canvas, function (dataUrl) { const newImg = document.createElement('img'); newImg.src = dataUrl;
base64Img.appendChild(newImg); }); }, false );
function getBase64(canvas, callback) { const dataURL = canvas.toDataURL('image/jpeg');
if (typeof callback !== undefined) { callback(dataURL); } } </script> </body> </html>
|
效果:
从图中可以看出这个方案是有缺陷的,当 png 图片上存在半透明区域时,也会将其填充为黑色。
解决方案来源:Change html canvas black background to white background when creating jpg image from png image
2. 在 canvas 绘制前填充白色背景
代码片段:
1 2 3
| ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, canvas.width, canvas.height);
|
完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head>
<body> <p>Canvas:</p> <canvas id="canvas" style="border: 1px solid #ccc;"></canvas> <br /> <p>Base64转码后的图片:</p> <div id="base64Img"></div>
<script type="text/javascript"> let base64Img = document.getElementById('base64Img'), canvas = document.getElementById('canvas'), ctx = canvas.getContext('2d');
let img = new Image(); img.src = 'off.png';
img.addEventListener( 'load', function () { canvas.width = img.width; canvas.height = img.height;
ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0);
getBase64(canvas, function (dataUrl) { const newImg = document.createElement('img'); newImg.src = dataUrl;
base64Img.appendChild(newImg); }); }, false );
function getBase64(canvas, callback) { const dataURL = canvas.toDataURL('image/jpeg');
if (typeof callback !== undefined) { callback(dataURL); } } </script> </body> </html>
|
效果:
显然,在 canvas 绘制前填充白色背景这种方法,不仅简单,而且对 png 图片的半透明区域填充难看的黑色块。推荐这种解决方案。
解决方案来源:How to fill the whole canvas with specific color