【问题标题】:Change html canvas black background to white background when creating jpg image from png image从png图像创建jpg图像时将html画布黑色背景更改为白色背景
【发布时间】:2015-11-16 13:35:08
【问题描述】:

我有一个canvas,其中加载了png 图像。我通过.toDataURL() 方法得到它的jpg base64 字符串,如下所示:

 $('#base64str').val(canvas.toDataURL("image/jpeg"));

png 图像的透明部分在新的jpg 图像中显示为黑色。

有什么办法可以把这种颜色变成白色吗?提前致谢。

【问题讨论】:

    标签: javascript html canvas base64 png


    【解决方案1】:

    出现这种变黑是因为“图像/jpeg”转换涉及将所有画布像素的 alpha 设置为完全不透明 (alpha=255)。问题是透明画布像素是彩色的fully-black-but-transparent。因此,当您将这些黑色像素变为不透明时,结果就是变黑的 jpeg。

    解决方法是将所有非透明画布像素手动更改为所需的白色而不是黑色。

    这样,当它们变得不透明时,它们将显示为白色而不是黑色像素。

    方法如下:

    // 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);
    

    【讨论】:

    • 谢谢你。在将黑色填充的图像传递给后面的代码后,我可以在后面的 ASP.NET 代码中执行此操作吗?
    • 不客气!在将图像传递给代码隐藏之前,您应该在客户端处理图像。那是因为 html5 画布只适用于 javascript,而 JS 在代码隐藏中本身不可用(至少在使用 html5 画布所需的方式中不可用)。
    • 我不敢相信这是解决这个问题的唯一方法。必须有某种方法来更改默认颜色。我将不得不对此进行研究。
    【解决方案2】:

    这个答案有点长,但我发现它更“正确”,因为它处理这些事情而不直接修改原始画布数据。我发现这是一个非常混乱且理论上不令人满意的解决方案。有内置函数可以实现这一点,应该使用它们。这是我找到/窃取的解决方案:

    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;
    }
    

    基本上,您创建一个白色背景图像并将其置于画布下方,然后打印。这个函数主要是从某人的博客中抄袭的,但它需要一些修改——例如实际获取上下文——并直接从我的(工作)代码中复制,所以只要你的画布元素具有 id 'canvas',你应该能够复制/粘贴它并让它工作。

    这是我修改的博客文章:

    http://www.mikechambers.com/blog/2011/01/31/setting-the-background-color-when-generating-images-from-canvas-todataurl/

    我的函数的最大优势在于它输出到 jpeg 而不是 png,这更可能在 chrome 中运行良好,它的 dataurl 限制为 2MB,它实际上抓住了上下文,这是一个明显的原函数省略。

    【讨论】:

      【解决方案3】:

      在这篇文章和这篇文章专门花了很多时间之后,这些解决方案有点奏效,我只是无法让画布看起来正确。无论如何,我在其他地方找到了这个解决方案,并想在这里发布它,以防它帮助其他人花费数小时试图将黑色背景变为白色并看起来像原来的那样。

      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");
      }
      

      【讨论】:

      • 谢谢,这条评论让我看到我所需要的几乎就是canvas.fillStyle = '#FFF';
      • 花了几个小时后我得到了这个答案。这应该在顶部。 . .
      • 优秀。我确实在这段代码上花了几个小时。正如吉山所说,这应该在顶部。谢谢
      【解决方案4】:

      标记答案是正确的,但是当图片应用了一些抗锯齿时,导出的图像不会像它应该的那样好(主要是文本)。我想加强他的解决方案:

      // 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);
      

      【讨论】:

        【解决方案5】:

        如果您想移动到只有全透明像素的白色,只需检查 (data[i+3]==0) 而不是 (data[i+3]

        【讨论】:

          【解决方案6】:

          为什么不将其保存为 PNG?

          canvas.toDataURL("image/png");
          

          【讨论】:

            【解决方案7】:
            // 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];
                }
            

            【讨论】:

            • 我建议你添加一个解释如何解决这个问题
            【解决方案8】:

            这是我调整照片大小并处理黑色透明背景问题的函数:

            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);
                });
            },
            

            那是一个 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);
            

            我在这里的解决方案是结合了大约 74 个不同的 StackOverflow 答案,这些答案与在客户端调整图像大小有关,最终的老板是处理透明的 PNG 文件。

            如果没有 Laereom 的回答,我的回答是不可能的。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2016-08-15
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-12-13
              • 2015-10-21
              • 1970-01-01
              • 2020-07-15
              相关资源
              最近更新 更多