【问题标题】:Function within requestanimationframe() called too many times, how to call only once?requestanimationframe() 中的函数调用了太多次,如何只调用一次?
【发布时间】:2018-03-26 09:27:23
【问题描述】:

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

现在我有这个:

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,但是我只想在变量超出范围发生变化时调用 something() 函数。我想过在这里尝试实现某种观察的东西,但是对于我真正需要的东西来说感觉太复杂了。

谢谢

【问题讨论】:

  • 你试过把exceededBounds = false;放在if (exceededBounds)里面吗?这样,您将继续使用 checkPosition(element); 检查元素的位置,但 something() 只会在 exceededBoundscheckPosition(element) 内再次设置为 true 时运行
  • 方法错误。不要使用requestAnimationFrame()。请改用MutationObserver`。

标签: javascript jquery html observable requestanimationframe


【解决方案1】:

你需要保留两个布尔值:

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

现在,我不知道是什么导致您的元素在 your 场景中移动,但不要这样轮询。相反,请听可能导致此更改的事件。例如,在我的 sn-p 中,你会听到一个受限制的 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>

【讨论】:

  • 好的,谢谢。我明白这一点。现在唯一的问题是 something() 函数被调用了两次......我只想调用 something() 一次(我将使用它来更改元素的样式),直到元素再次超出边界(当风格将再次改变)
  • @biscuit_ 我真的不明白你在说什么。如果你只想在something越界时触发它,那么在init调用if( changed &amp;&amp; exceeding ) { something(); }
【解决方案2】:

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

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

【讨论】:

  • 嗯,我不能立即更改元素,将其推回边界内,因为我需要动画自然(我实际上也在使用 CSS 动画来移动元素,所以我不能使用控制它的原始值)。
猜你喜欢
  • 2017-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多