Ich habe ein canvas, das mit einem png Bild geladen ist. Ich erhalte seine jpg base64-Zeichenfolge mit der .toDataURL() Methode wie folgt:

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

Die transparenten Teile des png Bilds werden im neuen jpg Bild jedoch schwarz angezeigt.

Gibt es Lösungen, um diese Farbe in Weiß zu ändern? Danke im Voraus.

13
Payam Sh 22 Aug. 2015 im 22:42

7 Antworten

Beste Antwort

Diese Schwärzung tritt auf, weil bei der Konvertierung von 'Bild / JPEG' das Alpha aller Leinwandpixel auf vollständig undurchsichtig gesetzt wird (Alpha = 255). Das Problem ist, dass transparente Leinwandpixel fully-black-but-transparent gefärbt sind. Wenn Sie diese schwarzen Pixel undurchsichtig machen, ist das Ergebnis ein geschwärztes JPEG.

Die Problemumgehung besteht darin, alle nicht undurchsichtigen Leinwandpixel manuell in die gewünschte weiße Farbe anstelle von Schwarz zu ändern.

Auf diese Weise erscheinen sie, wenn sie undurchsichtig gemacht werden, als weiße statt als schwarze Pixel.

Hier ist wie:

// 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);
14
markE 22 Aug. 2015 im 20:04

Diese Antwort ist etwas länger, aber ich finde sie "korrekter", da sie diese Dinge behandelt, ohne die Rohdaten der Leinwand direkt zu ändern. Ich finde das eine ziemlich chaotische und theoretisch unbefriedigende Lösung. Es gibt eingebaute Funktionen, um dies zu erreichen, und sie sollten verwendet werden. Hier ist die Lösung, die ich gefunden / gestohlen habe:

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

Grundsätzlich erstellen Sie ein weißes Hintergrundbild, legen es unter die Leinwand und drucken es dann aus. Diese Funktion wird größtenteils aus einem Blog von jemandem plagiiert, erfordert jedoch einige Änderungen - z. B. das Abrufen des Kontexts - und wird direkt aus meinem (Arbeits-) Code kopiert, sofern Ihr Canvas-Element die ID 'canvas' hat. Sie sollten in der Lage sein, es zu kopieren / einzufügen und es funktionieren zu lassen.

Dies ist der Blog-Beitrag, von dem ich ihn geändert habe:

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

Der große Vorteil meiner Funktion gegenüber dieser ist, dass sie auf JPEG anstatt auf PNG ausgegeben wird, was in Chrome mit einer Datenbeschränkung von 2 MB eher gut funktioniert, und dass sie tatsächlich den Kontext erfasst, was eine eklatante Auslassung in der ursprüngliche Funktion.

11
Laereom 25 Mai 2017 im 07:09

Die Antwort der Markierungen ist korrekt, aber wenn auf ein Bild ein Antialiasing angewendet wird, ist das exportierte Bild nicht so gut wie es sein sollte (hauptsächlich Text). Ich möchte seine Lösung verbessern:

// 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);
2
David Vass 19 Jän. 2016 im 09:06

Hier ist meine Funktion, die die Größe eines Fotos ändert und das Problem des schwarzen transparenten Hintergrunds behandelt:

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

Dies ist eine Vue JS-Instanzmethode, die wie folgt verwendet werden kann:

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

Meine Lösung hier ist eine Kombination von ungefähr 74 verschiedenen StackOverflow-Antworten, die sich auf die clientseitige Größenänderung von Bildern beziehen, und der letzte Chef bestand darin, transparente PNG-Dateien zu verarbeiten.

Meine Antwort wäre ohne Laereoms Antwort hier nicht möglich.

0
agm1984 3 Okt. 2019 im 20:13

Nachdem ich viel Zeit mit diesem und diesem Beitrag verbracht hatte und diese Lösungen irgendwie funktionierten, war zu erwarten, dass ich die Leinwand einfach nicht richtig aussehen lassen konnte. Wie auch immer, ich habe diese Lösung woanders gefunden und wollte sie hier veröffentlichen, falls sie jemand anderem hilft, stundenlang zu versuchen, den schwarzen Hintergrund weiß zu machen und wie das Original auszusehen.

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");
}
2
TJ972 5 Okt. 2018 im 20:32
// 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];
    }
0
Magige Daniel 31 Okt. 2016 im 09:37

Wenn Sie nur zu vollständig transparenten Pixeln nach Weiß wechseln möchten, suchen Sie einfach nach (Daten [i + 3] == 0) anstelle von (Daten [i + 3] <255).

2
manwell77 11 Jän. 2017 im 12:09