【性能优化】在大批量数据下使用 HTML+CSS实现走马灯,防止页面卡顿(二)

上一篇只是简单演示了'下一张'的操作和整体的设计思路,这两天把剩余功能补全了,代码经过精简,可封装当成轮播组件使用,详细如下.

代码

html 复制代码
<template>
    <div class="container">

        <button @click="checkNext('last')">上一张</button>
        <button @click="checkNext('next')">下一张</button>
        <div class="windows" @mousemove="stopPlay" @mouseleave="autoPlay">
            <div class="scrollBox">
                <div class="scrollItem">
                    <div class="img">
                        <el-image :src="initialData.imgUrl"></el-image>
                    </div>
                    <div class="messBox">{{ initialData.mess }}</div>
                </div>
            </div>
        </div>
    </div>
</template>
<script>
export default {
  data () {
    return {
      localData: [
        { imgUrl: '', mess: '11111' },
        { imgUrl: '', mess: '2222' },
        { imgUrl: '../assets/dz.png', mess: '3333' },
        { imgUrl: '../assets/logo.png', mess: '44444' },
        { imgUrl: '', mess: '55555' },
        { imgUrl: '', mess: '66666' }
      ],
      initialData: '', // 初始展示数据
      nowIndex: 0, // 当前展示数据的索引
      progressControler: false, // 控制切换的变量
      autoPlayTimer: '', // 自动播放定时器id
      nextShowData: { imgUrl: '', mess: '' }// 下一批展示的数据
    }
  },
  mounted () {
    this.initData()
  },
  beforeRouteLeave (to, from, next) {
    // 清除定时器
    clearInterval(this.autoPlayTimer)
    console.log('离开当前页面')
    next()
  },
  methods: {
    initData () {
      // 初始赋值
      this.initialData = this.localData[this.nowIndex]
      // 自动播放
      this.autoPlay()
    },

    // 查看上一张/下一张
    checkNext (type) {
      // 信息展示列表无数据或只有一条数据时,不执行
      if (!this.localData || this.localData.length <= 1) return
      // 防止猛点
      if (this.progressControler) {
        console.log('当前操作过快')
        return
      }
      this.progressControler = true // 将当前操作进程锁死,完成后才允许下一次操作
      this.readyBox(type)// 准备新的dom元素
      let removeDomIndex = ''
      let createDomIndex = ''
      if (type === 'last') {
        console.log('查看上一张')
        removeDomIndex = 1
        createDomIndex = 0
        this.nowIndex--
        if (this.nowIndex < 0) { this.nowIndex = this.localData.length - 1 }// 循环播放
      }
      if (type === 'next') {
        console.log('查看下一张')
        removeDomIndex = 0
        createDomIndex = 1
        this.nowIndex++
        if (this.localData.length < this.nowIndex + 1) { this.nowIndex = 0 }// 循环播放
      }
      // 获取dom,准备移动和删除节点
      const fatherDom = document.querySelector('.windows')
      const moveDistanceX = fatherDom.offsetWidth
      const domArr = fatherDom.querySelectorAll('.scrollBox')

      // 根据类名判断要移除的子盒子的偏移值
      const isRealDom = domArr[removeDomIndex].classList.contains('newScrollBox')
      const isRightCreatDom = domArr[removeDomIndex].classList.contains('RightnewScrollBox')
      const isLeftCreatDom = domArr[removeDomIndex].classList.contains('LeftnewScrollBox')

      // 实现走马灯移动效果
      if (!isRealDom) {
        domArr[removeDomIndex].style.transform = `translate(${type === 'next' ? -moveDistanceX : moveDistanceX}px,0px)`
      }

      if ((type === 'next' && isRightCreatDom) || (type === 'last' && isLeftCreatDom)) {
        domArr[removeDomIndex].style.transform = `translate(${type === 'next' ? -moveDistanceX * 2 : moveDistanceX * 2}px,0px)`
      }
      if ((type === 'next' && isLeftCreatDom) || (type === 'last' && isRightCreatDom)) {
        domArr[removeDomIndex].style.transform = 'translate(0px,0px)'
      }
      domArr[createDomIndex].style.transform = `translateX(${type === 'next' ? -moveDistanceX : moveDistanceX}px)`

      const timeId1 = setTimeout(() => {
        fatherDom.removeChild(domArr[removeDomIndex])
        this.progressControler = false // 允许进行下一次操作
        clearTimeout(timeId1)
      }, 501)
    },

    // 为下一次移动准备dom元素
    readyBox (type) {
      let nextShowData = ''// 上一张或下一张要展示的数据
      const fatherDom = document.querySelector('.windows')// 获取父元素
      const newDom = document.createElement('div')// 创建新元素
      // 设置新元素的样式
      newDom.className = 'scrollBox'
      newDom.classList.add('newScrollBox')
      newDom.style.width = '100%'
      newDom.style.height = '100%'
      newDom.style.position = 'absolute'
      newDom.style.transition = 'all 0.5s'

      // 上一张
      if (type === 'last') {
        // 确定下一条播放数据的取值
        if (this.nowIndex === 0) {
          nextShowData = this.localData[this.localData.length - 1]
        } else {
          nextShowData = this.localData[this.nowIndex - 1]
        }
        newDom.style.left = '-100%'
        newDom.classList.add('LeftnewScrollBox')
        // 插入子元素
        newDom.innerHTML = manageHtml(nextShowData)
        fatherDom.insertBefore(newDom, fatherDom.firstChild)
      }
      // 下一张的数据
      if (type === 'next') {
        // 确定下一条播放数据的取值
        if (this.localData.length === this.nowIndex + 1) {
          nextShowData = this.localData[0]
        } else {
          nextShowData = this.localData[this.nowIndex + 1]
        }

        newDom.style.left = '100%'
        newDom.classList.add('RightnewScrollBox')

        // 插入子元素
        newDom.innerHTML = manageHtml(nextShowData)
        fatherDom.appendChild(newDom)
      }

      // 管理动态页面结构
      function manageHtml (nextShowData) {
        // 新元素的内部结构
        const innerHtml = `
                <div class="scrollItem" style=" display: flex;  width: 100%; height: 100%; background-color: pink;">
                    <div class="img" style="width:50%; height:100%" >
                        <el-image src="${nextShowData.imgUrl}"></el-image>
                    </div>
                    <div class="messBox" style=" font-size: 16px; width:50%; height:100%; background-color: skyblue; ">
                        ${nextShowData.mess}
                    </div>
                </div>
        `
        return innerHtml
      }
    },
    // 自动播放
    autoPlay () {
      this.autoPlayTimer = setInterval(() => {
        this.checkNext('next')
      }, 3000)
    },

    // 暂停播放
    stopPlay () {
      if (this.progressControler) {
        console.log('鼠标移入时进程被打断')
        this.progressControler = !this.progressControler
      }
      clearInterval(this.autoPlayTimer)
    }
  }
}
</script>
<style lang='scss' scoped>
.container {
    width: 100%;
    height: 100%;
}

.container .windows {
    position: relative;
    left: 30%;
    top: 30%;
    font-size: 0px;
    overflow: hidden;
    width: 40%;
    height: 40%;
}

.scrollBox {
    position: absolute;
    width: 100%;
    height: 100%;
    transition: all 0.5s;
}

.windows .scrollItem {
    display: flex;
    width: 100%;
    height: 100%;
    background-color: pink;
}

.windows .scrollItem .img {
    width: 50%;
    height: 100%;
}

.windows .messBox {
    font-size: 16px;
    width: 50%;
    height: 100%;
    background-color: skyblue;
}
</style>
相关推荐
花生侠21 分钟前
记录:前端项目使用pnpm+husky(v9)+commitlint,提交代码格式化校验
前端
一涯28 分钟前
Cursor操作面板改为垂直
前端
我要让全世界知道我很低调35 分钟前
记一次 Vite 下的白屏优化
前端·css
1undefined237 分钟前
element中的Table改造成虚拟列表,并封装成hooks
前端·javascript·vue.js
蓝倾1 小时前
淘宝批量获取商品SKU实战案例
前端·后端·api
comelong1 小时前
Docker容器启动postgres端口映射失败问题
前端
花海如潮淹1 小时前
硬件产品研发管理工具实战指南
前端·python
用户3802258598241 小时前
vue3源码解析:依赖收集
前端·vue.js
WaiterL1 小时前
一文读懂 MCP 与 Agent
前端·人工智能·cursor