角度和SVG过滤器

时间:2013-11-02 14:14:49

标签: angularjs svg

当使用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

请注意,我没有在小提琴中重现Chrome的问题,尽管它“适用于”Firefox。

我试图用document.createElementNS()创建我的整个SVG无效。

有人知道发生了什么吗?

5 个答案:

答案 0 :(得分:71)

问题

问题是我的HTML页面中有<base>标记。因此,用于标识过滤器的IRI不再相对于当前页面,而是<base>标记中指示的网址。

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

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

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

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

我应该看到http://example.com/my-folder/的额外请求,但在我的辩护中,它在许多其他JavaScript文件请求中丢失了。

解决方案

如果<base>标记是必需的,则解决方案是使用绝对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?

答案 1 :(得分:1)

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

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

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

使用angularjs可能更容易的第二种可能性是将过滤器放在单个svgs(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>

答案 2 :(得分:0)

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

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));
        }
    };
});

答案 3 :(得分:0)

我最近遇到过这个问题,如果在不同的路由上使用svg,你必须将$ location添加到每个路径。 实现这一点的更好方法是使用window.location.href而不是$ location服务。

答案 4 :(得分:0)

还有svg link:hrefs的问题,是的,问题是由于<base> - 由于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错误