合并图像时出现间歇性结果

Intermittent results when merging images

我在程序代码中使用事件代码时遇到很多问题。我有一个合并图像的功能,它每秒被调用 20-40 次。有时有效,有时无效。有时 drawImage 在图像加载之前被调用,有时不是。这是代码(它是 class 的方法):

merge(image, pos) {
  if (!pos && !image.pos)
    pos = [0, 0];
  else if (!pos)
    pos = image.pos;

  canvas.element[3].width = this.width;
  canvas.element[4].width = this.width;
  canvas.element[3].height = this.height;
  canvas.element[4].height = this.height;

  canvas.ctx[3].putImageData(this.toImageData(), ...this.pos);
  var image1 = new Image(this.width, this.height);
  image1.onload = function() {/*help pls*/};
  image1.src = canvas.element[3].toDataURL();
  canvas.ctx[4].drawImage(image1, 0, 0);

  canvas.ctx[3].clearRect(0,0,this.width,this.height);

  canvas.ctx[3].putImageData(image.toImageData(), ...pos);
  image1 = new Image(this.width, this.height);
  image1.onload = function() {/*help pls*/};
  image1.src = canvas.element[3].toDataURL();
  canvas.ctx[4].drawImage(image1, 0, 0);

  this.data.set(new Uint32Array(canvas.ctx[4].getImageData(0,0,this.width,this.height).data.buffer));
  return this;
}

我知道我必须在onload活动中写点东西。我觉得我已经尝试了一切。我希望 "seem" 函数对调用者是程序化的。这个函数将被调用,它会做它的事情并 return 结果。我不想输入回调并将我所有漂亮的简单程序代码转换为事件代码。

我尝试使用 while 循环 "wait" 来触发 onload 事件,但这没有用(我讨厌 while 循环!)。它所做的只是崩溃!我尝试将所有后续内容放入 onload 事件中,但我无法在它被更改后立即 returning this

我想我在某处读到您可以使用递归循环。我依稀记得arguments.callee。但是我不知道如何实现这个。

"Javascript is sequential"

在 javascript 中编写事件驱动代码时要知道的一件重要事情是 javascript 中的代码执行是严格按顺序执行的。一次一个函数,一次一个指令。您不能中断 javascript.

的执行

当你写类似

的东西时
function doSomething(){
     var img = new Image();
     img.onload = function(){ // do stuff }; // This can not run until
                                             // your current call is over.
     img.src = "foo.img";
     // lots more code..
     ...
     ...
     ...
     // end of the function
}

//code
doSomething();
// more code.

console.log("Have a nice day"); // last line

// at this point there is no more code
// so javascript returns 
// then it checks if there is anything on the call stack.
// It is only now that onload can run.

当您调用 doSomething 时,该函数将阻止所有其他 javascript。您可以调用函数,但在您退出 doSomething.

之前不会执行任何异步事件

这意味着 onload 事件必须等到 doSomething 函数返回。更准确地说,调用是空的(不再有 returns),因此如果您从 foo() 开始调用 bar(),调用 baf()。直到 foo() 已经返回并且它下面的所有代码都完成了,任何其他代码都可以 运行.

图像加载是异步的,它是在单独的线程上加载的。完成加载后,它不会调用函数 onload,而是将调用放在调用堆栈中。只有一个调用堆栈,本地异步调用(如图像加载、超时、间隔等)对其进行的调用顺序取决于异步完成的时间。

您的代码中发生的情况是,您创建的图像正在被您分配给原始图像变量的新图像所取代。由于没有对原始图像的引用,并且在您退出之前不会发生加载,javascript 假设您不再需要第一个图像并将其转储。

有多种方法可以解决此问题。一个是确保维护对每个新图像的引用。您可以使用 image.addEventListener("load", function(){ //your image is "this'}); 执行此操作,这将保留对原始图像的引用,因为事件侦听器与图像分开存储,不像 image.onload = function(){} 在您将新图像分配给时与图像一起丢失那个变量。

另一种方法是使用闭包来保存对图像的引用。

function doSomething(){
    var img;   

    // when the following function is call the argument
    // image is closed over. This means that it keeps an unique 
    // reference to image every time this function is call.
    // this means that the image is not replaced below
    function addEvent(image){  
        image.onload = function(){}
    }

    img = new Image()
    img.src = "blah.img"
    addEvent(img);  // use function closure to close over the image
                    // thereby maintaining a separate reference to the img

    img = new Image(); // replace the previous image with new
    img.src = "blah1.img"; // because the addEvent function has closed over
                           // the previous img and is holding a separate reference
                           // the image will not be dumped
    addEvent(img); // clos over this one making a second reference via closure

}

doSomething();
// not until completely exited that the onload functions can be removed from the
// call stack and executed.

最简单的方法是自己创建个人引用。

var imageArray = [];
function doSomething(){
    img = new Image()
    img.src = "blah.img"
    img.onload = function(){};
    imageArray.push(img);  // copy the image reference to the array.

    img = new Image();  // create a new image but the previous one
    img.src = "blah.img"  // is still referenced and thus wont be lost.
    img.onload = function(){};
    imageArray.push(img);  // copy the image reference to the array.
}

这样做可能会让您的头脑更清晰一些,但这三种方法都做同样的事情。他们创建一个单独的图像引用,为每个图像存储图像引用的副本。允许图像存在直到当前执行 returns 并且可以触发 onload 事件。

使用数组当然会用图像填充内存,除非您从数组中删除图像引用。但那是另外一回事了。

希望这能解释您的代码出了什么问题,并为您提供修改代码和继续编码的有趣部分而不是令人沮丧的部分所需的内容。