简体   繁体   中英

Function within requestanimationframe() called too many times, how to call only once?

I currently have a script in which I am tracking the .offset() position of an element. When this element reaches the top, left, bottom, or right edge of the window, I need to call a function, which should only be called once, and would then be called again the next time it hits the edge of the window.

Right now I have this:

var exceededBounds = false

function checkPosition(element) {
 var pos = element.offset(); 
 var maxBoundsX = $(window).width();
 var maxBoundsY = $(window).height();
 var minBoundsX = 0
 var minBoundsY = 0

 // if the element hits one of the edges of the window
 if (pos.left >= maxBoundsX || pos.top >= maxBoundsY || pos.left <= minBoundsX || pos.top <= minBoundsY) {
  if (!exceededBounds) {
   exceededBounds = true
  }
  else {
   exceededBounds = false
  }
 }
 else {
  exceededBounds = exceededBounds;
 }
}

function something() {
 // do something here, which only happens once
}


function init() {
 checkPosition(element);
 if (exceededBounds) {
  something()
 }
  requestAnimationFrame(init);
}

requestAnimationFrame(init);

The problem is, the something() function is called multiple times, I believe due to the frame rate of requestanimationframe() ? I need to use requestanimationframe, however I only want to call the something() function essentially when the variable exceededBounds changes. I thought about trying to implement some kind of observe thing here, but it felt too complex for what I actually need.

Thanks

You need to keep two booleans:

  • exceeding to know if your element is currently out of bounds.
  • changed to know if at last check it was already exceeding or not.

 var element = $('#el'); var changed = false; var exceeding = false; function checkPosition(element) { var pos = element.offset(); var maxBoundsX = $(window).width(); var maxBoundsY = $(window).height(); var minBoundsX = 0 var minBoundsY = 0 // if the element hits one of the edges of the window if (pos.left >= maxBoundsX || pos.top >= maxBoundsY || pos.left <= minBoundsX || pos.top <= minBoundsY) { if (!exceeding) { changed = true } else { changed = false; } exceeding = true; } else { if (exceeding) { changed = true; } else { changed = false; } exceeding = false; } } function something() { console.log('changed:', exceeding ? 'hidden' : 'visible'); // do something here, which only happens once } function init() { checkPosition(element); if (changed) { something() } requestAnimationFrame(init); } requestAnimationFrame(init); 
 #el { margin-top: calc(100vh - 30px); margin-bottom: 100vh; } #cont { width: 100vw; height: 100vh; overflow: auto; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="cont"> <div id="el">Scroll me</div> </div> 

Now, I don't know what is causing your element to move in your scenario, but don't poll like that. Instead, listen to the event that may cause this change. EG, in my snippet, you would listen to a throttled scroll event.

 var element = $('#el'); var changed = false; var exceeding = false; cont.onscroll = throttle(init); function checkPosition(element) { var pos = element.offset(); var maxBoundsX = $(window).width(); var maxBoundsY = $(window).height(); var minBoundsX = 0 var minBoundsY = 0 // if the element hits one of the edges of the window if (pos.left >= maxBoundsX || pos.top >= maxBoundsY || pos.left <= minBoundsX || pos.top <= minBoundsY) { // our if else blocks can also be replaced by changed = !exceeding; exceeding = true; } else { changed = !!exceeding; exceeding = false; } } function something() { console.log('changed:', exceeding ? 'hidden' : 'visible'); // do something here, which only happens once } function init() { checkPosition(element); if (changed) { something() } } // wait for next screen refresh before triggering event's callback function throttle(callback) { if (typeof callback !== 'function') throw 'A callback function must be passed'; var active = false; var evt; function handler() { active = false; callback(evt); }; return function handleEvent(e) { evt = e; if (!active) { active = true; requestAnimationFrame(handler); } }; } 
 #el { margin-top: calc(100vh - 30px); margin-bottom: 100vh; } #cont { width: 100vw; height: 100vh; overflow: auto; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="cont"> <div id="el">Scroll me</div> </div> 

something() will be called multiple times if the element is ever has exceededBounds , unless your something immediately changes the element immediately so that it's within bounds - that's how recursively calling requestAnimationFrame works, it'll just keep getting called over and over again.

I'd suggest altering something such that the element is immediately altered so as to make it within bounds again - then, something will only run once (until it goes out of bounds again).

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