RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1272038
Accepted
Sevastopol'
Sevastopol'
Asked:2022-04-19 20:16:52 +0000 UTC2022-04-19 20:16:52 +0000 UTC 2022-04-19 20:16:52 +0000 UTC

当同一路径中有多个子补丁时,在滚动页面时绘制单个 SVG 元素的边框

  • 772

我有一个 SVG 元素,我在页面滚动上制作动画。以下是此类动画的示例:

var path = document.querySelectorAll('path'), 
        percentScroll;
$(path).each(function(){
  this.style.strokeDasharray = this.getTotalLength();
  this.style.strokeDashoffset = this.getTotalLength();
});
window.onscroll = function(){
  var viewportHeight = $(window).height();
var viewportTopX = $(window).scrollTop();
var viewportBottomX = viewportTopX + viewportHeight;
var elementHeight = $('#content').outerHeight();
var elementTopX = $('#content').offset().top;
var elementBottomX = elementTopX + elementHeight;
var percentScroll = (viewportBottomX - elementTopX) / ((viewportHeight + elementHeight) / 100);
  //console.log('%', 1 - percentScroll);
  foo = window.pageYOffset / (document.body.offsetHeight - window.innerHeight)
  //console.log('window', foo)
  $(path).each(function(){
    //console.log('math', Math.floor(this.getTotalLength() * (1 - foo)))
    this.style.strokeDashoffset = Math.floor(this.getTotalLength() * (1 - foo));
  });
};

//прогресс
let x = $('.progress');
$(window).on('scroll',function(){
  let st = $(window).scrollTop();
  let sa = $(document).height();
  let sb = $(window).height();
  let dd = Math.ceil(100 * (st / (sa - sb)));
  x.css({width : dd + '%'})
})
body {margin: 0; height: 3000px;}
.progress {position: fixed; top: 0; left: 0; width: 0%; height: 5px; background-color: red;}
.progress::after {content: "Крутим вниз"; white-space: nowrap; position: absolute; top: 15px; left: 15px;}

#svg {
  position: fixed;
  top: 10px;
  left: 10px;
}

path {
  fill: none;
  stroke: black;
  stroke-width: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="progress"></div><!--прогресс-->

<div id="content">
<svg id="svg" width="280px" height="280px" viewBox="0 0 1046 1280">
<g transform="translate(0,1280) scale(0.1,-0.1)" fill="none" stroke="black" stroke-width="100">
<path class="path" d="M6125 12741 c-387 -139 -597 -254 -908 -495 -233 -181 -331 -236 -422 -236 -17 0 -69 11 -115 24 -115 33 -387 82 -540 97 -236 22 -573 0 -849 -56 -161 -33 -227 -39 -285 -25 -32 7 -108 48 -220 117 -381 238 -783 418 -1165 522 l-74 20 7 -87 c47 -605 193 -1137 396 -1443 l40 -60 -25 -65 c-38 -101 -93 -307 -116 -439 -18 -97 -22 -161 -23 -330 0 -213 11 -317 49 -441 26 -86 51 -79 -277 -76 -331 4 -697 -10 -1025 -38 -215 -19 -564 -59 -572 -66 -2 -2 -1 -9 2 -17 4 -11 27 -10 138 4 476 63 1214 101 1600 84 l167 -7 36 -87 c20 -47 55 -121 80 -163 l43 -77 -36 -5 c-20 -3 -92 -13 -161 -21 -513 -65 -1082 -183 -1506 -315 -194 -59 -234 -76 -234 -95 0 -8 1 -15 3 -15 2 0 57 18 122 40 266 90 566 168 900 234 276 55 413 76 891 141 l41 6 58 -77 c31 -42 88 -109 126 -149 l69 -72 -118 -43 c-265 -97 -648 -277 -882 -415 -179 -105 -370 -235 -370 -251 0 -8 4 -14 9 -14 4 0 69 40 142 89 339 225 742 426 1156 577 l93 33 93 -94 c214 -215 287 -380 287 -652 0 -260 -53 -544 -204 -1093 -70 -254 -124 -480 -156 -652 -157 -835 -108 -1553 159 -2358 89 -266 155 -431 312 -782 274 -611 310 -727 354 -1146 22 -205 36 -683 33 -1072 l-3 -345 -88 -7 c-109 -8 -232 -32 -290 -57 -66 -28 -111 -73 -143 -143 -26 -56 -29 -74 -29 -158 0 -74 4 -103 19 -129 67 -123 257 -196 611 -235 1310 -143 2603 -163 3865 -61 684 56 1558 169 1680 218 349 141 670 737 925 1721 185 712 354 1713 371 2201 24 650 -166 1275 -569 1880 -207 310 -356 484 -807 940 -477 483 -631 662 -805 935 -143 224 -273 537 -311 747 -35 198 -28 460 18 673 47 221 145 445 262 601 203 269 554 486 916 565 66 15 125 19 260 18 157 0 185 -3 274 -27 228 -61 359 -160 386 -292 26 -124 -55 -283 -190 -373 -135 -91 -278 -109 -464 -59 -131 35 -183 41 -236 27 -75 -19 -115 -52 -150 -124 -30 -61 -32 -71 -28 -144 6 -97 36 -158 119 -238 71 -67 209 -135 321 -158 99 -20 253 -20 358 0 267 50 581 244 737 454 209 281 260 673 133 1013 -70 186 -311 431 -525 533 -206 98 -593 140 -925 99 -549 -68 -1041 -298 -1400 -654 -243 -242 -405 -492 -496 -769 l-37 -113 -56 6 c-135 13 -486 25 -753 25 -271 0 -289 1 -284 18 44 150 60 274 60 457 0 244 -33 410 -129 650 l-44 109 49 66 c252 342 406 715 456 1103 22 176 12 628 -15 627 -3 -1 -78 -27 -166 -59z m709 -3021 c88 -6 161 -11 162 -13 1 -1 -6 -42 -17 -92 -25 -119 -49 -305 -49 -377 0 -32 -3 -58 -6 -58 -3 0 -63 13 -132 29 -251 58 -593 119 -872 156 -69 9 -135 18 -147 21 -21 4 -20 8 32 117 29 61 61 138 71 169 18 53 21 57 54 62 57 7 732 -3 904 -14z m-604 -436 c173 -28 390 -71 666 -131 20 -5 21 -13 28 -166 27 -621 177 -1043 583 -1650 253 -378 580 -768 1023 -1222 273 -279 343 -357 438 -484 238 -315 387 -652 453 -1019 72 -406 36 -1045 -97 -1712 -160 -804 -476 -1596 -709 -1772 -91 -69 -196 -83 -285 -38 -126 65 -154 220 -111 605 12 105 37 318 56 475 62 523 75 690 75 955 0 641 -136 1169 -474 1844 -344 687 -592 1023 -1179 1597 -293 287 -426 407 -721 653 -143 120 -297 251 -340 292 -240 224 -359 512 -373 899 -4 114 -2 159 11 210 21 86 73 186 148 288 54 75 64 83 87 79 46 -10 341 -136 516 -222 207 -101 374 -196 564 -322 107 -71 146 -92 154 -84 8 8 8 14 2 19 -364 252 -809 488 -1172 621 -40 15 -60 27 -56 35 4 6 41 61 84 121 42 61 88 131 103 156 l26 46 143 -19 c78 -10 239 -34 357 -54z"/>
</g>
</svg>
</div>

在此示例中,SVG 元素包含一个补丁 ( path),但很明显,绘图不是从起点到其路径的终点,而是同时在不同的位置。为什么会发生这种情况,它取决于什么?是否可以将一个补丁的公式分解为多个部分并调整它们的绘制顺序?对在加载文档和滚动页面时执行此类动画感兴趣。还对两种实现的详细描述感兴趣。

javascript
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    Alexandr_TT
    2022-04-24T04:59:35Z2022-04-24T04:59:35Z

    在这个例子中,SVG 元素包含一个补丁(路径),但很明显,绘图不是从起点到路径终点,而是同时在不同的地方。为什么会发生这种情况,它取决于什么?

    如果仔细查看路径公式,您会注意到命令是重复的zm,这意味着一个子补丁的结束z和第二个子补丁的开始m(moveto)。

    SVG 预处理器moveto并行执行命令,就像一次用几支笔进行绘制一样。因此,会出现一些不愉快的细微差别。

    让我用另一个更典型的例子来解释:

    假设时间已定dur="10s",实际上动画仅持续4 сек。其余时间什么都没有发生。如果第一个动画应该跟在第二个动画之后,那么两个动画6сек之间会有一个相等的停顿。不提供应用程序开发人员的暂停,并且在画线结束后是非常不希望的。这是一个相当普遍的效果。用什么动画实现并不重要:Smil SVG 或 GS​​AP,或者在另一个 JS 框架中,到处都可以观察到停顿。

    在下面的示例中,如果您使用getTotalLength(),则将显示所有子补丁的总长度 -4987px

    var path = document.querySelector('#path1');
      var len = Math.round(path.getTotalLength() );
    console.log("Длина пути - " + len);
    <svg width="588" height="588" viewBox="0 0 588 588" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path id="path1" d="M528.49 216.816L231.47 294.976L526.535 365.323V404.373L572.254 294L528.49 188.347V216.816ZM442.51 449.351L153.306 380.956V203.134L428.832 138.652L159.168 87.8455L87.6862 467.68L97.2454 490.755L262.441 559.18L442.51 449.351ZM502.83 500.745C502.554 501.157 502.242 501.539 501.89 501.89C501.539 502.242 501.157 502.554 500.745 502.83C500.33 503.105 499.896 503.338 499.438 503.53L296.894 587.426C296.436 587.615 295.962 587.758 295.474 587.854C294.989 587.95 294.498 588 294 588C293.502 588 293.011 587.95 292.526 587.854C292.038 587.758 291.564 587.615 291.106 587.426L88.5624 503.53C88.1044 503.338 87.6696 503.105 87.258 502.83C86.8431 502.554 86.4614 502.242 86.1096 501.89C85.7577 501.539 85.4457 501.157 85.1703 500.745C84.8948 500.33 84.6624 499.896 84.4699 499.438L0.574233 296.894C0.385039 296.436 0.242271 295.962 0.146014 295.477C0.0497582 294.989 0 294.498 0 294C0 293.502 0.0497582 293.011 0.146014 292.526C0.242271 292.038 0.385039 291.564 0.574233 291.106L84.4699 88.5625C84.6624 88.1044 84.8948 87.6696 85.1703 87.258C85.4457 86.8431 85.7577 86.4614 86.1096 86.1096C86.4614 85.761 86.8431 85.4457 87.258 85.1703C87.6696 84.8948 88.1044 84.6624 88.5624 84.4699L291.106 0.574233C291.564 0.385039 292.038 0.242321 292.526 0.146065C293.011 0.0498088 293.502 0 294 0C294.498 0 294.989 0.0498088 295.474 0.146065C295.962 0.242321 296.436 0.385039 296.894 0.574233L499.438 84.4699C499.896 84.6624 500.33 84.8948 500.745 85.1703C501.157 85.4457 501.539 85.761 501.89 86.1096C502.242 86.4614 502.554 86.8431 502.83 87.258C503.105 87.6696 503.338 88.1044 503.53 88.5625L587.426 291.106C587.615 291.564 587.758 292.038 587.854 292.526C587.95 293.011 588 293.502 588 294C588 294.498 587.95 294.989 587.854 295.477C587.758 295.962 587.615 296.436 587.426 296.894L503.53 499.438C503.338 499.896 503.105 500.33 502.83 500.745ZM15.7462 294L59.5096 399.653V188.347L15.7462 294Z" fill="none" stroke="#0091E2" stroke-width="2">
         <!-- Анимация рисования линий    -->
       <animate id="dash" attributeName="stroke-dasharray"  dur="10s" values="0,4987;4987,0" fill="freeze" />  
             <!-- Анимвация заполнения цветом -->
      <animate attributeName="fill"  dur="1s" begin="dash.end" values="white;#0091E2" fill="freeze"  />  
    </path>
    
    </svg>

    在上面的示例中,可以清楚地观察到这种效果 - 绘制线条之后,在第二个动画开始之前有一个暂停 - 绘画。

    如果我们将每个子补丁转换为自制补丁并用 测量每个子补丁的长度getTotalLength(),我们得到:

    <path id="path1" d="M528.49 ...216.816Z" />  ---- Длина пути - 912px
    <path id="path2" d="M442.51 ...449.351Z" />  ---- Длина пути - 1834px
    <path id="path3" d="M502.83 ...500.745Z" />  ---- Длина пути - 1801px
    <path id="path4" d="M15.7462 ...294Z" />     ---- Длина пути - 440px  
    

    总之4987px,就像上面的片段一样

    10s因此,给定的动画时间与动画的实际持续时间之间存在差异的原因就很清楚了4s。
    绘制动画同时进行 4 行,整个动画在最长的补丁绘制完成后结束1834px。只需 4 秒。此外,其余的6s都没有发生任何事情(暂停),因为所有内容都已绘制完毕。
    解决方案很明显:

    选择最长的子补丁 - 1834px- 这是形状的外轮廓,并将其值代入动画命令stroke-dashoffset最长的子补丁将被绘制,颜色填充动画将不间断地开始:

    <svg width="588" height="588" viewBox="0 0 588 588" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path id="path1" d="M528.49 216.816L231.47 294.976L526.535 365.323V404.373L572.254 294L528.49 188.347V216.816ZM442.51 449.351L153.306 380.956V203.134L428.832 138.652L159.168 87.8455L87.6862 467.68L97.2454 490.755L262.441 559.18L442.51 449.351ZM502.83 500.745C502.554 501.157 502.242 501.539 501.89 501.89C501.539 502.242 501.157 502.554 500.745 502.83C500.33 503.105 499.896 503.338 499.438 503.53L296.894 587.426C296.436 587.615 295.962 587.758 295.474 587.854C294.989 587.95 294.498 588 294 588C293.502 588 293.011 587.95 292.526 587.854C292.038 587.758 291.564 587.615 291.106 587.426L88.5624 503.53C88.1044 503.338 87.6696 503.105 87.258 502.83C86.8431 502.554 86.4614 502.242 86.1096 501.89C85.7577 501.539 85.4457 501.157 85.1703 500.745C84.8948 500.33 84.6624 499.896 84.4699 499.438L0.574233 296.894C0.385039 296.436 0.242271 295.962 0.146014 295.477C0.0497582 294.989 0 294.498 0 294C0 293.502 0.0497582 293.011 0.146014 292.526C0.242271 292.038 0.385039 291.564 0.574233 291.106L84.4699 88.5625C84.6624 88.1044 84.8948 87.6696 85.1703 87.258C85.4457 86.8431 85.7577 86.4614 86.1096 86.1096C86.4614 85.761 86.8431 85.4457 87.258 85.1703C87.6696 84.8948 88.1044 84.6624 88.5624 84.4699L291.106 0.574233C291.564 0.385039 292.038 0.242321 292.526 0.146065C293.011 0.0498088 293.502 0 294 0C294.498 0 294.989 0.0498088 295.474 0.146065C295.962 0.242321 296.436 0.385039 296.894 0.574233L499.438 84.4699C499.896 84.6624 500.33 84.8948 500.745 85.1703C501.157 85.4457 501.539 85.761 501.89 86.1096C502.242 86.4614 502.554 86.8431 502.83 87.258C503.105 87.6696 503.338 88.1044 503.53 88.5625L587.426 291.106C587.615 291.564 587.758 292.038 587.854 292.526C587.95 293.011 588 293.502 588 294C588 294.498 587.95 294.989 587.854 295.477C587.758 295.962 587.615 296.436 587.426 296.894L503.53 499.438C503.338 499.896 503.105 500.33 502.83 500.745ZM15.7462 294L59.5096 399.653V188.347L15.7462 294Z" fill="none" stroke="#0091E2" stroke-width="2">
         <!-- Анимация рисования линий    -->
       <animate id="dash" attributeName="stroke-dasharray"  dur="10s" values="0,1834;1834,0" fill="freeze" />  
             <!-- Анимвация заполнения цветом -->
      <animate attributeName="fill"  dur="1s" begin="dash.end" values="white;#0091E2" fill="freeze"  />  
    </path>
    
    </svg>

    但是如果有很多补丁,那么选择其中最长的一个是非常不方便和费力的。

    为了自动执行查找和打印最长路径的任务,@Grundy♦创建了一个非常有用的脚本:

    var path = document.querySelector('#path1');
    var len = Math.round(path.getTotalLength());
    console.log("Длина пути - " + len);
    
    var paths = path.getAttribute('d')
      .match(/(M[^Z]+Z)/g)
      .map(p => {
        console.log(p); // добавлять надо было сюда
        const el = document.createElementNS('http://www.w3.org/2000/svg', "path");
        el.setAttribute('d', p);
        return el.getTotalLength();
      });
    
    console.log('пути', paths);
    console.log('максимальная длина', Math.max(...paths));
    
    
    var maxPath = path.getAttribute('d')
      .match(/(M[^Z]+Z)/g)
      .reduce((acc, p) => {
        const el = document.createElementNS('http://www.w3.org/2000/svg', "path");
        el.setAttribute('d', p);
        var pathLength = el.getTotalLength();
        if (pathLength > acc.maxLength) {
          return {
            maxPath: p,
            maxLength: pathLength
          }
        }
    
        return acc;
      }, {
        maxPath: '',
        maxLength: 0
      });
    
    console.log('максимальный путь с длинной', maxPath);
    <svg width="588" height="588" viewBox="0 0 588 588" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path id="path1" d="M528.49 216.816L231.47 294.976L526.535 365.323V404.373L572.254 294L528.49 188.347V216.816ZM442.51 449.351L153.306 380.956V203.134L428.832 138.652L159.168 87.8455L87.6862 467.68L97.2454 490.755L262.441 559.18L442.51 449.351ZM502.83 500.745C502.554 501.157 502.242 501.539 501.89 501.89C501.539 502.242 501.157 502.554 500.745 502.83C500.33 503.105 499.896 503.338 499.438 503.53L296.894 587.426C296.436 587.615 295.962 587.758 295.474 587.854C294.989 587.95 294.498 588 294 588C293.502 588 293.011 587.95 292.526 587.854C292.038 587.758 291.564 587.615 291.106 587.426L88.5624 503.53C88.1044 503.338 87.6696 503.105 87.258 502.83C86.8431 502.554 86.4614 502.242 86.1096 501.89C85.7577 501.539 85.4457 501.157 85.1703 500.745C84.8948 500.33 84.6624 499.896 84.4699 499.438L0.574233 296.894C0.385039 296.436 0.242271 295.962 0.146014 295.477C0.0497582 294.989 0 294.498 0 294C0 293.502 0.0497582 293.011 0.146014 292.526C0.242271 292.038 0.385039 291.564 0.574233 291.106L84.4699 88.5625C84.6624 88.1044 84.8948 87.6696 85.1703 87.258C85.4457 86.8431 85.7577 86.4614 86.1096 86.1096C86.4614 85.761 86.8431 85.4457 87.258 85.1703C87.6696 84.8948 88.1044 84.6624 88.5624 84.4699L291.106 0.574233C291.564 0.385039 292.038 0.242321 292.526 0.146065C293.011 0.0498088 293.502 0 294 0C294.498 0 294.989 0.0498088 295.474 0.146065C295.962 0.242321 296.436 0.385039 296.894 0.574233L499.438 84.4699C499.896 84.6624 500.33 84.8948 500.745 85.1703C501.157 85.4457 501.539 85.761 501.89 86.1096C502.242 86.4614 502.554 86.8431 502.83 87.258C503.105 87.6696 503.338 88.1044 503.53 88.5625L587.426 291.106C587.615 291.564 587.758 292.038 587.854 292.526C587.95 293.011 588 293.502 588 294C588 294.498 587.95 294.989 587.854 295.477C587.758 295.962 587.615 296.436 587.426 296.894L503.53 499.438C503.338 499.896 503.105 500.33 502.83 500.745ZM15.7462 294L59.5096 399.653V188.347L15.7462 294Z" fill="none" stroke="#0091E2" stroke-width="2">
         <!-- Анимация рисования линий    -->
       <animate id="dash" attributeName="stroke-dasharray"  dur="10s" values="0,4987;4987,0" fill="freeze" />  
             <!-- Анимвация заполнения цветом -->
      <animate attributeName="fill"  dur="1s" begin="dash.end" values="white;#0091E2" fill="freeze"  />  
    </path>
    
    </svg>

    • 2

相关问题

  • 第二个 Instagram 按钮的 CSS 属性

  • 由于模糊,内容不可见

  • 弹出队列。消息显示不正确

  • 是否可以在 for 循环中插入提示?

  • 如何将 JSON 请求中的信息输出到数据表 Vuetify vue.js?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    表格填充不起作用

    • 2 个回答
  • Marko Smith

    提示 50/50,有两个,其中一个是正确的

    • 1 个回答
  • Marko Smith

    在 PyQt5 中停止进程

    • 1 个回答
  • Marko Smith

    我的脚本不工作

    • 1 个回答
  • Marko Smith

    在文本文件中写入和读取列表

    • 2 个回答
  • Marko Smith

    如何像屏幕截图中那样并排排列这些块?

    • 1 个回答
  • Marko Smith

    确定文本文件中每一行的字符数

    • 2 个回答
  • Marko Smith

    将接口对象传递给 JAVA 构造函数

    • 1 个回答
  • Marko Smith

    正确更新数据库中的数据

    • 1 个回答
  • Marko Smith

    Python解析不是css

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5