scrollTop的兼容性配置

简介: 【8月更文挑战第22天】scrollTop的兼容性配置

简介

在上篇文章中,我们通过vue的自定义指令实现了可滚动区域自动轮播的效果:

JS实现可滚动区域自动滚动展示 - 掘金
JS如何优雅的实现模块自动滚动展示

上面的方法很大程度解决了业务的需求,但也有不足的地方:

1.如果数据会定时刷新获取,数据变化导致滚动区域高度发生变化时,由于滚动条使用的是初始化时的高度设置,此时滚动条自动滚动功能会出现异常。(其他方式更改浏览器高度时也会导致此问题)

2.在浏览器缩放小于百分之百时,滚动功能也会出现异常。
GIF 2022-12-26 14-40-51.gif

自定义指令

我们先回顾一下,自动滚动的指令实现方式

const scroll = {
   
   
    inserted(el, binding) {
   
   
      const rate =    window.time.scroll ?? binding?.value ?? 100;
      if (!el) return;
      let parentDom = el;
      const scroll = () => {
   
   
        //判断元素是否滚动到底部(可视高度+距离顶部=整个高度)
        if (  parentDom.scrollTop + parentDom.clientHeight === parentDom.scrollHeight ) {
   
   
          parentDom.scrollTop = 0;
        } else {
   
   
          parentDom.scrollTop ++; // 元素自增距离顶部
        }
      };
      let timer= setInterval(scroll, rate);
      el.onmouseenter = () => {
   
   
        clearInterval(timer);
        timer = null;
      };
      el.onmouseleave = function () {
   
   
        timer = setInterval(scroll, rate);
      };
    },
  },

export default (Vue) => {
   
   
  Vue.directive("scroll", scroll)
};
// main.js
//需要注册的全局指令
import directive from "./directives";
Vue.use(directive);

dom变化时的兼容性处理

当用户增加或者隐藏浏览器的书签栏、全屏浏览器、手动拖动浏览器窗口时,都会导致滚动区域dom高度变化。
此外,滚动区域数据更新也会导致滚动区域dom高度发生变化。

能否使用指令的componentUpdated钩子?

我们知道自定义指令除了bind钩子,还有其他钩子函数。

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。

我们会在稍后讨论渲染函数时介绍更多 VNodes 的细节。

  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  • unbind:只调用一次,指令与元素解绑时调用。

通过官方解释,我们可以知道,如果浏览器dom高度发生变化时(数据更新重新渲染导致),componentUpdated钩子一定会执行。所以,我们将指令的钩子改为componentUpdated可以解决问题吗?
答案是不可以的,因为每次dom更新都会重新为当前dom元素添加定时器,最后,会导致滚动速度越来越快。
除非,每次添加定时器前,我们先判断存在定时器与否,如果存在,将其清除即可。

为了方便,我们直接将指令改为函数写在vue文件里,便于控制。

<template>
  <div class="main"  ref="rank"></div>
</template>
export default {
   
   
  data() {
   
   
    return {
   
   
      timer:null,
    };
  },
  methods:{
   
   
    addTimer(){
   
   
      this.$nextTick( ()=> {
   
   
        const el = this.$refs.rank
        if (!el) return;
        if(this.timer){
   
   
          clearInterval(this.timer);
          this.timer = null;
        }
        // 指令里面的代码copy过来
        // .....
      )
    },
  }
}

注意,这里我们使用了this.$nextTick,必须保证dom渲染后才执行滚动条相关逻辑

那么,如果在浏览器高度变化时,调用addTimer这个方法呢?
很简单,使用window的resize方法。

mounted(){
   window.addEventListener('resize',this.addTimer() )
},

那么,dom高度变化的问题我们用这个方法就可以完美解决了。

缩放时兼容性处理

为什么缩放时,会导致滚动条滚动异常呢?这和浏览器的window.devicePixelRatio接口有关

Window 接口的devicePixelRatio返回当前显示设备的物理像素分辨率与CSS像素分辨率之比。 此值也可以解释为像素大小的比率:一个CSS像素的大小与一个物理像素的大小。 简单来说,它告诉浏览器应使用多少屏幕实际像素来绘制单个CSS像素。

当我们不缩放屏幕时,打印这个值

console.log(window.devicePixelRatio);
// 1

当我们屏幕缩放到110%时,打印这个值

console.log(window.devicePixelRatio);
// 1.100000023841858

当我们屏幕缩放到90%时,打印这个值

console.log(window.devicePixelRatio);
// 0.8999999761581421

可见,在屏幕不缩放时,屏幕的物理像素与css的像素之比是一比一,进行缩放时,这个值会变,这就是导致滚动条失效的原因。
那么,修改代码也就非常容易了

methods:{
   
   
    addTimer(){
   
   
      this.$nextTick( ()=> {
   
   
        const el = this.$refs.rank
        if (!el) return;
        if(this.timer){
   
   
          clearInterval(this.timer);
          this.timer = null;
        }
        const rate =  window.time.scroll ?? 100;
        let parentDom = el;
        let scrollAll = parentDom.scrollHeight - parentDom.clientHeight
        const ratio = parseFloat((1 /  window.devicePixelRatio).toFixed(2) ) + 0.01 
        const scroll = () => {
   
   
          //判断元素是否滚动到底部(可视高度+距离顶部=整个高度)
          if (  parentDom.scrollTop > scrollAll - 2 ) {
   
   
            parentDom.scrollTop = 0;
          } else {
   
   
            parentDom.scrollTop += ratio; // 元素自增距离顶部
          }
        };
        this.timer = setInterval(scroll, rate);
        el.onmouseenter = () => {
   
   
          clearInterval(this.timer);
          this.timer = null;
        };
        el.onmouseleave = () => {
   
   
          this.timer = setInterval(scroll, rate);
        };
      }
      )
    },
  },
相关文章
window.scrollTop 不生效的原因,如何解决
window.scrollTop 不生效的原因,如何解决
515 0
|
1月前
|
Web App开发 前端开发 JavaScript
|
Web App开发 移动开发 前端开发
如何解决不同浏览器的样式兼容性问题?
如何解决不同浏览器的样式兼容性问题?
375 0
|
6月前
|
前端开发
element-ui组件DatePicker日期选择器移动端兼容
element-ui组件DatePicker日期选择器移动端兼容
element-ui组件DatePicker日期选择器移动端兼容
|
JavaScript
vue里使用animated-scroll-to代替原生滚动实现兼容ie的平滑滚动定位
vue里使用animated-scroll-to代替原生滚动实现兼容ie的平滑滚动定位
107 0
Element UI - 滚动条样式(仅支持 Webkit 内核)
Element UI - 滚动条样式(仅支持 Webkit 内核)
651 0