Как конвертировать несколько изображений в base64 с помощью canvas

Я храню изображения в кодировке base64, и на данный момент я могу создать только один код (я пытаюсь создать два, но, похоже, второй перезаписывается). Я не понимаю общую концепцию рисования на холсте, поэтому я считаю, что это корень моей проблемы при попытке решить эту проблему.

текущее поведение: он хранит один и тот же DataUrl в локальном хранилище дважды. Это действительно регистрирует правильную информацию. зеленый значок хранится, но не красный

Как мне закодировать несколько изображений base64 с помощью canvas?

HTML:

    <head>
        ...
        <link id="favicon" rel="icon" src="/assets/favicon-red-16x16.ico.png">
    </head>
    <body>
        ...
        <!-- hidden images to store -->
        <img id="favicon-green" rel="icon" src="/assets/favicon-green-16x16.ico.png" width="16" height="16" />
        <img id="favicon-red" rel="icon" src="/assets/favicon-red-16x16.ico.png" width="16" height="16" />
        ...
    </body>

ЯШ:

    // cache images
    function storeImages() {

        // my sorry attempt to create two canvas elements for two image encodings
        var canvasGreen = document.createElement('canvas');
        var canvasRed = document.createElement('canvas');

        // painting both images
        var ctxGreen = canvasGreen.getContext('2d');
        var ctxRed = canvasRed.getContext('2d');

        // getting both images from DOM
        var favGreen = document.getElementById('favicon-green');
        var favRed = document.getElementById('favicon-red');

        // checking if images are already stored
        var base64Green = localStorage.getItem('greenFavicon');
        var base64Red = localStorage.getItem('redFavicon');
        console.log('storing...')

        if (base64Green == null && window.navigator.onLine || base64Red == null && window.navigator.onLine) {
            ctxGreen.drawImage(favGreen, 0, 0);
            ctxRed.drawImage(favRed, 0, 0);

            // getting images (the DataUrl is currently the same for both)
            base64Green = canvasGreen.toDataURL();
            base64Red = canvasRed.toDataURL();
            localStorage.setItem('greenFavicon', base64Green);
            localStorage.setItem('redFavicon', base64Red);
            console.log("are they equal : ", base64Green == base64Red); // returns true
        }
    }
    storeImages();

1 ответ

Я не вижу ничего особенно плохого в вашем коде. Если код не является прямым копированием и вставкой, я бы внимательно посмотрел его, чтобы убедиться, что вы не переключаете красный и зеленый.

Там не должно быть никаких удивительных механизмов, когда дело доходит до преобразования холстов в URL-адреса данных.

Вот быстрый пример двух:

const a = document.createElement('canvas');
const b = document.createElement('canvas');

const aCtx = a.getContext('2d');
const bCtx = b.getContext('2d');

aCtx.fillStyle = '#000';
aCtx.fillRect(0, 0, a.width, a.height);

const aUrl = a.toDataURL();
const bUrl = b.toDataURL();

console.log(aUrl == bUrl, aUrl, bUrl);
console.log('First difference index:', Array.prototype.findIndex.call(aUrl, (aChar, index) => aChar !== bUrl[index]));

Обратите внимание, что они разные. Тем не менее, обратите внимание, что они также очень похожи, и вам придется пройти довольно далеко, чтобы начать видеть различия (в моем примере, персонаж 70). Я бы дважды проверил, что они на самом деле одинаковы (сравнивая их, как я). Может быть, это выглядит так же.

Еще одна вещь, которую вы можете сделать, которая больше относится к стилю кода, но также может помочь со случайными зелеными и красными смешиваниями, - это создать функцию для сохранения только одного, а затем вызвать его дважды.

const saveImage = (imageId, key) => {
    if (localStorage.getItem(key)) {
      return; // already saved
    }

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    const image = document.getElementById(imageId);
    ctx.drawImage(image, 0, 0);

    if (window.navigator.onLine) {
      localStorage.setItem(key, canvas.toDataURL());
    }
}

saveImage('favicon-green', 'greenFavicon');
saveImage('favicon-red', 'redFavicon');

Это не только очищает ваш код и делает его СУХИМЫМ, но и помогает избежать случайного смешения красного и зеленого в вашей функции.


После некоторых комментариев я понял, что вы пытаетесь нарисовать изображения на холсте до их загрузки. Это заставит его рисовать пустые изображения, но в остальном ведет себя так, как будто работает нормально.

Вы можете быстро проверить это, запустив консоль:

console.log(image.width === 0);

после установки переменной изображения. Если значение равно true, то изображение еще не загружено (перед загрузкой изображения будут иметь ширину и высоту 0). Вы должны обязательно дождаться загрузки изображения, прежде чем пытаться его сохранить.

Лучший способ сделать это с addEventListener():

document.getElementById('favicon-green').addEventListener('load', () => {
  saveImage('favicon-green', 'greenFavicon');
});

Есть еще одна загвоздка в том, что если изображение каким-то образом уже загружено к моменту запуска кода, оно никогда не сработает. Вам также нужно посмотреть на ширину изображения. Вот функция, которая делает все это для вас, и возвращает Promise так что вы знаете, что это сделано:

const saveImage = (imageId, key) => new Promise(resolve => {
    if (localStorage.getItem(key)) {
      return resolve(); // already saved
    }

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    const image = document.getElementById(imageId);
    
    const onImageLoaded = () => {
      ctx.drawImage(image, 0, 0);
      
      if (window.navigator.onLine) {
        localStorage.setItem(key, canvas.toDataURL());
      }
      
      resolve();
    }
    
    if (image.width > 0) {
      onImageLoaded();
    } else {
      image.addEventListener('load', onImageLoaded);
    }
});

saveImage('favicon-green', 'greenFavicon').then(() => console.log('saved'));

Другие вопросы по тегам