Функции слияния изображений приводят к неверному результату в гибридном приложении на iOS - работает в Safari - PullRequest
0 голосов
/ 03 октября 2018

У меня есть эта функция:

   //Merge images
   mergeImages(sig, bg, outputFormat)
   {

   return new Promise((resolve, reject) => {
     var canvas = <HTMLCanvasElement> document.createElement("canvas");

     var img1 = new Image();
     var img2 = new Image();

     var finalURL = '';

     var canvas = <HTMLCanvasElement> document.createElement('canvas');

     document.getElementById("canvasContainer").appendChild(canvas);

     var context = canvas.getContext('2d');

     img1.src = '';
     img2.src = '';

     img1.addEventListener('load',function() {
          console.log("loaded img1");

          canvas.width = 100;
          canvas.height = 100;
      });

      img2.addEventListener('load',function() {
           console.log("loaded img2");

          context.drawImage(img1, 0, 0, 100, 100);
          context.drawImage(img2, 0, 0, 100, 100);

          finalURL = canvas.toDataURL(outputFormat);

          resolve (finalURL);

          console.log("\n finalURL" + finalURL +"\n");
       });


     setTimeout(function() {
        console.log(img1.complete);
      }, 1000);

      setTimeout(function() {
         console.log(img2.complete);
       }, 1000);

     img1.src = bg;

     img2.src = sig;

  });

}

Эта функция возвращает baseUR dataURL результирующего изображения из комбинации 'img1' и 'img2'.Приложение построено с использованием Ionic (HTML / Typescript).

В браузере Safari после запуска этой функции (вызываемой с помощью нажатия кнопок DOM) я всегда вижу правильное объединенное изображение.

Однако, когда я создаю это приложение для iOS (iPad), результат неверный, и я понятия не имею, почему.Таким образом, в конечном изображении отображается только img2.

Явных ошибок нет.

Любая помощь приветствуется.

Редактировать (добавленные изображения): Правильный вывод в настольных браузерах:

Correct

Неверное изображение на iOS:

Incorrect

1 Ответ

0 голосов
/ 03 октября 2018

Проблема здесь в том, что загрузка обоих изображений обрабатывается неправильно.
Оба будут загружаться асинхронно, и пока ваши коды ожидают каждое событие load, ваш код предполагает, что первое изображение всегда будет загружатьсяfirst.
Но, учитывая природу этих изображений, кажется, что второе изображение (sig) будет более светлым (большинство пикселей прозрачным) и, следовательно, на самом деле, вероятно, будет загружаться быстрее.

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

В эпоху доОбещания, распространенный способ справиться с этим - использовать счетчик, который будет увеличиваться при загрузке каждого изображения и который вызовет обратный вызов oncomplete :

function loadImages(urls, onsuccess, onerror) {
  var counter = 0;
  var imgs = urls.map(function load(url) {
    var img = new Image();
    img.onload = incrementCounter;
    img.src = url;
    img.onerror = onerror;
    return img;
  });
  function incrementCounter(evt) {
    if(++counter === urls.length) {
      onsuccess(imgs);
    }
  }
}

var canvas = document.body.appendChild(document.createElement('canvas'));
var ctx = canvas.getContext('2d');

loadImages([
  // a quite big 3000 x 2000 picture by Arturo Mann
  'https://upload.wikimedia.org/wikipedia/commons/6/65/Sunset_over_The_Pacific_Ocean_at_Acapulco_1.jpg',
  // a small 32x32 image
  'https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png'
  ], oncomplete, onerror);

function loadImages(urls, onsuccess, onerror) {
  var counter = 0;
  var imgs = urls.map(function load(url) {
    var img = new Image();
    img.onload = incrementCounter;
    img.src = url;
    img.onerror = onerror;
    return img;
  });
  function incrementCounter(evt) {
    if(++counter === urls.length) {
      onsuccess(imgs);
    }
    // only for demo
    console.log('successfully loaded', evt.target.src);
  }
}

// our callback
// in here we receive the img elements,
// in the order we did set the urls
// whatever was the first to load
function oncomplete(imgs) {
  canvas.width = imgs[0].width/8;
  canvas.height = imgs[0].height/8;
  ctx.scale(0.125, 0.125);
  ctx.drawImage(imgs[0], 0,0);
  ctx.imageSmoothingEnabled = false;
  ctx.scale(8, 8);
  ctx.drawImage(imgs[1], 176, 100);
}

function onerror(err) {
  console.error('failed to load resources');
}

Но так как вы, похоже, находитесь в контексте, который поддерживает Promises и ES6, вы можете просто обещать загрузку и ждать Promise.all из этих нагрузок.

Promise.all([loadImage(url1), loadImage(url2)])
  .then(oncomplete)
  .catch(onerror);

function loadImage(url) {
  return new Promise((res, rej) => Object.assign(
    new Image(),
    {
      onload: (evt)=>res(evt.target),
      onerror: rej,
      src: url
    }
  )
 );
}

var canvas = document.body.appendChild(document.createElement('canvas'));
var ctx = canvas.getContext('2d');
// a quite big 3000 x 2000 picture by Arturo Mann
var url1 = 'https://upload.wikimedia.org/wikipedia/commons/6/65/Sunset_over_The_Pacific_Ocean_at_Acapulco_1.jpg';
// a small 32x32 image
var url2 = 'https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png';


Promise.all([loadImage(url1), loadImage(url2)])
  .then(oncomplete)
  .catch(onerror);

function loadImage(url) {
  return new Promise((res, rej) => Object.assign(
    new Image(),
    {
      onload: (evt)=>res(evt.target),
      onerror: rej,
      src: url
    }
  )
 );
}

// our callback
// in here we receive the img elements,
// in the order we did set the urls
// whatever was the first to load
function oncomplete(imgs) {
  canvas.width = imgs[0].width/8;
  canvas.height = imgs[0].height/8;
  ctx.scale(0.125, 0.125);
  ctx.drawImage(imgs[0], 0,0);
  ctx.imageSmoothingEnabled = false;
  ctx.scale(8, 8);
  ctx.drawImage(imgs[1], 176, 100);
}

function onerror(err) {
  console.error('failed to load resources');
}
...