Я пытаюсь разместить svg с текстом точно в том же положении, что и div с текстом, независимо от используемого семейства шрифтов или размера шрифта.
Проблема связана с тем, что первый элемент смещен по позициям x и y.
Я пытаюсь установить оба элемента в абсолютное положение сверху 0 слева 0, но они не совпадают.
Конечно, вручную добавление правильного смещения x и y работает, но мне нужно общее решение.
Вот пример, если запустить, то тексты не будут правильно располагаться один поверх другого. Я пытаюсь заставить это работать с любым шрифтом и любым размером.
<div style = "position:absolute;color:red;font-family:Verdana;font-size:20px; margin-left:0; padding-left:0; left:0; ">
First Line
</div>
<div style = "position:absolute;top:0;left :0; background:transparent;">
<svg width = "100" height = "20" xmlns = "http://www.w3.org/2000/svg">
<text x = "0" y = "0" font-family = "Verdana" text-anchor = "start" dominant-baseline = "hanging" font-size = "20" fill = "black">First Line</text>
</svg>
</div>
Вот еще код https://codepen.io/Cristian-M/pen/VwJzXJv
Измените размер шрифта (увеличьте его), и смещение будет видно все больше и больше.
Есть идеи? Спасибо!
Для SVG мне кажется, что работает y = "1.4em", а не доминирующая базовая линия.
Исправлено недостающее : . Использование 1.4em работает, но если вы измените шрифт или размер текста, это больше не будет работать. Я пытаюсь найти универсальное решение, независимо от семейства и размера шрифта.
Ну, единицы em различаются в зависимости от размера шрифта, так что вроде бы все в порядке. С какими шрифтами не работает?
Я пробовал использовать em, но при изменении размера шрифта все равно не работает. Проверьте это codepen.io/Cristian-M/pen/VwJzXJv увеличьте размер шрифта, и смещение станет все более заметным. Я пробовал использовать em вместо px, но та же проблема.
@blaster god, я думаю, тебе нужно будет поместить оба элемента в обертку. Установите обертку в относительное позиционирование. Затем установите для обоих элементов абсолютное позиционирование, а верхний и левый установите на 0. Кроме того, убедитесь, что высота строки элемента div равна размеру его шрифта. Используйте один и тот же размер шрифта и семейство шрифтов для обоих элементов и удалите лишние элементы.
First Line First LineЭто не работает. Попробуйте увеличить размер шрифта до 50, и смещение станет все более и более очевидным. <div style = "position:relative"> <div style = "position:absolute;color:red;font-family:Verdana;font-size:50px; поля слева:0; отступа слева:0; слева :0; line-height:50px;"> Первая строка </div> <div style = "position:absolute;top:0;left :0; background:transparent;"> <svg width = "300" height = " 50" xmlns = "w3.org/2000/svg"> <text x = "0" y = "0" font-family = "Verdana" text-anchor = "start" доминант-baseline = "висит" font-size = "50" fill = "black">Первая строка</text> </svg> </div> </div>
Боюсь, вы сможете решить эту задачу лишь частично. В основном из-за ограничений SVG по компоновке текста и макету, которые делают идеальную (адаптированную) репликацию HTML-элементов совершенно невозможной:
inline-size
будет доработано в спецификациях W3C и принято – я не буду задерживать дыхание...)flex
или grid
)Другими словами: чтобы эмулировать/реплицировать макеты HTML в SVG, нам нужно вычислить подходящие значения с помощью JavaScript.
По сути, мы наследуем большинство свойств шрифта HTML от элемента SVG <text>
, поскольку мы скорее внедряем «двойник» SVG в существующий контекст макета HTML/CSS. В частности, мы устанавливаем базовую линию текста SVG в нижней части viewBox
и применяем overflow:visible
к обрезанным нижним или верхним элементам: элемент SVG будет перемещаться как текстовый элемент HTML с учетом унаследованного размера шрифта.
let svg = document.querySelector('.svgText')
let {width} = svg.getBBox();
// adjust viewBox
svg.setAttribute('viewBox', [0,0, width, 1].join(' '));
// fontSize change
inputFontSize.addEventListener('input', e=>{
let fontSize = +e.currentTarget.value;
document.body.style.fontSize = `${fontSize}px`;
})
body {
font-family: verdana;
font-size: 80px;
}
* {
box-sizing: border-box;
}
.fnt-siz, .tools,
h3 {
font-size: 16px;
line-height: 1.2em;
}
.tools {
position: sticky;
top: 0;
background: #000;
color: #fff;
padding: 0.5em;
font-size: 20px;
line-height: 1.2em;
}
.htmlText {
position: absolute;
background: yellow;
color: red;
margin-left: 0;
padding-left: 0;
left: 0.5em;
top: 2em;
}
svg {
font-size: 1px;
letter-spacing: inherit;
}
.svgTextOverlay {
position: relative;
}
svg {
font-size: 1em;
height: 1em;
overflow: visible;
outline: 2px dotted red;
}
.svgText {
position: absolute;
left: 0;
top: 0;
}
<div class = "tools">
<p><label>fontsize <input id = "inputFontSize" type = "range" value = "80" min = "10" max = "200"></label></p>
</div>
<h3>Synchonize SVG with HTML font-size</h3>
<div class = "htmlText">
<span class = "svgTextOverlay">
Hamburg fonts
<!-- svg text overlay -->
<svg id = "svg1" class = "svgText" viewBox = "0 0 1 1" >
<text x = "0" y = "1" font-size = "1" >Hamburg fonts</text>
</svg>
</span>
</div>
Как видите, мы можем воспроизвести свойства HTML-текста довольно точно… если только не получим разрыв строки.
Самый простой заполнитель для замены текста SVG может выглядеть примерно так:
<svg class = "svgText" viewBox = "0 0 1 1">
<text y = "1" font-size = "1">
Your text
</text>
</svg>
Требуемый CSS для того, чтобы SVG «плавал» по базовой линии, как обычный текст (а также наследовал такие свойства, как цвет текста), будет:
.svgText{
display:inline-block;
font-size: 1em;
height: 1em;
overflow: visible;
fill: currentColor;
}
Требуется для расчета подходящей viewBox
ширины.
let {width} = svg.getBBox();
svg.setAttribute('viewBox', [0,0, width, 1].join(' '));
let svgTexts = document.querySelectorAll('.svgText');
(async() => {
await document.fonts.ready;
replaceHTMLText(svgTexts)
})();
function replaceHTMLText(svgTexts) {
svgTexts.forEach(svg => {
let {
width
} = svg.getBBox();
svg.setAttribute('viewBox', [0, 0, width, 1].join(' '));
})
}
body {
font-size: 5vmin;
font-family: georgia, serif;
padding: 0.5em;
}
.svgText {
display: inline-block;
font-size: 1em;
height: 1em;
overflow: visible;
fill: currentColor;
}
.resize {
width: 75%;
overflow: auto;
padding: 0.2em;
outline: 1px solid #ccc;
resize: both;
}
p:hover .svgText {
color: red
}
<h3>Hover to see the SVG text elements</h3>
<div class = "resize">
<p>One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin. He lay on his armour-like back, and if he lifted his head a little he could see his brown belly, <svg class = "svgText" viewBox = "0 0 1 1"><text y = "1" font-size = "1">slightly</text></svg> domed and divided by arches into stiff sections. The bedding was <svg class = "svgText" viewBox = "0 0 1 1"><text y = "1" font-size = "1">hardly</text></svg> able to cover it and seemed ready to slide off any moment.</p>
</div>
measureText()
для абсолютного положения.Мы можем получить некоторые метрики вертикального шрифта с помощью MeasureText() , чтобы вычислить подходящие смещения для SVG. См. также «4.12.5.1.11 Рисование текста в растровом изображении»
(async() => {
await document.fonts.ready;
positionSVGTextOverlay(htmlText, svgText);
})()
function positionSVGTextOverlay(htmlEl, svgEl) {
// copy styles
let styles = window.getComputedStyle(htmlEl);
let {
fontFamily,
fontSize
} = styles;
fontSize = parseFloat(fontSize);
//get HTML element position
let {
x,
y,
bottom,
width,
height
} = htmlEl.getBoundingClientRect();
// get vertical metrics to calculate y offset
let metrics = getFontMetrics(fontFamily);
let {
fontBoundingBoxAscent,
fontBoundingBoxDescent,
hangingBaseline,
alphabeticBaseline
} = metrics;
let renderedHeight = (fontBoundingBoxDescent + alphabeticBaseline);
let scale = 1 + (fontBoundingBoxDescent + alphabeticBaseline) / fontBoundingBoxDescent
let top = Math.floor(bottom - fontSize * scale)
svgEl.setAttribute(
"style",
`
position:absolute;
display:inline-block;
font-size: ${fontSize}px;
font-family: ${fontFamily};
left: ${x}px;
top: ${ top }px;
`
);
//adjust viewBox
let bb = svgEl.getBBox();
svgEl.setAttribute('viewBox', [0, 0, bb.width, 1].join(' '));
}
function getFontMetrics(font) {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
ctx.font = `1000px ${font}`;
ctx.textBaseline = "top";
return ctx.measureText('H');
}
html,
body {
margin: 0;
padding: 0;
}
.fnt-siz,
.tools,
h3 {
font-size: 16px;
line-height: 1.2em;
}
.tools {
position: sticky;
top: 0;
background: #000;
color: #fff;
padding: 0.5em;
font-size: 20px;
line-height: 2.5em;
}
.htmlText {
font-family: sans-serif, Verdana, Georgia;
position: absolute;
background: yellow;
color: red;
margin-left: 0;
padding-left: 0;
left: 50px;
top: 200px;
font-size: 90px;
}
.svgText {
font-size: 1em;
height: 1em;
overflow: visible;
outline: 1px solid red;
}
<div class = "tools">
<p><label>fontsize <input id = "inputFontSize" type = "range" value = "80" min = "10" max = "200"></label></p>
</div>
<h3>Synchonize SVG with HTML font-size</h3>
<div id = "htmlText" class = "htmlText">
First Line
</div>
<!-- svg text overlay -->
<svg id = "svgText" class = "svgText" viewBox = "0 0 1 1">
<text y = "1" font-size = "1" >First Line
</text>
</svg>
<script>
// fontSize change
inputFontSize.addEventListener("input", (e) => {
let fontSize = +e.currentTarget.value;
htmlText.style.fontSize = `${fontSize}px`;
positionSVGTextOverlay(htmlText, svgText);
});
</script>
Однако мы по-прежнему не можем эмулировать разрывы строк.
Спасибо большое за подробный ответ. Мне удалось найти идеальное решение несколько часов назад. Все, что мне нужно было сделать, это установить доминирующую базовую линию по центру (вместо висячей, средней или любой другой) и установить позицию Y на половину размера шрифта. И это один на один с текстом div, независимо от размера или типа шрифта.
Проведя тесты, кажется, я нашел решение. Если вы хотите, чтобы SVG <text был идентичен dom <div text, вам нужно установить:
доминант-базовая линия="центр" (не средний, не висящий или какой-либо другой).
Пример:
<svg width = "300" height = "300" xmlns = "http://www.w3.org/2000/svg">
<text x = "0" y = "40px" font-family = "verdana" text-anchor = "start" font-size = "80px" dominant-baseline = "central" fill = "black">First Line</text>
<text x = "0" y = "120px" font-family = "verdana" text-anchor = "start" font-size = "80px" dominant-baseline = "central" fill = "black">Second Line</text>
</svg>
И вам нужно установить положение текста Y, чтобы оно было вдвое меньше размера шрифта.
вот ручка кода, показывающая простое решение: https://codepen.io/Cristian-M/pen/VwJzxmK
позиция: абсолютная, т. е. вам не хватает : в случае div.