Подтвердить что ты не робот

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().

Есть ли у кого-то представление о том, что происходит?

4b9b3361

Ответ 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