Как выбрать из path самый длинный под-path с помощью JS

Для более конкретного понимания задачи, немного пояснения сути проблемы.

При выполнении анимации с помощью изменения атрибута stroke-dasharray заданное время анимации не соблюдается.
Допустим задано время dur="10s", реально анимация длится всего 4 сек. Остальное время ничего не происходит.
И если за первой анимацией должна последовать вторая анимация, то возникает пауза мажду анимациями.
Это довольно распространенный эффект. Неважно в чем реализована анимация : Smil SVG или GSAP, либо в другом фреймворке JS, пауза наблюдается везде.

Это происходит в тех SVG, где в патче содержится несколько под-патчей.
Каждый под-патч начинается с команды M, заканчивается Z
В примере ниже четыре подпатча:

path d="M528.49...188.347V216.816ZM442.51...449.351ZM502.83ZM15.7462...500.745ZM15.7462...294Z"`

Если использовать 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 ничего не происходит (пауза), так как уже всё нарисовано.

Вопрос:

  1. Как с помощью JS разбить патч, содержащий несколько под-патчей на самостоятельные патчи, как в образце выше, вывести их формулы на экран
  2. Подсчитать длины для каждого и выбрать патч с максимальной длиной.

Update

Обратите внимание на отличный ответ @Grundy
Проблема пауз решена. Данное решение можно широко использовать на практике.
Теперь не нужно вручную искать самый длинный под-патч для устранения паузы между анимациями.

Просто подставляйте max значение, найденное скриптом, в атрибут animate values="0,1834;1834,0" и пауза между анимациями исчезнет, как в примере ниже

<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>


Ответы (1 шт):

Автор решения: Grundy

Разбить на пути можно с помощью регулярного выражения, например /(M[^Z]+Z)/g.

Чтобы подсчитать длину полученных путей, можно создать элемент path, и вызвать getTotalLength, для каждого куска, далее можно найти максимальный путь.

Например:

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 => {
    const el = document.createElementNS('http://www.w3.org/2000/svg', "path");
    el.setAttribute('d', p);
    return el.getTotalLength();
  });

console.log(paths);
console.log('max', Math.max(...paths));
console.log(p);
<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>

Update С примером вывода максимального пути и его длины

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>

→ Ссылка