Angular 2 , 函数完成前的返回值

Angular 2 , value returned before function complete

在我的 Angular 中,我有一项服务可以在 canvas 上绘制图片并获取其“ImageData”。

这是我的 service.ts:

getColorArray (movie: MovieDb): Observable<any> {
    var img = new Image();
    img.crossOrigin = "Anonymous";
    img.src = "https://www.themoviedb.org/t/p/w342/" + movie.poster;
    var canvas = <HTMLCanvasElement>document.getElementById("canvas");
    var ctx = canvas.getContext("2d");
    canvas.height = 50;
    canvas.width = 30;
    ctx.clearRect(0, 0, canvas.width, canvas.height); 
    ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
    var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    return of(imgData)
  }

还有我的 .ts 使用它的地方:

pixelate(movie) {
    this.getBackgroundColor.getColorArray(movie)
    .subscribe(r => do something with r)

我把它做成一个 Observable,认为 imgData 会在 getImageData() 完成时返回,但它会在 getColorArray() 被调用时立即返回。 如何等待 getImageData() 完成后再返回其值?

问题不是“等待 getImageData”,而是等待图像加载,您需要明确地执行此操作。类似于:

getColorArray (movie: MovieDb): Observable<any> {
  // create an observable
  return new Observable(obs => {
    var img = new Image();
    img.crossOrigin = "Anonymous";
    img.src = "https://www.themoviedb.org/t/p/w342/" + movie.poster;
    
    // once the image loads, draw it and the image data is available synchronously
    var imgLoadListener = e => {
      var canvas = <HTMLCanvasElement>document.getElementById("canvas");
      var ctx = canvas.getContext("2d");
      canvas.height = 50;
      canvas.width = 30;
      ctx.clearRect(0, 0, canvas.width, canvas.height); 
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
      var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
      obs.next(imgData);
      obs.complete();
    };

    img.addEventListener('load', imgLoadListener);
    
    return () => { // clean up after yourself.
      img.removeEventListener('load', imgLoadListener)
    }
  })
}

一种更清洁/更少手动操作的方法是这样,使用 deferfromEvent,这将为您处理一些清理工作并更好地处理错误:

getColorArray (movie: MovieDb): Observable<any> {
  // defer to not create image until subscribe
  return defer(() => {
    var img = new Image();
    img.crossOrigin = "Anonymous";
    img.src = "https://www.themoviedb.org/t/p/w342/" + movie.poster;
    
    return fromEvent(img, 'load').pipe(
      map(() => {
        var canvas = <HTMLCanvasElement>document.getElementById("canvas");
        var ctx = canvas.getContext("2d");
        canvas.height = 50;
        canvas.width = 30;
        ctx.clearRect(0, 0, canvas.width, canvas.height); 
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
        var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        return imgData;
      })
    ); 
  })
}

你可以删除上面的 defer 部分并且它会工作得很好,但是在 observables 中的标准做法是确保它们在订阅之前什么都不做,使用 defer 完成这里。