У меня есть текст, который движется по кругу SVG, и который масштабируется в зависимости от размера окна - Я хочу анимировать текст так, чтобы он вращался вокруг центра, как шатер. Для этого мой код в настоящее время выглядит так:
function Init(){
let wrap = document.getElementById('wrap');
let thePath = document.getElementById('thePath');
let ellipse = document.getElementById('ellipse');
let w = wrap.clientWidth;
let h = wrap.clientHeight;
ellipse.setAttributeNS(null,"viewBox",`0 0 ${w} ${h}`);
let d = `M${w/10},${h/2}A${4*w/10},${4*h/10} 0 0 0 ${9*w/10} ${5*h/10} A${4*w/10},${4*h/10} 0 0 0 ${w/10} ${5*h/10}`
thePath.setAttributeNS(null,"d", d)
}
window.setTimeout(function() {
Init();
window.addEventListener('resize', Init, false);
}, 15);
let so = 0
function Marquee(){
let tp = document.getElementById('tp');
requestAnimationFrame(Marquee)
tp.setAttributeNS(null,"startOffset",so+"%");
if(so >= 50){so = 0;}
so+= .05
}
Marquee()
<div id="wrap">
<svg id="ellipse" version="1.1" viewBox="0 0 1000 1000" preserveAspectRatio="none">
<path id="thePath" fill="transparent" d="M100,500A400,400 0 0 0 900 500 A400,400 0 0 0 100 500" />
<text stroke="black" font-size="20">
<textPath xlink:href="#thePath" dy="5" id="tp">
Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon •
Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon •
Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon •
</textPath>
</text>
</svg>
</div>
Это работает хорошо, за исключением того, что текст «проглатывается» в конце кривой (см. Прикрепленное изображение). Я бы хотел, чтобы он совершал полный оборот без каких-либо перерывов. Я попытался изменить переменную so
на отрицательное значение, но это привело к тому, что текст оказался слишком далеким, так что он медленно пополз на страницу. Я думал добавить фрагмент текста через определенное время, но это не учитывает движение startOffset
и, вероятно, не сработает
Спасибо за любые подсказки, в том числе и те, которые используют библиотеки JS или плагины!
Свободный перевод вопроса Animate marquee on SVG curve от участника @bruno.
Основная идея в том, что путь должен закручиваться дважды. И когда startOffset
равен 50%
, вы делаете его равным нулю. Также, поскольку длина пути изменяется при изменении размера окна, вам необходимо пересчитать размер шрифта. Я надеюсь, что это поможет.
function Init() {
let w = wrap.clientWidth;
let h = wrap.clientHeight;
ellipse.setAttributeNS(null, "viewBox", `0 0 ${w} ${h}`);
let d = `M${w / 10},${h / 2}A${4 * w / 10},${4 * h / 10} 0 0 0 ${9 *
w /
10} ${5 * h / 10} A${4 * w / 10},${4 * h / 10} 0 0 0 ${w / 10} ${5 *
h /
10} A${4 * w / 10},${4 * h / 10} 0 0 0 ${9 * w / 10} ${5 * h / 10} A${4 *
w /
10},${4 * h / 10} 0 0 0 ${w / 10} ${5 * h / 10}`;
thePath.setAttributeNS(null, "d", d);
let paths_length = thePath.getTotalLength();
tp.style.fontSize = paths_length / 205;
}
window.setTimeout(function() {
Init();
window.addEventListener("resize", Init, false);
}, 15);
let so = 0;
function Marquee() {
requestAnimationFrame(Marquee);
tp.setAttributeNS(null, "startOffset", so + "%");
if (so >= 50) {
so = 0;
}
so += 0.05;
}
Marquee();
#wrap{width:100vw; height:100vh}
svg {
background:#eee;
}
<div id="wrap">
<svg id="ellipse" version="1.1" viewBox="0 0 1000 1000">
<path id="thePath" fill="gold" d="M100,500A400,400 0 0 0 900 500 A400,400 0 0 0 100 500 A400,400 0 0 0 900 500 A400,400 0 0 0 100 500" />
<text stroke="#000000" >
<textPath xlink:href="#thePath" dy="5" id="tp">
Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon •
</textPath>
</text>
</svg>
</div>
ТС комментирует:
Этот сниппет, похоже, работает нормально для данного варианта использования, но когда я пытаюсь применить его к другому семейству шрифтов, размеры отключены, и два цикла начинают перекрываться.
Одно простое исправление - установить атрибут textLength
равным длине пути, деленному на 2 (поскольку путь наматывается дважды - это в два раза длиннее, чем должно быть). Также вам нужно использовать
lengthAdjust = "spacingAndGlyphs"
, который контролирует, как текст растягивается или сжимается до этой длины.
function Init() {
let w = wrap.clientWidth;
let h = wrap.clientHeight;
ellipse.setAttributeNS(null, "viewBox", `0 0 ${w} ${h}`);
let d = `M${w / 10},${h / 2}A${4 * w / 10},${4 * h / 10} 0 0 0 ${9 *
w /
10} ${5 * h / 10} A${4 * w / 10},${4 * h / 10} 0 0 0 ${w / 10} ${5 *
h /
10} A${4 * w / 10},${4 * h / 10} 0 0 0 ${9 * w / 10} ${5 * h / 10} A${4 *
w /
10},${4 * h / 10} 0 0 0 ${w / 10} ${5 * h / 10}`;
thePath.setAttributeNS(null, "d", d);
let path_length = thePath.getTotalLength();
//////////////////////////////////////////////////
tp.setAttributeNS(null,"textLength",path_length/2)
//////////////////////////////////////////////////
tp.style.fontSize = path_length / 200;
}
window.setTimeout(function() {
Init();
window.addEventListener("resize", Init, false);
}, 15);
let so = 0;
function Marquee() {
requestAnimationFrame(Marquee);
tp.setAttributeNS(null, "startOffset", so + "%");
if (so >= 50) {
so = 0;
}
so += 0.05;
}
Marquee();
#wrap{width:100vw; height:100vh}
svg {
background:#eee;
font-family:consolas;
}
<div id="wrap">
<svg id="ellipse" version="1.1" viewBox="0 0 1000 1000">
<path id="thePath" fill="gold" d="M100,500A400,400 0 0 0 900 500 A400,400 0 0 0 100 500 A400,400 0 0 0 900 500 A400,400 0 0 0 100 500" />
<text stroke="#000000" >
<textPath xlink:href="#thePath" id="tp" lengthAdjust="spacingAndGlyphs">Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • </textPath>
</text>
</svg>
</div>
Вам также может потребоваться добавить / удалить некоторые Coming Soon •, если текст становится слишком растянутым / сжатым.
Видимо последнее решение выше не работает в Firefox. Вот еще одно решение данной проблемы.
Изначально я устанавливаю размер шрифта намного больше, чем нужно. Затем я проверяю, больше ли длина текста, чем половина длины пути, и если да, я уменьшаю размер шрифта. Я делаю это в цикле while loop
:
function Init() {
let w = wrap.clientWidth;
let h = wrap.clientHeight;
ellipse.setAttributeNS(null, "viewBox", `0 0 ${w} ${h}`);
let d = `M${w / 10},${h / 2}A${4 * w / 10},${4 * h / 10} 0 0 0 ${9 *
w /
10} ${5 * h / 10} A${4 * w / 10},${4 * h / 10} 0 0 0 ${w / 10} ${5 *
h /
10} A${4 * w / 10},${4 * h / 10} 0 0 0 ${9 * w / 10} ${5 * h / 10} A${4 *
w /
10},${4 * h / 10} 0 0 0 ${w / 10} ${5 * h / 10}`;
thePath.setAttributeNS(null, "d", d);
let path_length = thePath.getTotalLength();
//begin at a bigger size than needed
let font_size = 100;
ellipse.style.fontSize = font_size+"px";
// while the text length is bigger than half path length
while(tp.getComputedTextLength() > path_length / 2 ){
//reduce the font size
font_size -=.25;
//reset the font size
ellipse.style.fontSize = font_size+"px";
}
}
window.setTimeout(function() {
Init();
window.addEventListener("resize", Init, false);
}, 15);
let so = 0;
function Marquee() {
requestAnimationFrame(Marquee);
tp.setAttributeNS(null, "startOffset", so + "%");
if (so >= 50) {
so = 0;
}
so += 0.02;
}
Marquee();
html, body {
margin: 0;
height: 100%;
width: 100%;
}
body {
font-family: "Arimo", sans-serif;
}
#wrap{
width:100%;
height:100%;
position: fixed;
top: 0;
left: 0;
}
text {
text-transform: uppercase;
font-weight: lighter;
}
<div id="wrap">
<svg id="ellipse" version="1.1" viewBox="0 0 1000 1000">
<path id="thePath" fill="transparent" d="M100,500A400,400 0 0 0 900 500 A400,400 0 0 0 100 500" />
<text stroke="black">
<textPath xlink:href="#thePath" dy="5" id="tp" lengthAdjust="spacingAndGlyphs">Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon • Coming Soon •</textPath>
</text>
</svg>
</div>
Свободный перевод ответа от участника @enxaneta.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Как реализовать нативным JavaScript, чтобы я брал переменную с input и ещё увеличивал по клику на единицу? Обычный input type="number" не подходит, потому...
В реализации приложения появилась трудностьМне нужно сделать систему регистрации, но смысла уходить в бек и курить spring и делать api не нужно
Мне не понятен следующий пункт Java conventions: "Four spaces should be used as the unit of indentationThe exact construction of the indentation (spaces vs