Affix 固钉

  • 需要考虑性能问题 刷新频率 实例个数
  • 需要考虑计算高度问题 getBoundingClientRect 获取元素边界 宽高等
  • 需要考虑固定时需要有占位元素替代原先位置 避免出现断层
  • 需要考虑监听事件列表 ‘resize’, ‘scroll’, ‘touchstart’, ‘touchmove’, ‘touchend’, ‘pageshow’, ‘load’,
  • 需要考虑销毁组件时清空事件监听
  • 需要考虑的元素自身属性 高度宽度变化等 ResizeObserver

节流防抖

底层刷新回调处理

  1. // 16 / 1000 60帧率
  2. // 在16ms之后 处理完当前任务 清空当前标记 开始接受下次任务处理
  3. // 防抖频率 在60帧
  4. let raf = (callback: FrameRequestCallback) => +setTimeout(callback, 16);
  5. let caf = (num: number) => clearTimeout(num);
  6. // 当你准备更新动画时你应该调用此方法。
  7. // 这将使浏览器在下一次重绘之前调用你传入给该方法的动画函数(即你的回调函数)。
  8. // 回调函数执行次数通常是每秒60次,但在大多数遵循W3C建议的浏览器中,
  9. // 回调函数执行次数通常与浏览器屏幕刷新次数相匹配。
  10. if (typeof window !== 'undefined' && 'requestAnimationFrame' in window) {
  11. raf = (callback: FrameRequestCallback) =>
  12. window.requestAnimationFrame(callback);
  13. caf = (handle: number) => window.cancelAnimationFrame(handle);
  14. }
  15. export function wrapperRaf(callback: () => void): number {
  16. return raf(callback);
  17. }
  18. wrapperRaf.cancel = caf;

真正控制任务频率的地方,通过控制更新requestId来控制操作频率

  1. export function throttleByAnimationFrame(fn: (...args: any[]) => void) {
  2. let requestId: number | null;
  3. // eslint-disable-next-line
  4. const later = (args: any[]) => () => {
  5. requestId = null;
  6. fn(...args);
  7. };
  8. // eslint-disable-next-line
  9. const throttled = (...args: any[]) => {
  10. if (requestId == null) {
  11. requestId = wrapperRaf(later(args));
  12. }
  13. };
  14. // eslint-disable-next-line
  15. (throttled as any).cancel = () => wrapperRaf.cancel(requestId!);
  16. return throttled;
  17. }

事件引发的刷新

在上述事件集合中 ‘resize’, ‘scroll’, ‘touchstart’, ‘touchmove’, ‘touchend’, ‘pageshow’, ‘load’,并不会立马执行更新位置,而是触发state发生变化,且打上需要重新计算的标记,触发组件render之后,执行componentDidUpdate进行位置的重新计算

两层ResizeObserver

原因猜测是 一个照顾原始dom变化跟踪,一个是fixed dom跟踪 要相互同步,比如dom的宽高变化

  1. <ResizeObserver
  2. onResize={() => {
  3. this.updatePosition();
  4. }}
  5. >
  6. <div {...props} ref={this.savePlaceholderNode}>
  7. {affixStyle && <div style={placeholderStyle} aria-hidden="true" />}
  8. <div className={className} ref={this.saveFixedNode} style={affixStyle}>
  9. <ResizeObserver
  10. onResize={() => {
  11. this.updatePosition();
  12. }}
  13. >
  14. {children}
  15. </ResizeObserver>
  16. </div>
  17. </div>
  18. </ResizeObserver>

Alert 警告提示