角度和 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,它隐藏了 元素 @musically_ut 我已经编辑了我的问题:在 Chrome 中,rect 可见但不模糊。 @fabrizioM 听起来有点奇怪……那为什么不在首页呢? 你为什么不使用正常的过滤器语法 【参考方案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"   />
</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 也有问题,是的,问题是因为&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 错误

【讨论】:

以上是关于角度和 SVG 过滤器的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 svg 过滤器应用渐变

应用 CSS 过滤器时,内联 SVG 在 iOS 和 Safari 中消失

svg下图像标签中的过滤器会降低图像质量吗?

使用 SVG 过滤器向 SVG 图像元素添加边框 [重复]

带有 CSS 过渡的 SVG 过滤器

如何使用 SVG 过滤器将白色图像反转为黑色?