【问题标题】:Handling Async Calls with Canvas and ForEach使用 Canvas 和 ForEach 处理异步调用
【发布时间】:2015-04-27 12:00:15
【问题描述】:

我正在尝试了解如何利用 Angular 的 $q 库来显示基于画布绘图的图像,然后使用 .toDataURL() 进行转换;

基本上我想:

  1. 循环遍历图像 ($scope.targetImages)
  2. 在画布上绘制它们
  3. 使用.toDataURL()将它们转换为图像并将其存储在($scope.outputImages)中;
  4. 使用 ng-repeat 显示图像

问题是,函数 .toDataURL() 可能需要一些时间才能执行,从而导致第 4 步的调用延迟,因此没有显示任何内容。

我尝试了以下方法,但在所有图像转换之前仍然可以解决。

正如我现在所拥有的,当我第二次调用drawCanvas() 时,图像就会显示出来。

    // 1
    $scope.targetImages= {}

    drawCanvas().then(function(data){
      console.log("done: " + new Date())
      console.log(data)
      $scope.outputImages = data;
      $scope.previewMode = false; // switch views, display canvas, remove preview
    });


    function drawCanvas() {

    var defer = $q.defer();

    var targetImages = {} 
    angular.forEach($scope.targetImages , function(imageObj, key) {

      var canvas = document.getElementById("canvas");
      var ctx = canvas.getContext("2d");
      var img = new Image();
      img.src = imageObj.nativeURL;
      img.onload = start

      // 2
      function start() {

        ctx.drawImage(img, 0, 0, img.width, img.height);

        outputImages[key] = {
          IID: key,
          dataURL: canvas.toDataURL()
        }

      } // start

    }); // for loop target images

    defer.resolve(outputImages);
    return defer.promise;

    } // draw canvas

并显示为:

<img ng-show="!previewMode" ng-src="{{image.dataURL || ''}}" style="width: 100vw; max-width: 600px;">

【问题讨论】:

    标签: javascript angularjs html canvas asynchronous


    【解决方案1】:

    首先,定义一个将图像绘制到画布并返回结果的函数:

    function drawToCanvas(nativeURL) {
        return $q(function (resolve) {
            var canvas = document.getElementById("canvas");
            var ctx = canvas.getContext("2d");
            var img = new Image();
    
            img.src = nativeURL;
            img.onload = function () {
                ctx.drawImage(img, 0, 0, img.width, img.height);
    
                resolve(canvas.toDataURL());
            };
        });
    }
    

    那么解决方案就变成了:

    $scope.targetImages = [];
    
    function drawCanvas() {
        // pResults is an array of promises
        var pResults = $scope.targetImages.map(function (imageObj) {
            return drawToCanvas(imageObj.nativeURL);
        });
    
        return $q.all(pResults);
    }
    
    drawCanvas().then(function(data) {
        // data is an array of imageUrls
        console.log("done: " + new Date())
        console.log(data)
        $scope.outputImages = data;
        $scope.previewMode = false; 
        // switch views, display canvas, remove preview
    });
    

    为简化起见,我已将 $scope.targetImages$scope.outputImages 更改为数组而不是对象,但如果您需要的话,返回为它们使用对象应该不会太难。

    【讨论】:

    • 感谢您的反馈,看起来不错。我要试试。只是一个评论,angular.forEach 在哪里,它会随着解析而改变吗?
    • 我已将angular.forEach 替换为Array#map,因为这是将一组事物映射到另一组事物的最简洁方式。
    • @JohnAndrews 不,不需要循环。 Array#map 负责迭代。
    • 但是 imageObj 长什么样子呢?它是一个只有属性 .nativeUrl 的对象吗? Console.log(data) 结果 []
    • @JohnAndrews 是的,假设$scope.targetImages 将是具有.nativeURL 属性的对象数组。在上面的代码中,$scope.targetImages 被初始化为一个空数组,因此您需要将一些值放入其中以便在最后取出值。
    猜你喜欢
    • 2015-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-17
    • 2021-04-16
    • 1970-01-01
    • 1970-01-01
    • 2016-03-30
    相关资源
    最近更新 更多