【问题标题】:Angular directive: rescale images, update when ngSrc changesAngular 指令:调整图像大小,在 ng Src 更改时更新
【发布时间】:2014-11-08 11:24:23
【问题描述】:

我有一个 Angular 指令,它当前在触发加载事件时执行它的工作:

.directive("testDir", [
    function() {
        return {
            link: function(scope, elm, attrs) {
                    ...
                return elm.one("load", function() {
                    return ...
                });
            }
        };
    }
]);

该指令被添加到<img> 标记,该标记源自ng-src。 该指令应该缩小图像的大小,以在移动设备上获得更好的性能。

现在我的问题是:我希望指令函数不是在.one() 时间触发,而是在每次ng-src 的链接变量(通过{{}})发生变化之后。我怎么能意识到这一点?

我希望指令尽可能独立,因此我希望它没有底层控制器的显式变量名称。

【问题讨论】:

  • attrs.$observe 应该可以帮到你。

标签: javascript angularjs angularjs-directive html5-canvas


【解决方案1】:

JavaScript:

.directive(
  "testDir", [
    function() {
      return {
        scope: {
          imgSrc: '='
        },

        link: function(scope, elm, attrs) {
          scope.$watch(
            'imgSrc', function(newVal) {
              // here you can update whatever you want
            }
          );
        }
      };
    }
  ]
);

HTML:

<img ng-src="{{scopeModel}}" test-dir img-src="scopeModel"/>

【讨论】:

    【解决方案2】:

    编辑 2

    好吧,这里是画布而不是 css。你应该明白了。因为我使用了两个参数 - percent 和 url,所以我把它做成了一个完整的元素指令:

    script.js

    var myApp = angular.module('myApp', []);
    
    myApp.controller("MyCtrl", function($scope) {
      $scope.i = 0;
      $scope.urls = ["_71832498_71825880.jpg",
        "0411_hubble_970-630x420.jpg"
      ]
      $scope.changePic = function() {
        if ($scope.i == 0) {
          $scope.i = 1;
          return;
        }
        if ($scope.i == 1) {
          $scope.i = 0;
          return;
        }
      }
    })
    
    myApp.directive("scaleImage", [
      '$http',
      function($http) {
        return {
          restrict: 'E',
          scope: {
            percent: '&',
            url: '&',
          },
          template: '<img ng-src="{{ src }}" />',
          link: function (scope, elm, attrs) {
            var img;
    
            scope.$watch(function () {
              // Because we're returning an object we need to deep watch.  Do this sparingly.
              // Doing this on large arrays or objects can be ill-performant, but I think this
              // is fine here.
              return {
                percent: scope.percent(),
                url: scope.url(),
              }
            }, function (value) {
              if (!value.percent || !value.url) {
                return;
              }
    
              // Remove the old one, if any.  Should hopefully fix any memory leaks related to
              // creating an element.
              if (img) {
                  img.remove();
              }
    
              img = angular.element('<img style="display: none" src="' + value.url + '">');
              elm.append(img);
    
              // This will give it enough time to load the image and render the element 
              // so that it has a height and whatnot; may not be needed.  Was needed in plunker
              // at some point; can't tell on localhost, doesn't seem to hurt.
              $http.get(value.url).then(function () {
                var canvas, ctx, neededHeight, neededWidth;
                neededHeight = img[0].naturalHeight * value.percent / 100;
                neededWidth = img[0].naturalWidth * value.percent / 100;
                canvas = document.createElement("canvas");
                canvas.width = neededWidth;
                canvas.height = neededHeight;
                ctx = canvas.getContext("2d");
                ctx.drawImage(img[0], 0, 0, neededWidth, neededHeight);
                scope.src = canvas.toDataURL("image/jpeg");
              });
    
            }, true); // true to deep watch
          }
        };
      }
    ]);
    

    index.html

    <!DOCTYPE html>
    <html>
    
    <head>
      <script src="angular.js"></script>
      <script src="script.js"></script>
    </head>
    
    <body>
      <div ng-app="myApp" ng-controller="MyCtrl">
        <input type="number" ng-model="percent" ng-init="percent = 4">
        <scale-image url="urls[i]" percent="percent"></scale-image>
        <button ng-click="changePic()">Change Picture</button>
      </div>
    </body>
    
    </html>
    

    编辑

    http://plnkr.co/edit/MoEsJPRwR1nFOhRKd37q?p=preview

    myApp.directive("scaleImage", [
      function() {
        return {
          link: function(scope, elm, attrs) {
            attrs.$observe('scaleImage', function (value) {
              elm.css('width', value + '%');
              elm.css('height', value + '%');
            });
          }
        };
      }
    ]);
    

    如果这不起作用,请告诉我。


    你能做到吗:

    attrs.$observe('src' /* might be `ngSrc` */, function (value) { /* this is executed when the interpolated value of src changes */ });
    

    如果这不起作用,您可以设置一个 plunker 或 fiddle 吗?

    【讨论】:

    • 我已经建立了一个plnkr...唯一的问题是该指令涉及画布绘图并且plnkr似乎不支持它。无论如何,你会明白的。该指令应该调整图片的大小。 PLNKR here
    • 可以发链接吗?
    • 对不起,是的:PLNKR here
    • 遇到了同样的问题...我认为这不可能是因为 CORS...也许您可以下载 plnkr 并在本地计算机上尝试,因为它在那里工作,当所有图片来自本地文件系统。
    • @Clawish 让它在本地主机上工作。必须在服务器中运行(只使用了我放置的 IIS VS 工作室项目)。希望这也对你有用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-10
    • 2013-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多