WangScroll说明文档

基础知识

offsetWidth/offsetHeight

HTMLElement.offsetWidth 是一个只读属性,返回一个元素的布局宽度。offsetWidth 是测量包含元素的边框 (c)、水平线上的内边距 (padding)、竖直方向滚动条 (scrollbar)(如果存在的话)、以及 CSS 设置的宽度 (width) 的值。即:

offsetWidth = offsetWidth * 2 + padding * 2 + width + scrollbar

ResizeObserver

ResizeObserver 是一个 JavaScript API,用于监视元素的大小变化。它可以观察一个或多个 DOM 元素,以便在元素的大小或形状发生变化时触发回调函数。ResizeObserver 是为了更有效地处理元素尺寸变化而引入的,特别适用于响应式设计和自适应布局。

Vue3 复制代码
const myObserver = new ResizeObserver((entries) => {
  entries.forEach((entry) => {
    console.log('被监听元素content的宽高及位置', entry.contentRect)
    // bottom: 700 指top + height的值
    // height: 600 指元素本身的高度,不包含padding,border值
    // left: 100 指padding-left的值
    // right: 1143 指left + width的值
    // top: 100 指padidng-top的值
    // width: 1043  指元素本身的宽度,不包含padding,border值
    // x: 100
    // y: 100
    console.log('被监听元素的宽高', entry.borderBoxSize)
    // blockSize: 1000
    // inlineSize: 1443
    console.log('被监听元素content部分的宽高', entry.contentBoxSize)
    // blockSize: 600
    // inlineSize: 1043
    console.log('被监听元素', entry.target)
  })
})
myObserver.observe(contentRef.value)

在使用ResizeObserver API的时候,在每次触发元素的大小变化时,会在1s内触发回调多次。如果想进一步优化性能,可以加防抖函数处理。

Vue3 复制代码
const observer = new ResizeObserver(useDebounceFn(handleSetPosition, 300));
observer.observe(contentRef.value);

节流与防抖的区别

函数防抖:一段时间内连续触发事件,只执行最后一次

函数节流:一段时间内只执行一次

鼠标事件mouseover和mouseenter的区别

mouseover和mouseenter都是鼠标移到元素身上就触发,区别是:

1.mouseover经过自身盒子触发,经过子盒子也触发,用于冒泡特性

2.mouseenter只经过自身盒子触发,没有冒泡特性

1.事件的触发时机

mouseover:当鼠标移入元素或子元素都会 触发事件。

mouseenter:当鼠标移入元素才会触发事件。

2.是否支持冒泡

mouseover:支持冒泡

mouseenter:不支持冒泡

WangScroll实现原理

主要通过transform translateY以及requestAnimationFrame来实现竖向滚动效果。

  1. transform translateY不会触发回流,有助于优化性能。原因是:渲染流水线是这样的顺序:重排 -> 重绘 -> 合成,transform: translate是直接合成,跳过了前面的重排重绘。
  2. requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率。能保证回调函数在屏幕每一次的绘制间隔中只被执行一次,这样就不会引起丢帧现象,也不会导致动画出现卡顿的问题。

WangScroll代码说明

  1. WangScroll接收两个props, loading以及dataSource。
Vue3 复制代码
const props = defineProps({
  loading: {
    type: Boolean,
    required: true
  },
  dataSource: {
    type: Array as PropType<any[]>,
    required: true
  }
});

dataSource为传入的数据源,当传入的dataSource发生变化时,会触发addNewList方法。addNewList将新拿到的dataSource赋值给propList。

Vue3 复制代码
/**
 * @description 将新拿到的数据赋值给propList
 */
const addNewList = () => {
  propList.value = props.dataSource.slice(0);
};

// 监听数据源传入
watch(
  () => props.dataSource.length,
  () => {
    addNewList();
  }
);

当数据(dataSource)正在获取过程中时,loading为true,关闭滚动。 当数据(dataSource)获取完成后,loading为false,开启滚动。

Vue3 复制代码
/**
 * @description 播放滚动
 */
const move = () => {
  if (state.isHover) return;
  // 数据加载中就停止
  if (props.loading) {
    _cancel();
    return;
  }
  tanslateY.value += 1;
  if (tanslateY.value === propList.value.length * state.itemHeight) {
    tanslateY.value = 0;
    emit("scrollEnd");
  }
  state.rafTimer = requestAnimationFrame(move);
};
/**
 * @description 关闭滚动
 */
const _cancel = () => {
  state.rafTimer !== null && window.cancelAnimationFrame(state.rafTimer);
};

watch(
  () => props.loading,
  value => {
    // 数据加载完之后开启滚动
    if (!value) {
      move();
    }
  }
);
  1. 滚动区域的滚动通过transform translateY来实现,且会进行鼠标移入移出监听,当鼠标悬停在滚动区域时,停止滚动,并于用户查看滚动信息。
Vue3 复制代码
<div 
    class="fs-estimated-virtuallist-content" 
    :style="scrollStyle" @mouseleave="leave" 
    @mouseenter="enter" 
    @mouseover="over">
  ...
</div>

const tanslateY = ref<number>(0); // 通过tanslateY实现滚动效果
const scrollStyle = computed(
  () =>
    ({
      transform: `translate3d(0, -${tanslateY.value}px, 0)`
    }) as CSSProperties
);

/**
 * @description 关闭滚动
 */
const _cancel = () => {
  state.rafTimer !== null && window.cancelAnimationFrame(state.rafTimer);
};
// 鼠标移入关闭滚动
const enter = () => {
  state.isHover = true; //关闭_move
  _cancel();
};
// 鼠标移出开启滚动
const leave = () => {
  state.isHover = false; //开启_move
  move();
};

const over = () => {
  _cancel();
};
  1. 滚动区域的中包含两块数据列表,用于实现滚动的无缝衔接。随着滚动区域的滚动,当上面列表的底部到达可视区的顶部,下面列表的顶部也正好在可视区的顶部。此时,将滚动区域的transform translateY设置为0,上面列表的顶部又回到可视区的顶部,这个变换过程是无感知的,因为transform translateY不会引起回流,并且两个列表内容一样,人眼感知不到变化。相关代码在move方法中:
js 复制代码
// 当上面列表的底部到达可视区的顶部 将滚动区域的transform translateY设置为0 上面列表的顶部又回到可视区的顶部
if (tanslateY.value === propList.value.length * state.itemHeight) {
    tanslateY.value = 0;
    emit("scrollEnd");
}

当上面列表的底部到达可视区的顶部,即数据列表滚动完成后,会抛出scrollEnd方法,在使用WangScroll的Vue文件中可监听scrollEnd方法,进行相关处理,比如调用接口刷新数据源。

  1. WangScroll使用的前提是滚动的每一行都是定高的,且会在onMounted生命周期中计算出相应高度。这个高度主要用于计算出滚动列表的总高,进而用于判断上面列表的底部是否到达可视区的顶部,即if (tanslateY.value === propList.value.length * state.itemHeight)
Vue3 复制代码
onMounted(() => {
  setTimeout(() => {
    if (itemRef.value) state.itemHeight = itemRef.value[0].offsetHeight;
  });
});
相关推荐
痴憨道人几秒前
openharmony sdk描述
javascript
loey_ln4 分钟前
FIber + webWorker
javascript·react.js
NightCyberpunk23 分钟前
HTML、CSS
前端·css·html
xcLeigh33 分钟前
HTML5超酷响应式视频背景动画特效(六种风格,附源码)
前端·音视频·html5
zhenryx35 分钟前
前端-react(class组件和Hooks)
前端·react.js·前端框架
lv程序媛36 分钟前
el-table表头前几列固定,后面几列根据接口返回的值不同展示不同
javascript·vue.js·elementui
ZwaterZ37 分钟前
el-table-column自动生成序号&&在序号前插入图标
前端·javascript·c#·vue
蒟蒻的贤1 小时前
vue学习11.21
javascript·vue.js·学习
初遇你时动了情2 小时前
uniapp 城市选择插件
开发语言·javascript·uni-app
zhangjr05752 小时前
【HarmonyOS Next】鸿蒙实用装饰器一览(一)
前端·harmonyos·arkts