SVG 曲线上的动画选取框

bru*_*uno 6 html javascript css svg

我有一个环绕 SVG 圆圈的文本,该圆圈根据窗口大小进行缩放 - 感谢用户 enxaneta /sf/answers/3922537181/。我想为文本设置动画,使其像选取框一样围绕中心旋转。为此,我的代码目前如下所示:

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()
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

这运行良好,除了文本在曲线末端被“吞下”(见附图)。我想让它在没有任何中断的情况下进行完整的旋转。我曾尝试将so变量更改为负值,但这最终导致文本偏移太远,因此它只会慢慢爬到页面上。我想在一段时间后添加一个文本片段,但这不会考虑到startOffset移动并且可能不起作用......

感谢任何提示,也感谢那些使用 JS 库或插件的人!

在此处输入图片说明

enx*_*eta 6

主要思想是路径必须盘绕两次。当startOffset为 50% 时,您将其设为 0。此外,由于在调整窗口大小时路径的长度会发生变化,因此您需要重新计算字体大小。我希望它有帮助。

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();
Run Code Online (Sandbox Code Playgroud)
#wrap{width:100vw; height:100vh}
  svg {
    background:#eee;
  }
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

更新

OP 正在评论:

这个片段似乎适用于用例,但是当我尝试将它应用于另一个字体系列时,尺寸关闭并且两个循环开始重叠

一个简单的解决方法是将属性设置为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();
Run Code Online (Sandbox Code Playgroud)
#wrap{width:100vw; height:100vh}
  svg {
    background:#eee;
    font-family:consolas;
  }
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

Coming Soon •如果文本变得过于拉伸/压扁,您可能还需要添加/删除一些内容。

更新 2

显然,最后一个解决方案不适用于 Firefox。这是该问题的另一种解决方案。

最初,我将字体大小设置得比需要的大得多。接下来我检查文本长度是否大于路径长度的一半,如果是,我将减小字体大小。我在一个while循环中这样做:

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();
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)