#javascript #html #css #svg
#javascript #HTML #css #svg
Вопрос:
Мне трудно поверить, что не существует стандартного и простого (и независимого от браузера) способа нанести эффект штриха вокруг внешней части текста с помощью CSS.
У нас есть, -webkit-text-stroke
но по какой-то странной причине обводка сосредоточена вокруг границы текста, а не за его пределами, как упоминалось здесь.
Итак, я пытаюсь реализовать обходной путь, основанный на этой идее, которая помещает обводочный текст в псевдоэлемент позади исходного текста без обводки. Я продемонстрировал это в этом jsfiddle со следующим кодом:
var jQueryAttr = function(selector, attr, setterFunction) {
document.querySelectorAll(selector).forEach((el, i) => {
el.setAttribute(attr, setterFunction.call(el, i, attr));
});
};
jQueryAttr('.myclass', 'data-myclass', function(index, attr) {
return this.innerHTML;
});
body {
background: none;
}
.basic {
color: rgba(186, 218, 85, 1);
font: 2.5em Georgia, serif;
}
.myclass {
position: relative;
background: transparent;
z-index: 0;
}
.myclass::before {
content: attr(data-myclass);
position: absolute;
-webkit-text-stroke: 0.2em rgba(0, 0, 0, 1);
z-index: -1;
}
.anotherclass {
-webkit-text-stroke: 0.2em rgba(0, 0, 0, 1);
}
<p class="basic">Text without any stroke</p>
<p class="myclass basic">Text with outer stroke</p>
<p class="anotherclass basic">Without the trick applied</p>
Это работает нормально, за исключением того, что если сам текст имеет некоторую прозрачность, то вы видите темную обводку под ним, как показано в этом варианте (единственное изменение — привязать непрозрачность текста обратно к 0.3). Как вы можете видеть, черный цвет из обводки элемента проникает в текст (в верхней строке).
Итак, есть ли другой изящный трюк, который можно использовать для преодоления этой проблемы? Я предполагаю, что можно добавить еще один псевдоэлемент между обводным слоем и не обводным слоем с чистым белым текстом (или таким, который соответствует фону), но я хотел бы применить этот метод в контексте, где я заранее не знаю цвет фона … например, когда он накладывается на произвольное выбранное пользователем изображение. По этой причине я установил background
для body
в none
приведенном выше примере.
Ответ №1:
Вот идея, которую вы можете рассмотреть mix-blend-mode
, и комбинация text-shadow для приближения к этому. Самое сложное — настроить тень на случай, если вы хотите обводку большего размера:
.text > span {
font-family:sans-serif;
font-size:60px;
font-weight: bold;
color:#fff; /*use white*/
/*create the stroke around text*/
text-shadow:
2px 0 0px #000,
0 2px 0px #000,
2px 2px 0px #000,
-2px 0 0px #000,
0 -2px 0px #000,
-2px -2px 0px #000,
-2px 2px 0px #000,
2px -2px 0px #000;
mix-blend-mode: darken; /*everything is more dark than white so we always see the background */
}
.text {
display:inline-block;
padding:20px;
background:linear-gradient(to right,red, blue);
}
<div class="text"><span>Some text here</span></div>
Использование переменной CSS, вероятно, упростит настройку:
.text > span {
font-family:sans-serif;
font-size:60px;
font-weight: bold;
color:#fff; /*use white*/
/*create the stroke around text*/
text-shadow:
var(--s,2px) 0 var(--c,0) #000,
0 var(--s,2px) var(--c,0) #000,
var(--s,2px) var(--s,2px) var(--c,0) #000,
calc(-1*var(--s,2px)) 0 var(--c,0) #000,
0 calc(-1*var(--s,2px)) var(--c,0) #000,
calc(-1*var(--s,2px)) calc(-1*var(--s,2px)) var(--c,0) #000,
calc(-1*var(--s,2px)) var(--s,2px) var(--c,0) #000,
var(--s,2px) calc(-1*var(--s,2px)) var(--c,0) #000;
mix-blend-mode: darken; /*everything is more dark than white so we always see the background */
}
.text {
display:inline-block;
padding:20px;
background:linear-gradient(to right,red, blue);
background-size:cover;
background-position:center;
}
<div class="text"><span>Some text here</span></div>
<div class="text" style="--s:4px;--c:2px;background-image:url(https://picsum.photos/800/600?image=1069)"><span>Some text here</span></div>
<div class="text" style="--s:6px;--c:4px;background-image:url(https://picsum.photos/800/600?image=1051)"><span>Some text here</span></div>
Если вам нужен прозрачный цвет для текста, вы можете дублировать его с помощью псевдоэлемента:
.text > span {
font-family:sans-serif;
font-size:60px;
font-weight: bold;
position:relative;
display:inline-block;
}
.text > span::before,
.text > span::after {
content:attr(data-text);
}
.text > span::before {
color:#fff; /*use white*/
/*create the stroke around text*/
text-shadow:
var(--s,2px) 0 var(--c,0) #000,
0 var(--s,2px) var(--c,0) #000,
var(--s,2px) var(--s,2px) var(--c,0) #000,
calc(-1*var(--s,2px)) 0 var(--c,0) #000,
0 calc(-1*var(--s,2px)) var(--c,0) #000,
calc(-1*var(--s,2px)) calc(-1*var(--s,2px)) var(--c,0) #000,
calc(-1*var(--s,2px)) var(--s,2px) var(--c,0) #000,
var(--s,2px) calc(-1*var(--s,2px)) var(--c,0) #000;
mix-blend-mode: darken; /*everything is more dark than white so we always see the background */
}
.text > span::after {
position:absolute;
top:0;
left:0;
color:rgba(0,255,0,0.4);
}
.text {
display:inline-block;
padding:20px;
background:linear-gradient(to right,red, blue);
background-size:cover;
background-position:center;
}
<div class="text"><span data-text="Some text here"></span></div>
<div class="text" style="--s:4px;--c:2px;background-image:url(https://picsum.photos/800/600?image=1069)"><span data-text="Some text here"></span></div>
<div class="text" style="--s:6px;--c:4px;background-image:url(https://picsum.photos/800/600?image=1051)"><span data-text="Some text here"></span></div>
Комментарии:
1. В вашем подходе, если бы мы действительно хотели определенный цвет для текста (не полностью прозрачный), например,
rgba(186, 218, 85, 0.5)
, тогда вы бы предложили использовать::before
трюк согласно моему сообщению, чтобы поместить цветной текст (без обводки) сверху, а фон (обводкой) ниже? Кроме того, я уже пробовал обходной путь с несколькими тенями раньше (с более чем 4 или 8 тенями … для плавного эффекта), но причина, по которой я отклонил его, отчасти в том, что мне нужно контролировать непрозрачность штриха … трудно достичь с перекрывающимися тенями, когда непрозрачность имеет тенденцию нарастать до1.0
.2. @drmrbrewer да, правильно, это недостаток использования shadow в данной ситуации. Нелегко контролировать и сложно добавить непрозрачность .. для вашего цвета текста, да, я бы использовал псевдоэлемент с текстом выше (или ниже) прозрачного родительского элемента с некоторой непрозрачностью … кстати, я постараюсь отредактировать свой ответ на случай, если я нашел трюк с непрозрачностью
3. А что произойдет, если фона не будет?
4. @drmrbrewer у вас будет белый цвет.. смешайте два цвета .. хитрость при использовании white и darken заключается в том, что во всех случаях мы выбираем фон, поскольку он самый темный (все более темное, чем белое)… при отсутствии фона будет выбран белый
5. @Kaiido нет, я должен добавить display: inline-block, забудьте об этом.. перенос необходим и должен работать нормально, но псевдоэлемент на встроенном элементе не подходит. (отредактировано)
Ответ №2:
Самым простым и с наилучшей поддержкой браузера на самом деле может быть SVG.
Вы можете настроить примерно то же самое, что вы делали с ::before, с той разницей, что версия с фоновым штрихом может иметь маску, которая позволяет видеть только внешнюю линию.
Оттуда вы можете просто добавить копию того же текста поверх, и вы сможете применить непрозрачность по своему усмотрению, как к обводке, так и к заливке:
body{
background-image:url(https://picsum.photos/800/200?image=1051);
font-family: sans-serif;
}
svg {
font-size: 40px;
font-weight: bold;
}
.textStroke {
stroke: black;
stroke-width: 12px;
stroke-linejoin: round;
}
.visibleText {
fill: rgba(186, 218, 85, 1);
transition: fill-opacity .5s linear;
}
.visibleText:hover {
fill-opacity: 0;
}
<svg width="350">
<defs>
<!-- we type it only once -->
<text x="10" y="55" id="txt">Text with outline</text>
<mask id="mymask">
<!-- white => visible, black => tansparent -->
<rect x="0" y="0" width="450" height="70" fill="#FFF"></rect>
<use xlink:href="#txt" fill="#000"/>
</mask>
</defs>
<!-- our stroked text, with the mask -->
<use xlink:href="#txt" mask="url(#mymask)" class="textStroke"/>
<!-- fill version -->
<use xlink:href="#txt" class="visibleText"/>
</svg>
Комментарии:
1. согласен, я был уверен, что SVG легко решит это. Жаль, что мы не можем применить непрозрачность к штриху из-за перекрытия (OP также хотел, чтобы это прокомментировало мой ответ)
2. да, но это приведет к неоднородному цвету, потому что у нас будет перекрытие между штрихами соседних букв.
3. @TemaniAfif О, теперь я вижу, в Chrome … stoopids. Отлично работает на FF, я проверю, что там происходит, но настройка
opacity
работает везде: jsfiddle.net/rv7sjLof4. ах, опять конфликт между Chrome и Fiferox .. и да, забудьте о непрозрачности, которая здесь тривиальна. Я пытался использовать цвета rgba:x
5. Что касается непрозрачности штриха текста, то это было бы ошибкой Chrome, я открою ее, когда у меня будет время найти диапазон регрессии (webkit исправляет, так что это может быть регрессия). Они делают это правильно для путей . Adn хотя я не понимаю, почему нельзя было контролировать
opacity
каждый элемент (обводку и заливку) вместо того, чтобы контролировать альфа-значение их <color>, этого также можно достичь даже в глючном Chrome с помощью двух масок , но тогда мы сможем повеселиться .
Ответ №3:
Решение с использованием фильтров SVG
Чтобы получить обводку вокруг текста, вы можете использовать комбинированный SVG-фильтр, состоящий из последовательно примененных фильтров: feMorphology
, feComposite
и feColorMatrix
.
body{
background-image:url(https://picsum.photos/800/800?image=1061);
background-size:cover;
font-family: serif;
}
<svg viewBox="0 0 350 350" >
<defs>
<filter id="groupborder" filterUnits="userSpaceOnUse"
x="-20%" y="-20%" width="300" height="300">
<feMorphology operator="dilate" in="SourceAlpha"
radius="5" result="e1" />
<feMorphology operator="dilate" in="SourceAlpha"
radius="2" result="e2" />
<feComposite in="e1" in2="e2" operator="xor"
result="outline"/>
<feColorMatrix type="matrix" in="outline"
values="0 0 0 0 0.1
0 0 0 0 0.2
0 0 0 0 0.2
0 0 0 1 0" result="outline2"/>
<feComposite in="outline2" in2="SourceGraphic"
operator="over" result="output"/>
</filter>
</defs>
<g id="group" filter="url(#groupborder)">
<text x="10" y="100" stroke-width="1" fill="#1D3A56" font-family="serif" font-size="30" font-weight="700" > Text with outline </text>
</g>
</svg>
Ответ №4:
CSS-свойство paint-order могло бы сделать свое дело:
.stroke {
-webkit-text-stroke: 0.2em rgba(0, 0, 0, 1);
paint-order: stroke fill;
}
.basic {
color: rgba(186, 218, 85, 1);
font: 2.5em Georgia, serif;
}
<span class="basic">Text without stroke</span><br>
<span class="basic stroke">Text with stroke</span>
Примечание: Это экспериментальная технология
К сожалению, это поддерживается не каждым браузером (таблица совместимости с браузерами), и поведение может измениться в будущем.
Результат будет выглядеть следующим образом в Firefox 65.0.2:
Комментарии:
1. можете ли вы поделиться скриншотом результата, чтобы мы могли увидеть, как это выглядит? поддержка кажется очень плохой
2. Хорошие новости: поддержка браузера, похоже, повышается. 🙂