Есть ли способ динамически обновлять атрибуты keyPoints и keyTimes SVG-animateMotion - PullRequest
0 голосов
/ 27 марта 2019

Я хочу анимировать несколько объектов SVG вдоль пути SVG. Цель заключается в создании анимированной версии Gartner HypeCycle для новых технологий. У меня есть старая анимация в powerpoint, но я хочу сделать ее дружественной к Интернету.

Каждый из объектов (который для HypeCycle будет в конце концов технологией) должен двигаться в соответствии с различным набором keyTimes и keyPoints, например, они должны двигаться на разных скоростях. У меня это работает в коде, который я разместил, и все хорошо в мире, анимация начинается, когда я нажимаю на кнопку, а затем непрерывно повторяется. Счастливые дни.

Однако я хотел бы добавить ползунок на страницу, чтобы вместо начала анимации, когда я нажимал на кнопку, он вместо этого контролировался ползунком и перемещал все точки вдоль пути в соответствии с набором определенные ключевые точки (возможно, в файле JSON).

Итак, я хочу сделать две вещи (1) управлять анимацией с помощью ползунка (но при этом для каждого круга должны быть определены значения keyPoints / keyTime, чтобы они двигались с различной скоростью) (2) получить keyTimes и keyPoints из файла JSON для каждого из объектов.

Спасибо

 <?xml version="1.0" encoding="iso-8859-1" ?> 
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG Tiny 1.1//EN"
    "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">


    <!-- style  change colour on hover -->
    <style>
    circle.circle1 {fill: rgb(0,0,22);transition: fill 0.5s ease;}
    circle.circle1:hover {fill: rgb(0,255,255);}
    circle.circle2 {fill: rgb(0,100,0);transition: fill 0.5s ease;}
    circle.circle2:hover {fill: rgb(0,255,255);}
    circle.circle3 {fill: rgb(100,0,0);transition: fill 0.5s ease;}
    circle.circle3:hover {fill: rgb(0,255,255);}
    </style>

    <svg width="400px" height="400px" viewBox="0 0 400 400" 
      xmlns="http://www.w3.org/2000/svg" version="1.1" baseProfile="tiny"
      xmlns:xlink="http://www.w3.org/1999/xlink">
 
    <!-- draw path and circles -->
    <path id="hypecurve" d="M12.967,349.469c15.107-87.283,25.932-180.142,54.214-264.61c31.17-93.095, 54.138, 17.688,65.096,53.934c11.354,37.558,23.177,74.976,34.309,112.6c26.534,89.679,79.275-25.286,92.183-45.57c11.749-18.462,20.938-43.699,69.798-48.289c70.298-6.604,177.054-4.848,224.858-5.774" fill="none" stroke="#444" stroke-width="3"/>
    <circle class= "circle1" id="c1" cx="0" cy="0" r="5" fill="#004" />
    <circle class= "circle2" id="c2" cx="0" cy="0" r="6" fill="#66f" />
    <circle class= "circle3" id="c3" cx="0" cy="0" r="7" fill="#00f" />
   
    <!-- button to start animation -->
    <rect id="startButton" style="cursor:pointer;"x="20" y="350" rx="5" height="25" width="80"fill="#EFEFEF" stroke="black" stroke-width="1" />
    <text x="60" y="370" text-anchor="middle" style="pointer-events:none;">Click me</text>

    <animateMotion xlink:href="#c1"
    begin="startButton.click"
    dur="10s"
    calcMode="linear"
    repeatDur="indefinite">
    <mpath xlink:href="#hypecurve" />

    </animateMotion>
    <!-- these are the attributes I want to update dynamically -->
    <animateMotion xlink:href="#c2"
    begin="startButton.click"
    dur="10s"
    calcMode="linear"
    keyPoints="0.3;0.35;0.375;0.4;0.45;0.5;0.6;0.61;0.7;0.8;1"
    keyTimes="0;0.19;0.36;0.51;0.64;0.75;0.84;0.91;0.96;0.99;1"
    repeatDur="indefinite">
    <mpath xlink:href="#hypecurve" />
    </animateMotion>
    <animateMotion xlink:href="#c3"
    begin="startButton.click"
    dur="10s"
    calcMode="linear"
    keyPoints="0.0;0.1;0.2;0.3;0.4;0.5;0.6;0.7;0.8;0.9"
    keyTimes="0;0.19;0.36;0.51;0.64;0.75;0.84;0.91;0.96;0.99"
    repeatDur="indefinite">
    <mpath xlink:href="#hypecurve" />
    </animateMotion>

    </svg>

1 Ответ

1 голос
/ 27 марта 2019

В следующем примере я использую ползунок для перемещения круга на пути. Я делаю это только для первого круга. Для этого я вычисляю длину пути, используя метод getTotalLength(), и вычисляю новую позицию круга, используя метод getPointAtLength().

Я не понимаю, как вы хотите объединить анимацию и ползунки. Пожалуйста, отредактируйте ваш вопрос, объясняя этот вопрос.

Если вам нужно динамически изменить некоторые атрибуты animateMotion, вы можете сделать это, используя метод setAttributeNS.

let trackLength = hypecurve.getTotalLength();

c1.setAttributeNS(null,"transform", "translate(12.967,349.469)")

itr1.addEventListener("input",()=>{
  let val = (itr1.value * trackLength)/ 100;
  let poz = hypecurve.getPointAtLength(val)
  //console.log(poz)
  c1.setAttributeNS(null,"transform", `translate(${poz.x},${poz.y})`)
})
svg{width:100vh; display:block;}


circle.circle1 {fill: rgb(0,0,22);}
circle.circle1:hover {fill: rgb(0,255,255);}
circle.circle2 {fill: rgb(0,100,0);}
circle.circle2:hover {fill: rgb(0,255,255);}
circle.circle3 {fill: rgb(100,0,0);}
circle.circle3:hover {fill: rgb(0,255,255);}
<svg viewBox="0 0 550 400" >

<!-- draw path and circles -->
<path id="hypecurve" d="M12.967,349.469c15.107-87.283,25.932-180.142,54.214-264.61c31.17-93.095,54.138,17.688,65.096,53.934c11.354,37.558,23.177,74.976,34.309,112.6c26.534,89.679,79.275-25.286,92.183-45.57c11.749-18.462,20.938-43.699,69.798-48.289c70.298-6.604,177.054-4.848,224.858-5.774" fill="none" stroke="#444" stroke-width="3"/>
<circle class= "circle1" id="c1" cx="0" cy="0" r="5" fill="#004" />
<circle class= "circle2" id="c2" cx="0" cy="0" r="6" fill="#66f" />
<circle class= "circle3" id="c3" cx="0" cy="0" r="7" fill="#00f" />

</svg>

c1: <input type="range" id="itr1" min="0" max="100" value="0" step="1" />

UPDATE

ОП обновил свой вопрос, и я не уверен, что понимаю, чего они хотят

В следующем примере я использую диапазон типов ввода #itr, чтобы изменить значение для keyTimes * 100. Значения keyTimes имеют значение от 0 до одного, #itr принимает значения от 0 до 100.

Для каждого момента времени я вычисляю положение круга на кривой и сохраняю его в массиве.

Пожалуйста, прочитайте комментарии в моем коде. Я надеюсь, что это то, что вы просили.

let trackLength = hypecurve.getTotalLength();

c1.setAttributeNS(null, "transform", "translate(12.967,349.469)");
// the position on the track at the key times: 11 values
let values = [];
// the position on the track at the key times: 101 values
let values1 = [0];

let keyTimes = [0, 19, 36, 51, 64, 75, 84, 91, 96, 99, 100]; //keyTimes * 100
let keyPoints = [0, 0.35, 0.375, 0.4, 0.45, 0.5, 0.6, 0.61, 0.7, 0.8, 1];

// create the values array
keyPoints.map(p => {
  values.push(trackLength * p);
});


// create the values1 array
for (let time = 0; time <100; time++) {
//for every value that the #itr can take
  for (let k = 0; k < keyTimes.length - 1; k++) {
    //the current value
    let curr = values[k];
    //the target value
    let target = values[k + 1];
    // the distance between the current value and the target value
    let dist = target - curr;
    // detect the interval of time we are in
    if (time >= keyTimes[k] && time < keyTimes[k + 1]) {
    // the increment for this time interval
    let increment = dist / (keyTimes[k + 1] - keyTimes[k]);
      // add a new value to the values1 array
      values1.push(values1[time]+increment);
      // break the loop
      break;
    }
  }
}


itr1.addEventListener("input", () => {
  let val = itr1.value;
  // get the new position on the curve
  let pos = hypecurve.getPointAtLength(values1[val]);
  c1.setAttributeNS(null, "transform", `translate(${pos.x},${pos.y})`);
});
svg{width:90vh; display:block;border:1px solid;overflow:visible}
#itr1{width:90vh;}

circle.circle1 {fill: rgb(0,0,22);}
circle.circle1:hover {fill: rgb(0,255,255);}
<svg viewBox="0 0 550 400" >

<!-- draw path and circles -->
<path id="hypecurve" d="M12.967,349.469c15.107-87.283,25.932-180.142,54.214-264.61c31.17-93.095,54.138,17.688,65.096,53.934c11.354,37.558,23.177,74.976,34.309,112.6c26.534,89.679,79.275-25.286,92.183-45.57c11.749-18.462,20.938-43.699,69.798-48.289c70.298-6.604,177.054-4.848,224.858-5.774" fill="none" stroke="#444" stroke-width="3"/>
<circle class= "circle1" id="c1" cx="0" cy="0" r="5" fill="#004" />


</svg>

<p>c1:<br><input type="range" id="itr1" min="0" max="100" value="0" step="1" /></p>
...