Home > OS >  How to change an element's animate keyframe while animation is running?
How to change an element's animate keyframe while animation is running?

Time:12-02

I have a little mouse speed detector (which is far from perfect) which gives me the current mouse speed every 100ms in the variable window.mouseSpeed.t. I only implemented it because I want to have a funny animation on the bottom edge of the screen with a bar that grows with higher speeds and shrinks with lower speeds. I want it to be animated with Element.animate().
The only problem is: How can I change the Animation's end keyframe (I only give an end frame so the browser assumes the current status as the first frame) while the animation is running?
I want to achieve that the bar smoothly changes its length.

// The code I want to have animated is below this function.

// Mouse speed tracker (I hope this isn't too horrible code):
document.addEventListener('DOMContentLoaded', mausgeschwindigkeitVerfolgen, {once:true});
function mausgeschwindigkeitVerfolgen() { // "Mausgeschwindigkeit verfolgen" means "track mouse speed" in German
    var speedX = NaN;
    var speedY = NaN;
    var posX = NaN;
    var posY = NaN;
    var speed = NaN;

    document.addEventListener("mousemove", function(ev){
        speedX  = Math.abs(ev.movementX);
        speedY  = Math.abs(ev.movementY);
        speed = 10*Math.sqrt(ev.movementX**2 ev.movementY**2);
        window.mousePosition = {x:posX = ev.clientX,y:posY = ev.clientY};
    }, false);

    setInterval(function(){
        [window.mouseSpeed, window.mousePosition] = [{x:speedX,y:speedY,t:speed}, {x:posX,y:posY}]; // Werte in window.mouseSpeed und window.mouseDistance speichern
        speed = totalX = totalY = 0;
    }, 100);
    window.mausgeschwindigkeitVerfolgen = () => {return {speed:window.mouseSpeed, pos:window.mousePosition};};
    return {speed:window.mouseSpeed, pos:window.mousePosition};
}

// --- This is the code I want to have animated: ---
setInterval(() => {
  document.querySelector('div#mouseSpeedIndicator').style.width = window.mouseSpeed.t 'px';
  //document.querySelector('div#mouseSpeedIndicator').animate({width:'0px'}, {duration:1000,iterations:1}); // This just keeps the bar at width 0, I want it to slowly change to any newly set width
}, 100);
div#mouseSpeedIndicator {
  position: fixed;
  bottom: 0px;
  left: 0px;
  height: 33px;
  background-color: green;
  max-width: 100vh;
  border: 0px solid green;
  border-top-right-radius: 10px;
}
<!-- What I currently have -->
<div id="mouseSpeedIndicator"></div>

CodePudding user response:

First, something as simple as one additional line of the transition CSS property like e.g. ...

transition: width 1s ease-out;

... already does the job; no need for more JavaScript based computation and DOM manipulation.

But of cause the OP's script could be dramatically simplified with or even without the support of an external helper method like throttle (lodash _.throttle or underscorejs _.throttle) where the latter would create a delayed executed version of the passed function which for the OP's example-script is the 'mousemove'-handler.

This handler before being throttled (or even not throttled) could be created as a bound version of the function which actually computes the speed value and updates the indicator-node's appearance.

function handleMouseSpeedIndicatorUpdateFromBoundData(evt) {
  const { movementX, movementY } = evt;
  const { rootNode, timeoutId } = this;

  // prevent indicator nullification at time.
  clearTimeout(timeoutId);

  // compute `speed`.
  const speed = 10 * Math.sqrt(movementX**2   movementY**2);

  // update indicator appearance.
  rootNode.style.width = `${ speed }px`;

  // trigger delayed indicator nullification.
  this.timeoutId = setTimeout(() => rootNode.style.width = 0, 110);
}

function initialzeMouseSpeedIndicator() {
  document
    .addEventListener(
      'mousemove',

      // create throttled version of the just created bound handler.
      _.throttle(

        // create handler function with bound contextual data.
        handleMouseSpeedIndicatorUpdateFromBoundData.bind({

          rootNode: document.querySelector('#mouseSpeedIndicator'),
          timeoutId: null,

        }), 100
      ),
      false
    );
}

// - no need for `'DOMContentLoaded'`
//   in order to initialize the indicator. 
initialzeMouseSpeedIndicator();
div#mouseSpeedIndicator {
  position: fixed;
  top: 0px;
  left: 0px;
  height: 33px;
  background-color: green;
  max-width: 100vh;
  border: 0px solid green;
  border-bottom-right-radius: 10px;

  /* proposed change(s) */

  transition: width 1s ease-out;
  /* transition: width .5s ease-in; */
  /* transition: width .5s ease-in-out; */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>

<div id="mouseSpeedIndicator"></div>

  • Related