Можете ли вы контролировать, как рисуется ширина хода SVG?


204

В настоящее время строит SVG-приложение на основе браузера. В этом приложении пользователь может стилизовать и позиционировать различные формы, включая прямоугольники.

Когда я применяю a stroke-widthк rectэлементу SVG, скажем 1px, обводка применяется к rectсмещению и вставке по-разному в разных браузерах. Это оказывается проблематичным, особенно когда я пытаюсь вычислить внешнюю ширину и визуальное положение прямоугольника и расположить его рядом с другими элементами.

Например:

  • Firefox добавляет вставку в 1 пиксель (внизу и слева) и смещение в 1 пиксель (вверху и справа)
  • Chrome добавляет вставку в 1 пиксель (вверху и слева) и смещение в 1 пиксель (внизу и справа)

Мое единственное решение до сих пор состояло бы в том, чтобы нарисовать фактические границы самостоятельно (возможно, с помощью pathинструмента) и расположить границы за обведенным элементом. Но это решение - неприятный обходной путь, и я бы предпочел не идти по этому пути, если это возможно.

Итак, мой вопрос, можете ли вы контролировать, как SVG stroke-widthрисуется на элементах?


Есть хаки фильтров, которые вы можете использовать для достижения этой цели, но это не очень
хорошее

2
Существует paint-orderпараметр, в котором вы можете указать, что заливка должна отображаться поверх обводки, поэтому вы получите «внешнее выравнивание», см. Jsfiddle.net/hne0kyLg/1
Иван Кукир,

Нашел способ сделать это с помощью атрибутов css 'outline-': codepen.io/badcat/pen/YVzmYY . Не уверен, что это поддерживает в разных браузерах, но может быть полезно.
bplmp

Ответы:


375

Нет, вы не можете указать, рисуется ли обводка внутри или снаружи элемента. Я сделал предложение рабочей группе SVG по этой функциональности в 2003 году, но он не получил поддержки (или обсуждения).

SVG предложил пример местоположения инсульта от phrogz.net/SVG/stroke-location.svg

Как я отметил в предложении,

  • вы можете получить тот же визуальный результат, что и «внутри», удвоив ширину штриха, а затем используя обтравочный контур, чтобы обрезать объект к себе, и
  • вы можете получить тот же визуальный результат, что и «снаружи», удвоив ширину обводки и наложив поверх нее копию без обводки.

Изменить : этот ответ может быть неправильным в будущем. Должно быть возможно достичь этих результатов, используя векторные эффекты SVG , комбинируя veStrokePathс veIntersect(для «внутри») или с veExclude(для «снаружи»). Однако Vector Effects все еще является рабочим черновым модулем без реализаций, которые я пока не могу найти.

Редактировать 2 : черновая спецификация SVG 2 включает stroke-alignmentсвойство (с центром | внутри | вне возможных значений). Это свойство может в конечном итоге превратиться в UA.

Редактировать 3 : Забавно и неутешительно, рабочая группа SVG удалила stroke-alignmentиз SVG 2. Вы можете увидеть некоторые проблемы, описанные после проза здесь .


2
Думал, что это может быть так. Чтобы решить эту проблему, я написал функцию-обертку для getBBox (), которая называется getStrokedBBox (). Эта оболочка возвращает BBox в соответствии с тем, как браузер применяет обводки к вставке и смещению фигуры. Он не идеален (нужно постоянно проверять последние версии браузеров), но на данный момент он точно обеспечивает внешнюю ширину формы.
Стив

6
@Phrogz, может быть, мы увидим это раньше, чем через 10 лет. svgwg.org/svg2-draft/painting.html#SpecifyingStrokePaint аннотация
французский

1
Это наконец здесь! Я, например, действительно призываю к прибытию этого имущества.
mnsth

Я создал скрипт svg-contour для трассировки контура любого SVGGeometryElement, который можно использовать в качестве обходного пути выравнивания штрихов, для всех, кто интересуется, вы можете найти описание здесь
maioman

2
Есть ли какая-нибудь страница, где я могу поддержать предложение? Спасибо. Кажется смешным это не поддерживается.
Махиш

57

UPDATE:stroke-alignment атрибут был на 1 апреля 2015 года перешел в совершенно новую спецификацию под названием SVG Strokes .

Начиная с редакции SVG 2.0 от 26 февраля 2015 г. (и, возможно, с 13 февраля ),stroke-alignmentсвойство присутствуетсо значениями inner, center ( по умолчанию) и outer.

Кажется, он работает так же, как stroke-locationсвойство, предложенное @Phrogz и последующим stroke-positionпредложением . Это свойство было запланировано как минимум с 2011 года, но кроме аннотации, которая гласила

SVG 2 должен включать способ задания положения хода

, это никогда не было детализировано в спецификации, как это было отложено - до сих пор, кажется.

Ни один браузер не поддерживает это свойство или, насколько мне известно, какие-либо новые функции SVG 2, но, надеюсь, они появятся в ближайшее время, когда спецификации будут готовы. Это было свойство, которое я лично хотел получить, и я очень рад, что он наконец-то есть в спецификации.

Кажется, есть некоторые проблемы относительно того, как свойство должно вести себя как на открытых путях, так и на циклах. Эти проблемы, скорее всего, продлят реализации в браузерах. Однако я обновлю этот ответ новой информацией, поскольку браузеры начинают поддерживать это свойство.


1
stroke-alignmentуказано в SVG Strokes, W3C Working Draft. Между тем, черновик редактора SV3 для W3C гласит, что свойство позиционирования штриха должно быть в спецификации SVG по адресу svgwg.org/svg2-draft/painting.html#SpecifyingStrokePaint , но эта спецификация уже достигла статуса рекомендации кандидата W3C и такого свойства не существует. в спецификации, кроме ссылки на stroke-positionпредложение, кажется, что это не так.
Патрик Дарк

47

Я нашел простой способ, который имеет несколько ограничений, но работал для меня:

  • определить форму в определениях
  • определить путь клипа, ссылающийся на форму
  • использовать его и удвоить ход, так как снаружи обрезается

Вот рабочий пример:

<svg width="240" height="240" viewBox="0 0 1024 1024">
<defs>
	<path id="ld" d="M256,0 L0,512 L384,512 L128,1024 L1024,384 L640,384 L896,0 L256,0 Z"/>
	<clipPath id="clip">
		<use xlink:href="#ld"/>
	</clipPath>
</defs>
<g>
	<use xlink:href="#ld" stroke="#0081C6" stroke-width="160" fill="#00D2B8" clip-path="url(#clip)"/>
</g>
</svg>


2
Лучший ответ, который я нашел
Эд Колосовский

1
Умное решение. Спасибо
Винс Юань

Это должно быть принято как ответ. Использование <defs> и <use> является наиболее элегантным решением, доступным в настоящее время.
Nyerguds

Хорошее решение. Как бы вы изменили это, чтобы сделать внешний удар?
Lucent

Почему на высшем уровне <use>? Почему бы просто не поместить путь и обрезать путь напрямую? Это работает просто отлично <svg width="240" height="240" viewBox="0 0 1024 1024"> <path id="ld" d="M256,0 L0,512 L384,512 L128,1024 L1024,384 L640,384 L896,0 L256,0 Z" clip-path="url(#clip)" stroke="#0081C6" stroke-width="160" fill="#00d2b8"/> <clipPath id="clip"> <use xlink:href="#ld"/> </clipPath> </svg>. (Да, это совершенно разумный и правильный SVG, и он будет правильно отображаться везде. Не бойтесь рекурсии.)
Крис Морган

30

Вы можете использовать CSS для стилизации порядка обводки и заливки. То есть сначала обведите, а затем залейте второе и получите желаемый эффект.

MDN на paint-order: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/paint-order

Код CSS:

paint-order: stroke;

Это потрясающе! Спасибо, что поделились
BrunoFenzl

1
Связанная документация, кажется, не указывает, находится ли обводка внутри, снаружи или по центру? Не могли бы вы уточнить, как контролировать, где черта рисуется? Спасибо.
Crashalot

2
Обводка отцентрирована, как и всегда, но если у вас сплошная заливка, эффект корректировки порядка рисования для рисования обводки сначала заключается в том, что внутренняя половина обводки закрашена, что имеет тот же эффект, что и рисование наполовину толстый ход снаружи.
Крис Морган

7

Вот функция, которая подсчитает, сколько пикселей нужно добавить - используя данный штрих - к верху, справа, снизу и слева, все на основе браузера:

var getStrokeOffsets = function(stroke){

        var strokeFloor =       Math.floor(stroke / 2),                                                                 // max offset
            strokeCeil =        Math.ceil(stroke / 2);                                                                  // min offset

        if($.browser.mozilla){                                                                                          // Mozilla offsets

            return {
                bottom:     strokeFloor,
                left:       strokeFloor,
                top:        strokeCeil,
                right:      strokeCeil
            };

        }else if($.browser.webkit){                                                                                     // WebKit offsets

            return {
                bottom:     strokeCeil,
                left:       strokeFloor,
                top:        strokeFloor,
                right:      strokeCeil
            };

        }else{                                                                                                          // default offsets

            return {
                bottom:     strokeCeil,
                left:       strokeCeil,
                top:        strokeCeil,
                right:      strokeCeil
            };

        }

    };

6

Как уже отмечалось выше, вам придется либо пересчитать смещение по координатам траектории обводки, либо удвоить его ширину, а затем замаскировать одну или другую сторону, потому что SVG не только изначально не поддерживает выравнивание обводки Illustrator, но и PostScript не поддерживает ,

Спецификация для ударов в ручном PostScript 2 - состояниях издания компании Adobe: " 4.5.1 Поглаживания: тактный оператор рисует линию некоторой толщины вдоль пути тока для каждого прямого или изогнутого сегмента в пути,. Тактный рисует линию, центрированная на отрезок с параллельными сторонами сегменту. " (акцент их)

Остальная часть спецификации не имеет атрибутов для смещения позиции строки. Когда Illustrator позволяет вам выравнивать внутри или снаружи, он пересчитывает фактическое смещение пути (потому что это все еще в вычислительном отношении дешевле, чем наложение, чем маскирование). Координаты пути в документе .ai являются ссылочными, а не растровыми или экспортированными в окончательный формат.

Поскольку родной формат Inkscape - это спецификация SVG, он не может предложить функции, которой не хватает в спецификации.


4

Вот обходной путь для внутреннего граничного rect использования symbolи use.

Пример : https://jsbin.com/yopemiwame/edit?html,output

SVG :

<svg>
  <symbol id="inner-border-rect">
    <rect class="inner-border" width="100%" height="100%" style="fill:rgb(0,255,255);stroke-width:10;stroke:rgb(0,0,0)">
  </symbol>
  ...
  <use xlink:href="#inner-border-rect" x="?" y="?" width="?" height="?">
</svg>

Примечание: Убедитесь в том , чтобы заменить ?в useреальных значениях.

Предпосылки : Причина , почему это работает, потому что символ устанавливает новый видовой экран, заменив symbolс svgи создавая элемент в тени DOM. Этот svgтеневой DOM затем связывается с вашим текущим SVGэлементом. Обратите внимание, что svgs может быть вложенным, и каждый svgсоздает новый видовой экран, который обрезает все перекрывающиеся области, включая перекрывающуюся границу. Для более подробного обзора происходящего прочитайте эту фантастическую статью Сары Соуэйдан.


2

Я не знаю, насколько это будет полезно, но в моем случае я просто создал еще один круг с рамкой и поместил его «внутри» другой фигуры.


0

(Грязное) возможное решение - использование шаблонов,

Вот пример с внутренним штрихованным треугольником:

https://jsfiddle.net/qr3p7php/5/

<style>
#triangle1{
  fill: #0F0;
  fill-opacity: 0.3;
  stroke: #000;
  stroke-opacity: 0.5;
  stroke-width: 20;
}
#triangle2{
  stroke: #f00;
  stroke-opacity: 1;
  stroke-width: 1;
}    
</style>

<svg height="210" width="400" >
    <pattern id="fagl" patternUnits="objectBoundingBox" width="2" height="1" x="-50%">
        <path id="triangle1" d="M150 0 L75 200 L225 200 Z">
    </pattern>    
    <path id="triangle2" d="M150 0 L75 200 L225 200 Z" fill="url(#fagl)"/>
</svg>

0

Решение от Ксавье Хо об удвоении ширины обводки и изменении порядка рисования является блестящим, хотя работает только в том случае, если заливка имеет сплошной цвет без прозрачности.

Я разработал другой подход, более сложный, но работает для любой заливки. Это также работает в эллипсах или путях (с более поздним есть некоторые угловые случаи со странным поведением, например, открытые пути, которые пересекаются, но не сильно).

Хитрость заключается в том, чтобы отобразить форму в два слоя. Один без обводки (только заливка), а другой только с обводкой на двойной ширине (прозрачная заливка), пропущенный через маску, которая показывает всю форму, но скрывает исходную форму без обводки.

  <svg width="240" height="240" viewBox="0 0 1024 1024">
  <defs>
    <path id="ld" d="M256,0 L0,512 L384,512 L128,1024 L1024,384 L640,384 L896,0 L256,0 Z"/>
    <mask id="mask">
      <use xlink:href="#ld" stroke="#FFFFFF" stroke-width="160" fill="#FFFFFF"/>
      <use xlink:href="#ld" fill="#000000"/>
    </mask>
  </defs>
  <g>
    <use xlink:href="#ld" fill="#00D2B8"/>
    <use xlink:href="#ld" stroke="#0081C6" stroke-width="160" fill="red" mask="url(#mask)"/>
  </g>
  </svg>
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.