【问题标题】:Is there a way to dynamically update the SVG-animateMotion keyPoints and keyTimes attributes有没有办法动态更新 SVG-animateMotion keyPoints 和 keyTimes 属性
【发布时间】:2019-03-27 13:32:23
【问题描述】:

我想沿 SVG 路径为多个 SVG 对象设置动画。目标是为新兴技术创建 Gartner HypeCycle 的动画版本。我在 powerpoint 中有一个旧动画,但想让它对网络友好。

每个对象(对于 HypeCycle 来说最终将是一项技术)需要根据一组不同的 keyTimes 和 keyPoints 移动,例如他们需要以不同的速度移动。我在我发布的代码中有这个工作,世界上一切都很好,当我点击一个按钮时动画开始,然后连续循环。快乐的日子。

但是,我想在页面上添加一个滑块,这样当我单击按钮时,动画就不会开始,而是由滑块控制,并根据一组沿路径移动所有点定义的关键点(可能在 JSON 文件中)。

所以我想做两件事 (1) 使用滑块控制动画(但仍然为每个圆圈定义了 keyPoints/keyTimes,因此它们以不同的速度移动) (2) 从 JSON 文件中获取每个对象的 keyTimes 和 keyPoints。

谢谢

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

【问题讨论】:

    标签: svg svg-animate


    【解决方案1】:

    在下一个示例中,我使用滑块在路径上移动一个圆圈。我只为第一个圈子做这件事。为此,我使用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" />

    更新

    OP 更新了他们的问题,我不太确定我理解他们想要什么

    在以下示例中,我使用输入类型范围 #itr 来更改 keyTimes * 100 的值。keyTimes 的值是从 0 到 1,#itr 的值是从 0 到100.

    对于每个时间点,我都在计算圆在曲线上的位置并将其保存在一个数组中。

    请阅读我代码中的 cmets。我希望这是您所要求的。

    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>

    【讨论】:

    • 感谢 enxaneta!,我已经编辑了我的问题以增加清晰度。我要解决的问题是让每个圆圈以关键点中定义的速度沿着路径移动。每个圆圈都需要根据自己的一组值移动。如果你曾经看过 Gartner 炒作周期的一个版本(谷歌搜索,你可以看到一些图片示例),一些技术沿着曲线快速移动,而另一些则随着速度的变化懒惰地漂移。滑块底部代表 1995 年至 2018 年的时间,圆圈代表技术,希望对您有所帮助。
    • 太棒了!我想这正是我想要的。今天将使用它并添加一些其他对象以查看它如何与多个点一起使用。不会自己解决。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多