requestanimationframe() 中的函数调用次数过多,如何只调用一次?

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

我目前有一个脚本,我在其中跟踪元素的 .offset() 位置。当这个元素到达window的上、左、下、右边缘时,我需要调用一个函数,该函数应该只调用一次,然后在下一次碰到边缘时再次调用window.

现在我有这个:

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);

问题是,something() 函数被多次调用,我相信是由于 requestanimationframe() 的帧率?我需要使用 requestanimationframe,但是我只想在变量 exceededBounds 发生变化时调用 something() 函数。我想尝试在这里实现某种观察的东西,但感觉对于我实际需要的东西来说太复杂了。

谢谢

something() 如果元素曾经有 exceededBounds,将 被多次调用,除非你的 something 立即更改元素,以便它是在范围内 - 这就是递归调用 requestAnimationFrame 的工作原理,它只会被一遍又一遍地调用。

我建议更改 something 以便立即更改元素以使其再次在范围内 - 然后,something 只会 运行 一次(直到它消失又出界了)。

您需要保留两个布尔值:

  • exceeding 了解您的元素当前是否越界。
  • changed最后检查是否已经超标

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>

现在,我不知道是什么导致您的元素在 您的 场景中移动,但不要那样轮询。相反,监听可能导致此更改的事件。例如,在我的代码片段中,您会收听受限的 scroll 事件。

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>