Angular и фильтры 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 виден, но не размывается вообще.
- SVG снова отображается, когда я удаляю вручную (с Firebug) стиль
filter
.
Вот конфигурация маршрутов:
$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 в Fiddle, хотя он "работает" с Firefox.
Я не пытаюсь создать весь SVG с помощью document.createElementNS()
.
Есть ли у кого-то представление о том, что происходит?
Ответы
Ответ 1
Проблема
Проблема в том, что на моей странице HTML есть тег <base>
. Поэтому IRI, используемый для идентификации фильтра, больше не относится к текущей странице, а к URL-адресу, указанному в <base>
тег.
Этот URL-адрес также был 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). 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 Градиент становится черным, если на HTML-странице есть тег BASE?
Ответ 2
Похоже на поведение, которое я наблюдал раньше. Основная причина заключается в том, что вы получаете несколько элементов (фильтров) с одним и тем же идентификатором (размытым). Различные браузеры обрабатывают его по-другому...
Вот что я сделал, чтобы воспроизвести ваше дело: http://jsfiddle.net/z5cwZ/
Он имеет два svg и один скрыт, firefox ничего не показывает.
Есть две возможности избежать конфликтующих идентификаторов. Сначала вы можете создавать уникальные идентификаторы из своего шаблона (я не могу не делать этого с жестким углом). Вот пример: 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>
Ответ 3
Просто столкнулся с этой проблемой. Расширяясь от ответа @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));
}
};
});
Ответ 4
Недавно я столкнулся с этой проблемой, если svg используется на разных маршрутах, вам нужно будет добавить $location к каждому пути.
Лучший способ реализовать это - просто использовать window.location.href вместо службы $location.
Ответ 5
Также были проблемы с svg link: hrefs, и да проблема связана с <base>
- у меня были ошибки, возникающие при конкатенации абсолютного URL-адреса из-за Строгое контекстное экранирование.
Мое решение состояло в том, чтобы написать фильтр, который добавил абсолютный 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