Чи можете ви контролювати, як намальована ширина ходу SVG?


204

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

Коли я застосую 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 Vector Effects , комбінуючи 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 анотація
frenchone

1
Це нарешті тут! Я, наприклад, справді закликав цю власність прибути.
mnsth

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

2
Чи є сторінка, на якій я можу подати пропозицію? Дякую. Смішно здається, що це не підтримується.
mahish

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. Тим часом, в редакторі редактора SVG 2 W3C йдеться про те, що властивість позиціонування обведення має бути у специфікації SVG за адресою svgwg.org/svg2-draft/painting.html#SpecifyingStrokePaint , але ця специфікація вже досягла статусу Рекомендації кандидата W3C, і такої властивості немає у специфікації, окрім посилання на stroke-positionпропозицію, тому, схоже, це не буде так.
Патрік Темний

47

Я знайшов простий спосіб, який має кілька обмежень, але працював для мене:

  • визначте форму в defs
  • визначити шлях кліпу з посиланням на форму
  • використовуйте його та подвійне обведення, як зовні обрізається

Ось робочий приклад:

<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

Рішення Xavier Ho подвоїти ширину штриху та змінити порядок фарби є геніальним, хоча працює лише в тому випадку, якщо заливка суцільного кольору, без прозорості.

Я розробив інший підхід, більш складний, але працює для будь-якої заливки. Він також працює в еліпсах або доріжках (пізніше трапляються кутові випадки із дивним поведінкою, наприклад, відкриті шляхи, що перетинають себе, але не дуже багато).

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

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