角度和 SVG 过滤器
Posted
技术标签:
【中文标题】角度和 SVG 过滤器【英文标题】:Angular and SVG filters 【发布时间】: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" />
</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" />
</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,它隐藏了rect
可见但不模糊。
@fabrizioM 听起来有点奇怪……那为什么不在首页呢?
你为什么不使用正常的过滤器语法 问题
问题是我的 HTML 页面中有一个 <base>
标记。因此,用于标识过滤器的IRI不再是相对于当前页面,而是相对于<base>
标签中指示的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 不会呈现 <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" />
</svg>
相关: SVG Gradient turns black when there is a BASE tag in HTML page?
【讨论】:
感谢您分享您的发现。我赞成它,因为它可能很快就会帮助我:) 在 Chrome 和 Firefox 上运行良好,但在 IE11 上不行。有趣的是,它在没有修复的情况下在 IE11 上工作得很好。很奇怪。 这拯救了我的一天。谢谢! 如果使用Angular2,有一个更好的解决方案是去掉基础标签***.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" >
<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" />
</svg>
<br/>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<rect style="filter:url(#blurred)" fill="red" />
</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 也有问题,是的,问题是因为<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 错误
【讨论】:
以上是关于角度和 SVG 过滤器的主要内容,如果未能解决你的问题,请参考以下文章