制作一个无限无缝轮播滚动的通知栏

最近使用swiper这个库,突然想做个无限轮播滚动的通知栏。效果如下图:

思路

这东西其实是一个比较特殊的轮播图效果,所以主要参照轮播图的开发流程来做就差不多了。

  1. 需要一个显示的内容的盒子包裹内容,高度按照内容的整体高度(cilentHeight )来决定,我比较懒,直接写死(差不多放下4个内容 ),四个内容为一版显示,后续都是根据4个为一组进行轮播显示
  2. 因为要进行滚动显示,只需要给内容外层容器添加translateY配合transition来达到滚动和缩放的动画效果。
  3. 后续需要计算translateY的每一次滚动的距离,所以内容之间最好不要有间隔,比如margin之类的间隔,因为这样子更加简单,后面计算的时候直接滚动对应的高度就好了。
  4. 要实现无限无缝滚动,就是要参照轮播图的无缝滚动了,原来其实就是复制第一份视图容器所能容下的内容数据,放到数据最后方,当滚动到最后的复制数据时,停止对应的动画,然后直接初始化数据我这里做得不够好,希望大伙帮我优化一下,哈哈哈。

实现

结构

HTML 复制代码
<!-- container就是视图容器,这里写死一个高度 -->
<div class="container">
<!-- 内容外层容器  -->
    <ul :ref="(el) => ulRef = el" class="user-list flex flex-col flex-aic" :style="{
        transform: `translateY(${translateY}px)`
      }">
      <!-- 内容 -->
      <li class="flex flex-aic flex-jcc" :ref="(el) => itemRef = el" v-for="(item,index) in list">
        <div :id="`User${index}`" class="flex flex-aic gap-10 w-100p h-100p" :style="userStyle(index)">
          <span>{{ item.name }}</span>
          <span>has placed an enquiry on BetterYou products of <span style="color: #1A237C">{{ item.money }}</span>
          pieces</span>
        </div>
      </li>
    </ul>
  </div>

样式:注释记得删掉

css 复制代码
 // 写死高度
  .container {
    margin: 100px auto;
    height: 380px; // 这里是可以通过计算获得的,比较懒
    overflow: hidden;
  }

  li::marker {
    content: ""
  }

  .user-list li {
    font-size: 18px;
    font-family: Microsoft YaHei-Bold, Microsoft YaHei;
    font-weight: 700;
    color: #1A237C;
    padding-bottom: 30px; // 这里是为了给内容之间看起来存在间隔


  }

  li div {
    padding: 18px 40px;
    width: fit-content;
    background: rgba(182, 182, 182, 0.1);
    box-shadow: 0px 10px 10px 0px rgba(0, 0, 0, 0.05);
    border-radius: 10px 10px 10px 10px;
    text-align: center;
  }

  li span:last-child {
    color: #000000;
  }

逻辑

js 复制代码
 import { defineComponent, ref,defineProps,computed,onMounted,onUnmounted } from 'vue';
// 原来使用TS写的,但是掘金居然不支持VUE3的TS版本,只能随便搬过来了
export default defineComponent({
  setup() {
    
const data = ref([
    {
      name: "遥遥领先",
      money: 99999
    },
    {
      name: "大米",
      money: 7777
    },
    {
      name: "大米",
      money: 7777
    },
    {
      name: "大米",
      money: 7777
    },
    {
      name: "大米",
      money: 7777
    },
    {
      name: "大米",
      money: 7777
    },
    {
      name: "大米",
      money: 7777
    },
    {
      name: "大米",
      money: 7777
    }
  ])

const ulRef = ref(null) // 内容外层容器
const itemRef = ref(null) // 内容容器
const timer = ref(null)
const translateY = ref(0) // 内容外层容器滚动Y轴距离
const activeIndex = ref(0) // 当前最大的活动内容(就是视图容器中显示的第一个内容)
const delayed = ref(1) // 动画持续时间

const list = computed(
  () => data.value?.length
    ? [
      ...data.value,
      ...data.value.filter((item, index) => index < 4) // 将最前面的四个内容复制一份放到最后
    ]
    : []
)
const disY = computed(() => itemRef.value?.clientHeight ?? 90)
const userStyle = computed(() => (index) => {
  let style = {}
  switch (index) {
    case activeIndex.value:
      style['transform'] = `scale(1)`
      style['transition'] = `all ${delayed.value}s`
      break
    case activeIndex.value + 1:
      style['transform'] = `scale(.9)`
      style['transition'] = `all ${delayed.value}s`
      break
    case activeIndex.value + 2:
      style['transform'] = `scale(.8)`
      style['transition'] = `all ${delayed.value}s`
      break
    case  activeIndex.value + 3:
      style['transform'] = `scale(.7)`
      style['transition'] = `all ${delayed.value}s`
      break
    case  activeIndex.value + 4:
    // 视图之外第五个内容的样式,这样子动画看起来就不会很突兀
      style['transform'] = `scale(.6)`
      style['transition'] = `all ${delayed.value}s`
      break
    default:
      // 视图容器之外(除了第五个内容)内容样式
      style['transform'] = `scale(1)`
      style['transition'] = 'none'
      break
  }
  if (activeIndex.value === 0) style['transition'] = 'none' // 循环了一轮后,显示了复制前面的四个内容之后
  return style
})

onMounted(() => {
  start()
})
onUnmounted(() => {
  clearInterval(timer.value)
})

function start() {
  timer.value = setInterval(() => {
    if (activeIndex.value === list.value.length - 4) {
      // 循环了一轮之后初始化
      ulRef.value.style.transition = 'none'
      translateY.value = 0
      activeIndex.value = 0
    } else {
      // 循环轮播
      ulRef.value.style.transition = `all ${delayed.value}s`
      translateY.value -= disY.value
      activeIndex.value += 1
    }

  }, delayed.value * 1000)
}

    return {
      list,
      translateY,
      ulRef,
      itemRef,
      timer,
      translateY,
      activeIndex,
      delayed,
      userStyle
    };
  },
});

效果展示

cdvtSAbR - 码上掘金 (juejin.cn)

相关推荐
zqx_722 分钟前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己39 分钟前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称1 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色1 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_2342 小时前
npm、yarn、pnpm之间的区别
前端·npm·node.js
秋殇与星河2 小时前
CSS总结
前端·css
BigYe程普2 小时前
我开发了一个出海全栈SaaS工具,还写了一套全栈开发教程
开发语言·前端·chrome·chatgpt·reactjs·个人开发
余生H2 小时前
前端的全栈混合之路Meteor篇:关于前后端分离及与各框架的对比
前端·javascript·node.js·全栈
花花鱼2 小时前
@antv/x6 导出图片下载,或者导出图片为base64由后端去处理。
vue.js
程序员-珍2 小时前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发