javascript 从png图像创建jpg图像时将html画布黑色背景更改为白色背景
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32160098/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Change html canvas black background to white background when creating jpg image from png image
提问by Payam Sh
I have a canvas
which is loaded with a png
image. I get its jpg
base64 string by .toDataURL()
method like this:
我有一个canvas
加载了png
图像的图像。我jpg
通过.toDataURL()
这样的方法得到它的base64 字符串:
$('#base64str').val(canvas.toDataURL("image/jpeg"));
But the transparent parts of the png
image are shown black in the new jpg
image.
但是png
图像的透明部分在新jpg
图像中显示为黑色。
Any solutions to change this color to white? Thanks in advance.
有什么解决方案可以将此颜色更改为白色?提前致谢。
回答by markE
This blackening occurs because the 'image/jpeg' conversion involves setting the alpha of all canvas pixels to fully opaque (alpha=255). The problem is that transparent canvas pixels are colored fully-black-but-transparent
. So when you turn these black pixels opaque, the result is a blackened jpeg.
发生这种变黑是因为“图像/jpeg”转换涉及将所有画布像素的 alpha 设置为完全不透明 (alpha=255)。问题是透明画布像素是彩色的fully-black-but-transparent
。因此,当您将这些黑色像素变为不透明时,结果是变黑的 jpeg。
The workaround is to manually change all non-opaque canvas pixels to your desired white color instead of black.
解决方法是手动将所有非不透明画布像素更改为所需的白色而不是黑色。
That way when they are made opaque they will appear as white instead of black pixels.
这样,当它们变得不透明时,它们将显示为白色而不是黑色像素。
Here's how:
就是这样:
// change non-opaque pixels to white
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+1]=255;
data[i+2]=255;
data[i+3]=255;
}
}
ctx.putImageData(imgData,0,0);
回答by Laereom
This answer is a bit longer, but I find it to be more 'correct' in that it handles these things without directly modifying raw canvas data. I find that to be a pretty messy and theoretically unsatisfying solution. There are built in functions to achieve that, and they ought to be used. Here is the solution I found/pilfered:
这个答案有点长,但我发现它更“正确”,因为它可以在不直接修改原始画布数据的情况下处理这些事情。我发现这是一个非常混乱且理论上不令人满意的解决方案。有内置函数可以实现这一点,并且应该使用它们。这是我发现/窃取的解决方案:
function canvasToImage(backgroundColor){
var context = document.getElementById('canvas').getContext('2d');
canvas = context.canvas;
//cache height and width
var w = canvas.width;
var h = canvas.height;
var data;
//get the current ImageData for the canvas.
data = context.getImageData(0, 0, w, h);
//store the current globalCompositeOperation
var compositeOperation = context.globalCompositeOperation;
//set to draw behind current content
context.globalCompositeOperation = "destination-over";
//set background color
context.fillStyle = backgroundColor;
//draw background / rect on entire canvas
context.fillRect(0,0,w,h);
//get the image data from the canvas
var imageData = this.canvas.toDataURL("image/jpeg");
//clear the canvas
context.clearRect (0,0,w,h);
//restore it with original / cached ImageData
context.putImageData(data, 0,0);
//reset the globalCompositeOperation to what it was
context.globalCompositeOperation = compositeOperation;
//return the Base64 encoded data url string
return imageData;
}
Basically, you create a white background image and underlay it under the canvas and then print that. This function is mostly plagiarized from someone's blog, but it required a bit of modification -- such as actually getting the context -- and copied directly from my (working) code, so as long as your canvas element has the id 'canvas', you should be able to copy/paste it and have it work.
基本上,您创建一个白色背景图像并将其放在画布下,然后打印它。这个函数主要是从某人的博客中抄袭而来,但它需要一些修改——比如实际获取上下文——并直接从我的(工作)代码中复制,只要你的画布元素具有 id 'canvas',你应该能够复制/粘贴它并让它工作。
This is the blog post I modified it from:
这是我修改的博客文章:
The big advantage of my function over this is that it outputs to jpeg instead of png, which is more likely to work well in chrome, which has a dataurl limit of 2MB, and it actually grabs the context, which was a glaring omission in the original function.
我的函数在此方面的一大优势是它输出为 jpeg 而不是 png,后者更有可能在 chrome 中运行良好,其 dataurl 限制为 2MB,并且它实际上获取了上下文,这是一个明显的遗漏原来的功能。
回答by TJ972
After spending a lot of time on this and this post specifically, and these solutions kinda worked expect I just couldn't get the canvas to look right. Anyway I found this solution elsewhere and wanted to post it here incase it helps someone else from spending hours trying to get the black background to white and look like the original.
特别是在这个和这个帖子上花了很多时间之后,这些解决方案有点奏效,我只是无法让画布看起来正确。无论如何,我在其他地方找到了这个解决方案,并想在这里发布它,以防它帮助其他人花费数小时试图将黑色背景变为白色并看起来像原始背景。
public getURI(): string {
let canvas = <HTMLCanvasElement>document.getElementById('chartcanvas');
var newCanvas = <HTMLCanvasElement>canvas.cloneNode(true);
var ctx = newCanvas.getContext('2d');
ctx.fillStyle = "#FFF";
ctx.fillRect(0, 0, newCanvas.width, newCanvas.height);
ctx.drawImage(canvas, 0, 0);
return newCanvas.toDataURL("image/jpeg");
}
回答by manwell77
If you want to move to white only full transparent pixels just check for (data[i+3]==0) instead of (data[i+3]<255).
如果您只想将全透明像素移至白色,只需检查 (data[i+3]==0) 而不是 (data[i+3]<255)。
回答by David Vass
Marks answer is correct, but when a picture has some antialiasing applied, the exported image won't be as good as it should be (mainly text). I would like to enhance his solution:
Marks 回答是正确的,但是当图片应用了一些抗锯齿时,导出的图像不会像它应该的那样好(主要是文本)。我想增强他的解决方案:
// change non-opaque pixels to white
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);
回答by Magige Daniel
// change non-opaque pixels to white
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];
}
回答by agm1984
Here is my function that resizes a photo and handles the black transparent background problem:
这是我调整照片大小并处理黑色透明背景问题的函数:
resizeImage({ file, maxSize, backgroundColor }) {
const fr = new FileReader();
const img = new Image();
const dataURItoBlob = (dataURI) => {
const bytes = (dataURI.split(',')[0].indexOf('base64') >= 0)
? window.atob(dataURI.split(',')[1])
: window.unescape(dataURI.split(',')[1]);
const mime = dataURI.split(',')[0].split(':')[1].split(';')[0];
const max = bytes.length;
const ia = new Uint8Array(max);
for (let i = 0; i < max; i += 1) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ia], { type: mime });
};
const resize = () => {
// create a canvas element to manipulate
const canvas = document.createElement('canvas');
canvas.setAttribute('id', 'canvas');
const context = canvas.getContext('2d');
// setup some resizing definitions
let { width, height } = img;
const isTooWide = ((width > height) && (width > maxSize));
const isTooTall = (height > maxSize);
// resize according to `maxSize`
if (isTooWide) {
height *= maxSize / width;
width = maxSize;
} else if (isTooTall) {
width *= maxSize / height;
height = maxSize;
}
// resize the canvas
canvas.width = width;
canvas.height = height;
// place the image on the canvas
context.drawImage(img, 0, 0, width, height);
// get the current ImageData for the canvas
const data = context.getImageData(0, 0, width, height);
// store the current globalCompositeOperation
const compositeOperation = context.globalCompositeOperation;
// set to draw behind current content
context.globalCompositeOperation = 'destination-over';
// set background color
context.fillStyle = backgroundColor;
// draw background / rect on entire canvas
context.fillRect(0, 0, width, height);
// get the image data from the canvas
const imageData = canvas.toDataURL('image/jpeg');
// clear the canvas
context.clearRect(0, 0, width, height);
// restore it with original / cached ImageData
context.putImageData(data, 0, 0);
// reset the globalCompositeOperation to what it was
context.globalCompositeOperation = compositeOperation;
// return the base64-encoded data url string
return dataURItoBlob(imageData);
};
return new Promise((resolve, reject) => {
if (!file.type.match(/image.*/)) {
reject(new Error('VImageInput# Problem resizing image: file must be an image.'));
}
fr.onload = (readerEvent) => {
img.onload = () => resolve(resize());
img.src = readerEvent.target.result;
};
fr.readAsDataURL(file);
});
},
That is a Vue JS instance method that can be used like this:
这是一个 Vue JS 实例方法,可以像这样使用:
// this would be the user-uploaded file from the input element
const image = file;
const settings = {
file: image,
maxSize: 192, // to make 192x192 image
backgroundColor: '#FFF',
};
// this will output a base64 string you can dump into your database
const resizedImage = await this.resizeImage(settings);
My solution here is a combination of about 74 different StackOverflow answers related to resizing images client-side, and the final boss was to handle transparent PNG files.
我的解决方案是大约 74 个与客户端调整图像大小相关的不同 StackOverflow 答案的组合,最终的老板是处理透明的 PNG 文件。
My answer would not be possible without Laereom's answer here.
如果没有 Laereom 在这里的回答,我的回答是不可能的。