javascript canvas — проверьте, загружены ли изображения, созданные с помощью toDataURL()

Я загружаю изображение в свой массив albImg.

в моем цикле я делаю это:

    for(var j = 1; j < albImg.length; j++){
        if(albImg[j].complete == true && albImg[j].width > 0){
            loadedimages++;
        }
    }

чтобы убедиться, что все мои изображения загружены. Затем я вызываю свою функцию flipImg() следующим образом:

    if(loadedimages == albImg.length-1){
         flipImg();
    }

Затем я переворачиваю изображение и

ctx2.save();
ctx2.scale(-1, 1);
for (var i = RszSpriteCount; i < sprites.length; i++) {
    ctx2.drawImage(albImg[sprites[i][0]], sprites[i][1], sprites[i][2], sprites[i][3], sprites[i][4], 0 - (sprites[i][1] + sprites[i][3]), sprites[i][2], sprites[i][3], sprites[i][4]);
}
ctx2.restore();
var flipSz = albImg.length;
albImg[flipSz] = new Image();
albImg[flipSz].src = cnv2.toDataURL();

Вот где моя проблема начинается.

Новое изображение, которое я создал — albImg[5] — не может быть отображено, пока оно не загружено. Но он создается так, как будто он уже загружен.

То есть:

albImg[5].width уже установлен (до 750), прежде чем я смогу его отобразить. Для albImg[5].complete установлено значение true, прежде чем я смогу его отобразить. albImg[5].onload = ctx.drawImage(albImg[5], 0, 0); попытается нарисовать изображение до его загрузки.

Как я могу проверить, действительно ли мое перевернутое изображение загружено, прежде чем отображать его? в яваскрипте?

(из-за обстоятельств я не использую jQuery для этого)

Пожалуйста помоги.


person Daniel Bengtsson    schedule 25.06.2017    source источник
comment
Почему onload пытается отобразить до загрузки изображения? Вызов onload означает, что изображение загружено.   -  person 11thdimension    schedule 26.06.2017


Ответы (1)


Ваша основная ошибка заключается в том, как вы устанавливаете обработчик событий onload:

albImg[5].onload = ctx.drawImage(albImg[5], 0, 0)  

установит возвращаемое значение drawImage() (undefined) для слушателя onload.

То, что вы хотите, это

albImg[5].onload = e => ctx.drawImage(albImg[5], 0, 0);  

or

albImg[5].onload = function(){ ctx.drawImage(this, 0, 0) };

Если для свойств complete и width установлено значение true, это связано с тем, что хотя загрузка изображения всегда асинхронна, в вашем случае изображение, вероятно, уже кэшировано HTTP.
Поскольку загрузка HTTP и выполнение javascript не выполняется в одном и том же потоке, возможно, что изображение фактически загружается до того, как браузер вернет его свойства.

Но даже в этом случае событие onload сработает (хотя лучше всего установить его до src).

var cacher = new Image();
cacher.onload = function(){
  var img = new Image();
  img.onload = function(){
    console.log('"onload" fires asynchronously even when cached');
    };
  img.src = c.toDataURL();
  console.log('cached : ', img.complete, img.naturalWidth);
  }
cacher.src = c.toDataURL();
console.log('before cache :', cacher.complete, cacher.naturalWidth);
<canvas id="c"></canvas>

Поэтому при работе с новым изображением (а не с изображением в html-разметке) всегда просто слушайте его событие onload.


Теперь, с той небольшой информацией, которую вы дали в своем вопросе, может показаться, что вам даже не нужны ни эти изображения, ни иметь дело с какими-либо их загрузками (кроме спрайтов, конечно), поскольку вы можете напрямую и синхронно вызывать ctx.drawImage(CanvasElement, x, y) .

const ctx = c.getContext('2d');
ctx.moveTo(0, 0);
ctx.lineTo(300, 75);
ctx.lineTo(0, 150);
ctx.fillStyle = 'rgba(120,120,30, .35)';
ctx.fill();

const flipped = c.cloneNode(); // create an offscreen canvas
const f_ctx = flipped.getContext('2d');
f_ctx.setTransform(-1, 0,0,1, c.width, 0);// flip it
f_ctx.drawImage(c,0,0);// draw the original image

// now draw this flipped version on the original one just like an Image.
ctx.drawImage(flipped, 0,0);
// again in 3s
setTimeout(()=>ctx.drawImage(flipped, 150,0), 3000);
<canvas id="c"></canvas>

person Kaiido    schedule 26.06.2017
comment
Я решил это благодаря вам. И для тех, кто увидит это в будущем: я добавил ваш код, и он не сработал, но я понял, что изображение, рисуемое на холсте, также было сгенерировано toDataUrl(). Это изображение не всегда загружалось на холст, на котором я основывал свое второе изображение. Пустой холст = пустое изображение. Однако реализация вашей загрузки на первом изображении решила проблему. Теперь картинка всегда будет загружаться, поэтому загруженное изображение всегда будет содержать перевернутое изображение. - person Daniel Bengtsson; 26.06.2017
comment
И причина, по которой я сначала загружаю его в изображение, заключается в том, что я полагаю, что, переворачивая изображение только один раз, а не несколько раз в каждом цикле, я сэкономлю некоторую вычислительную мощность и, надеюсь, приведу к меньшему использованию батареи (это для мобильного приложения) . Имеет ли это смысл? Может быть, я просто слишком много думал об этом. - person Daniel Bengtsson; 26.06.2017
comment
@DanielBengtsson, это имеет смысл, но это не лучший способ, нет. Если это действительно просто переворачивание изображения (или холста, который действует как он), то он будет потреблять меньше памяти, чтобы просто изменить матрицу преобразования вашего видимого холста и использовать drawImage. Если бы это была полная композиция, то было бы лучше использовать закадровый холст (как я сделал в ответе) и сохранить ссылку на этот закадровый холст. Преобразование в изображение всегда тяжело для памяти. Браузер должен будет сохранить строковое представление файла b64 + декодированное изображение. - person Kaiido; 26.06.2017