Как вычислить stroke-width в SVG
Подскажите, как вычисляется толщина линии в SVG? Пример, мне нужно нарисовать квадрат 100х100 с толщиной линии 2 пиксела. При этом я рисую в координатах viewBox="0 0 10 10" Что нужно указать в stroke-width? Значение viewBox я менять не могу.
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" width="100">
<rect x="0" y="0" width="10" height="10" fill="none" stroke="red" stroke-width="2"/>
</svg>
И обратные пропорции. Квадрат 20х20 viewBox="0 0 100 100" нужны те же 2 пиксела
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="20">
<rect x="0" y="0" width="100" height="100" fill="none" stroke="red" stroke-width="2"/>
</svg>
Ответы (3 шт):
x="10" y="10" - смещение квадрата относительно сцены
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100">
<rect x="10" y="10" width="20" height="20" fill="none" stroke="red" stroke-width="2"/>
</svg>
Можно добавить vector-effect="non-scaling-stroke", а толщину задать в CSS.
rect {
stroke-width: 5px;
}
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" width="100">
<rect x="0" y="0" width="10" height="10" fill="none" stroke="red" vector-effect="non-scaling-stroke"/>
</svg>
Или можно задать толщину в относительных единицах SVG, но не знаю насколько они адекватно отражают именно пиксели CSS.
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" width="400">
<rect x="0" y="0" width="10" height="10" fill="none" stroke="red" stroke-width="3" vector-effect="non-scaling-stroke"/>
</svg>
Для сравнения добавил толщину линии по-разному. Получается, что никакой разницы. В конце добавил пару <div> - сразу видно, что толщина в 2 пикселя там работает по-другому. Но считаю, что корневая задача, а именно выравнивание толщины элементов вне зависимости от масштабирования таким образом решается. А во-вторых, можно подогнать толщину линий под нужный окончательный эффект.
.non-scale {
stroke-width: 2px;
}
div {
box-sizing: border-box;
border: solid 2px red;
display: inline-block;
}
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" width="100">
<rect x="0" y="0" width="10" height="10" fill="none" stroke="red" stroke-width="2" vector-effect="non-scaling-stroke"/>
</svg>
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="20">
<rect x="0" y="0" width="100" height="100" fill="none" stroke="red" stroke-width="2" vector-effect="non-scaling-stroke"/>
</svg>
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" width="100">
<style>
rect {
stroke-width: 2px;
}
</style>
<rect x="0" y="0" width="10" height="10" fill="none" stroke="red" vector-effect="non-scaling-stroke"/>
</svg>
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="20">
<style>
rect {
stroke-width: 2px;
}
</style>
<rect x="0" y="0" width="100" height="100" fill="none" stroke="red" stroke-width="2" vector-effect="non-scaling-stroke"/>
</svg>
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" width="100" class="non-scale">
<rect x="0" y="0" width="10" height="10" fill="none" stroke="red" vector-effect="non-scaling-stroke"/>
</svg>
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="20" class="non-scale">
<rect x="0" y="0" width="100" height="100" fill="none" stroke="red" vector-effect="non-scaling-stroke"/>
</svg>
<div style="width: 100px; height: 100px;"></div>
<div style="width: 20px; height: 20px;"></div>
Код автора вопроса:
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10"
width="100" style="border:2px solid;">
<rect id="rect" x="0" y="0" width="10" height="10" fill="none" stroke="red" stroke-width="2"/>
<polyline points="1,5 9,5" stroke="green" stroke-width="2" />
</svg>
<script>
console.log(rect.getBBox())
</script>
Чёрная рамка, - это границы видимой части холста SVG 100x100px. Это так называемый viewPort, который определяется width="100" height="100" в шапке SVG файла.
Эта та область, которую видит пользователь на дисплее после рендеринга фигур svg
viewBox="0 0 10 10" виртуальная область, которая захватывается для вычислений рендеринга и её не видит пользователь.
После вычислений viewport / viewBox = 10 Другими словами один пиксель физической фигуры SVG растягивается на 10 пикселей viewPort.
Поэтому фигура, которая имеет физические размеры 10x10px после рендеринга становится в 10 раз больше 100x100px. Соответственно линии становятся в 10 раз толще.
Но есть ещё один нюанс.
Так как границы прямоугольника идут по краю холста SVG, то они уменьшаются в два раза, так как половина слева от осевой, вторая половина справа от осевой.
Обратите внимание, я добавил зелёную линию по центру stroke-width="2" и она в два раза толще границ красного прямоугольника.
Поэтому необходимо изменить толщину границ прямоугольника на stroke-width="4", тогда видимая, физическая ширина линии будет 2px
Чтобы при любом значении viewPort widthи height линия оставалась после рендеринга строго 2px необходимо добавить , как советует @Leonid в своем ответе - vector-effect="non-scaling-stroke"
Решение:
- stroke-width x 2 то есть, устанавливать в два раза толще, чтобы половина была равна, требуемому размеру
- vector-effect="non-scaling-stroke" - для того чтобы линия не меняла свою толщину при изменении масштаба (соотношение viewPort / viewBox) или при изменении только
width
Ниже пример с 4 разными размерами квадратов, у которых толщина границы остается постоянной -2px
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" width="20" >
<rect id="rect" x="0" y="0" width="10" height="10" fill="none" stroke="red" stroke-width="4"
vector-effect="non-scaling-stroke"/>
</svg>
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" width="40" >
<rect id="rect" x="0" y="0" width="10" height="10" fill="none" stroke="red" stroke-width="4"
vector-effect="non-scaling-stroke"/>
</svg>
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" width="100" >
<rect id="rect" x="0" y="0" width="10" height="10" fill="none" stroke="red" stroke-width="4"
vector-effect="non-scaling-stroke"/>
</svg>
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" width="200" >
<rect id="rect" x="0" y="0" width="10" height="10" fill="none" stroke="red" stroke-width="4"
vector-effect="non-scaling-stroke"/>
</svg>