简体   繁体   中英

Equivalent of CSS animation direction and infinite iteration count in rAF

I am new to requestAnimationFrame and trying to understand how can I develop something that is done with CSS Keyframe Animation .

To elaborate, I can do the following with CSS

 let curve = document.querySelector('#curve') let dot = document.querySelector('#dot'); let d = curve.getAttribute('d'); dot.style.setProperty('--mp', '"' + d + '"')
 #dot { offset-path: path(var(--mp)); animation: move 3000ms infinite alternate ease-in-out; } @keyframes move { 100% { offset-distance: 100%; } }
 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720"> <g> <circle id="dot" r="10" cy="0.5" cx="0.5" style="fill: #dd1819; "></circle> </g> <path id="curve" d="M 29.928125,119.32884 C 59.868756,78.85387 99.789596,30.283895 138.66049,30.104515 c 80.46172,-0.37133 90.87185,137.794285 140.7729,129.699295 40.35037,-6.54568 49.90105,-80.94994 86.53937,-78.86548 33.24073,1.89117 22.2547,70.77048 73.14399,70.77048 39.92084,0 58.65264,-56.66496 99.80211,-56.66496 49.90105,0 69.86147,80.94995 99.8021,48.56997" style="fill: none; stroke:black; stroke-width: 1;" /> </svg>

animation-direction: alternate; makes sure it plays back and forth and animation-iteration-count: infinite; ensures it runs in an infinite loop.

I am trying to understand if the same can be achieved in rAF . This is what I tried so far

 let u = 0; let time = 3000; let start = performance.now(); let curve = document.querySelector('#curve') let totalLength = curve.getTotalLength(); let dot = document.querySelector('#dot'); const temp = []; const move1 = () => { let p = curve.getPointAtLength(u * totalLength); dot.setAttribute("transform", `translate(${px}, ${py})`); if (u < 1) { u = (performance.now() - start) / time temp.push(p); requestAnimationFrame(move1) } }; move1();
 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720"> <g> <circle id="dot" r="10" cy="0.5" cx="0.5" style="fill: #dd1819; "></circle> </g> <path id="curve" d="M 29.928125,119.32884 C 59.868756,78.85387 99.789596,30.283895 138.66049,30.104515 c 80.46172,-0.37133 90.87185,137.794285 140.7729,129.699295 40.35037,-6.54568 49.90105,-80.94994 86.53937,-78.86548 33.24073,1.89117 22.2547,70.77048 73.14399,70.77048 39.92084,0 58.65264,-56.66496 99.80211,-56.66496 49.90105,0 69.86147,80.94995 99.8021,48.56997" style="fill: none; stroke:black; stroke-width: 1;" /> </svg>

The above rAF animation is probably equivalent to

#dot {
    offset-path: path(var(--mp));
    animation: move 3000ms 1 forwards linear;
}

Is there any way to achieve what can be done in CSS

A couple of things about working with requestAnimationFrame(…) :

  1. It's best practice to use the timestamp argument given to the callback by requestAnimationFrame . There is no need to call performance.now() inside the function. But if you do, make sure to do it before the animation, otherwise the animation will lack behind any potential frame rate drops on slower PCs and especially phones.

    The callback method is passed a single argument, a DOMHighResTimeStamp similar to the one returned by performance.now() , which indicates the current time (based on the number of milliseconds since time origin).

  2. Take the given timestamp and get from it what you need. (This is usually done using outside variables acting as a configuration.) What iteration of the animation am I in? How far progressed has this iteration?

  3. This approach is valid but not the best: Best practice would be to just save the last time, last requestID (returned by requestAnimationFrame(…) ) and last state ( iterationWithFraction ) of the animation outside. Then compare the last time with the time now in every step and add the normalized progress to the state. Then update the animation accordingly.
    Always comparing back to the very first timestamp start can lead to very confusing errors. (Just imagine time is set to 2000 mid animation.)

  4. If you want to end an animation, check out cancelAnimationFrame(requestID) .

 let time = 3000; let start = performance.now(); let curve = document.querySelector('#curve') let totalLength = curve.getTotalLength(); let dot = document.querySelector('#dot'); let d = curve.getAttribute('d'); const step = (timestamp) => { // get current time normalized with "time" (2s passed = 0.6666, 3s passed = 1, 4s passed = 1.33333, ...) const iterationWithFraction = (timestamp - start) / time; // get current iteration as an integer const iteration = Math.floor(iterationWithFraction); // get current moment in iteration let fraction = iterationWithFraction % 1; // if iteration odd: invert fraction to get the desired alternation if (iteration % 2 == 1) fraction = 1 - fraction; // if you want an easing effect, you can do something like this now fraction = fraction<.5 ? 2*fraction**2 : -1+(4-2*fraction)*fraction; // do your animation const point = curve.getPointAtLength(fraction * totalLength); dot.setAttribute("transform", `translate(${point.x}, ${point.y})`); // get next AF as the last thing of the function requestAnimationFrame(step); }; step(performance.now());
 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720"> <g> <circle id="dot" r="10" cy="0.5" cx="0.5" style="fill: #dd1819; "></circle> </g> <path id="curve" d="M 29.928125,119.32884 C 59.868756,78.85387 99.789596,30.283895 138.66049,30.104515 c 80.46172,-0.37133 90.87185,137.794285 140.7729,129.699295 40.35037,-6.54568 49.90105,-80.94994 86.53937,-78.86548 33.24073,1.89117 22.2547,70.77048 73.14399,70.77048 39.92084,0 58.65264,-56.66496 99.80211,-56.66496 49.90105,0 69.86147,80.94995 99.8021,48.56997" style="fill: none; stroke:black; stroke-width: 1;" /> </svg>

No need for CSS or JavaScript (and I do love JavaScript & Web Components)

To add easing, see:

 <svg viewBox="0 0 640 720" xmlns="http://www.w3.org/2000/svg"> <path id="PATH" stroke="black" fill="none" d="m30 119c30-40 70-89 109-89 81 0 91 138 141 130 40-7 50-81 87-79 33 2 22 71 73 71 40 0 59-57 100-57 50 0 70 81 100 49" /> <circle r="10" fill="red"> <animateMotion dur="5s" keyPoints="0;1;0" keyTimes="0;.7;1" calcMode="linear" repeatCount="indefinite"> <mpath href="#PATH" /> </animateMotion> </circle> </svg>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM