【问题标题】:Angular and SVG filters角度和 SVG 过滤器
【发布时间】:2013-11-13 14:25:13
【问题描述】:

在将 SVG 与 AngularJS 一起使用时,我遇到了一个奇怪的行为。我正在使用$routeProvider 服务来配置我的路线。当我把这个简单的 SVG 放在我的模板中时,一切都很好:

<div id="my-template">
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
        <rect fill="red" height="200" width="300" />
    </svg>
    // ...
</div>

但是当我添加一个过滤器时,例如以下代码:

<div id="my-template">
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
        <defs>
            <filter id="blurred">
                <feGaussianBlur stdDeviation="5"/>
            </filter>
        </defs>
        <rect style="filter:url(#blurred)" fill="red" height="200" width="300" />
    </svg>
</div>

然后:

  • 它适用于我的主页
  • Firefox 中,SVG 在 其他页面 上不再可见,但它仍会在原处留出空间。使用 Chrome,SVG 是可见的,但根本不会模糊。
  • 当我手动(使用 Firebug)删除 filter 样式时,SVG 再次可见。

这里是路由配置:

$routeProvider
    .when('/site/other-page/', {
            templateUrl : 'view/Site/OtherPage.html',
            controller : 'Site.OtherPage'
    })
    .when('/', {
            templateUrl : 'view/Site/Home.html',
            controller : 'Site.Home'
    })
    .otherwise({
        redirectTo : '/'
    })
;

Fiddle

请注意,我无法在 Fiddle 中重现 Chrome 的问题,尽管它适用于 Firefox。

我尝试使用 document.createElementNS() 创建整个 SVG,但无济于事。

有人知道发生了什么吗?

【问题讨论】:

  • 是浏览器特有的问题吗?它适用于 Chrome 吗?
  • 我认为发生的事情是 angularjs 正在将过滤器运算符应用于 svg 元素,并且因为 url(#blurred) 在 angularjs 上下文中计算为 false,它隐藏了 元素
  • @musically_ut 我已经编辑了我的问题:在 Chrome 中,rect 可见但不模糊。
  • @fabrizioM 听起来有点奇怪……那为什么不在首页呢?
  • 你为什么不使用正常的过滤器语法

标签: angularjs svg


【解决方案1】:

问题

问题是我的 HTML 页面中有一个 &lt;base&gt; 标记。因此,用于标识过滤器的IRI不再是相对于当前页面,而是相对于&lt;base&gt;标签中指示的URL。

这个网址也是我主页的网址,例如http://example.com/my-folder/

对于主页以外的页面,例如http://example.com/my-folder/site/other-page/#blurred 被计算为绝对 URL http://example.com/my-folder/#blurred。但是对于一个简单的 GET 请求,没有 JavaScript,因此没有 AngularJS,这只是我的基本页面,没有加载模板。因此,#blurred 过滤器在此页面上不存在

在这种情况下,Firefox 不会呈现 &lt;rect&gt;这是正常行为,请参阅 W3C recommandation)。 Chrome 根本不应用过滤器。

对于主页,#blurred 也被计算为绝对 URL http://example.com/my-folder/#blurred。但这一次,这也是当前的 URL。无需发送 GET 请求,因此#blurred 过滤器存在

我应该看到对http://example.com/my-folder/ 的附加请求,但在我的辩护中,它在大量其他对 JavaScript 文件的请求中丢失了。

解决方案

如果&lt;base&gt;标签是强制性的,解决方法是使用绝对IRI来识别过滤器。在 AngularJS 的帮助下,这非常简单。在控制器或链接到 SVG 的指令中,注入 $location 服务并使用 absUrl() getter:

$scope.absUrl = $location.absUrl();

现在,在 SVG 中,只需使用这个属性:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
    <defs>
        <filter id="blurred">
            <feGaussianBlur stdDeviation="5"/>
        </filter>
    </defs>
    <rect style="filter:url({{absUrl}}#blurred)" fill="red" height="200" width="300" />
</svg>

相关: SVG Gradient turns black when there is a BASE tag in HTML page?

【讨论】:

  • 感谢您分享您的发现。我赞成它,因为它可能很快就会帮助我:)
  • 在 Chrome 和 Firefox 上运行良好,但在 IE11 上不行。有趣的是,它在没有修复的情况下在 IE11 上工作得很好。很奇怪。
  • 这拯救了我的一天。谢谢!
  • 如果使用Angular2,有一个更好的解决方案是去掉基础标签stackoverflow.com/a/34535256/3218806
  • 动态创建的过滤器或渐变由于角度路由而无法正常工作
【解决方案2】:

这看起来像是我之前观察到的一种行为。根本原因是您最终拥有多个具有相同 id(模糊)的元素(过滤器)。不同的浏览器处理方式不同...

这是我为重现您的案例所做的:http://jsfiddle.net/z5cwZ/ 它有两个 svg,一个是隐藏的,firefox 没有显示。

有两种方法可以避免 id 冲突。首先,您可以从您的模板中生成唯一的 id(我无法帮助使用 angularjs 强硬)。这是一个例子:http://jsfiddle.net/KbCLB/1/

第二种可能,使用 angularjs 可能更容易,是将过滤器放在单个 svg 之外 (http://jsfiddle.net/zAbgr/1/):

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="0" height="0">
    <defs>
        <filter id="blurred">
            <feGaussianBlur stdDeviation="10" />
        </filter>
    </defs>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" style="display:none">
    <rect style="filter:url(#blurred)" fill="red" height="200" width="300" />
</svg>
<br/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
    <rect style="filter:url(#blurred)" fill="red" height="200" width="300" />
</svg>

【讨论】:

  • 问题不在这里;-)。当我更改页面时,带有 ID 的先前 filter 将被 Angular 删除,作为页面的其余部分。此外,如果我对每个页面上的过滤器命名不同,那也不能解决问题。
  • 我并不是说当您更改页面时,旧元素仍然存在。我的意思是您的模板和从模板生成的所有元素(在给定页面上)中都有一个 id="blurred" 。我很想知道我的第二个解决方案是否能解决问题(将过滤器放在模板之外并在模板中使用它)。
  • 唉,你的提议没有进展……
  • 好的,谢谢。太糟糕了:(你能复制你的DOM(用firefox,打开检查器,右键单击html标签并“复制外部HTML”)并分享它以便我们看看吗?
  • 那是我过去的问题。这是这个问题最奇怪的地方之一:DOM、元素的属性……看起来两个页面之间的一切都是一样的!
【解决方案3】:

刚刚遇到这个问题。扩展@Blackhole 的答案,我的解决方案是添加一个指令来更改每个填充属性的值。

angular.module('myApp').directive('fill', function(
    $location
) { 'use strict';
    var absUrl = 'url(' + $location.absUrl() + '#';

    return {
        restrict: 'A',
        link: function($scope, $element, $attrs) {
            $attrs.$set('fill', $attrs.fill.replace(/url\(#/g, absUrl));
        }
    };
});

【讨论】:

    【解决方案4】:

    我最近自己遇到了这个问题,如果 svg 用于不同的路线,您将不得不将 $location 添加到每个路径。 实现这一点的更好方法是只使用 window.location.href 而不是 $location 服务。

    【讨论】:

      【解决方案5】:

      svg link:hrefs 也有问题,是的,问题是因为&lt;base&gt; - 由于Strict Contextual Escaping 限制,我在连接绝对网址时出错。

      我的解决方案是编写一个添加绝对 url 并且还信任连接的过滤器。

      angular.module('myApp').filter('absUrl', ['$location', '$sce', function ($location, $sce) {
          return function (id) {
              return $sce.trustAsResourceUrl($location.absUrl() + id);
          };
      }]);
      

      然后像这样使用它:{{'#SVGID_67_' | absUrl}

      结果:http://www.example.com/deep/url/to/page#SVGID_67_ 并且没有 $sce 错误

      【讨论】:

        猜你喜欢
        • 2020-07-28
        • 1970-01-01
        • 2014-06-08
        • 1970-01-01
        • 2014-04-22
        • 2018-07-23
        • 2021-02-27
        相关资源
        最近更新 更多