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

最近使用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)

相关推荐
也无晴也无风雨27 分钟前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
Martin -Tang1 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js
FakeOccupational3 小时前
nodejs 020: React语法规则 props和state
前端·javascript·react.js
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
曹天骄4 小时前
next中服务端组件共享接口数据
前端·javascript·react.js
阮少年、4 小时前
java后台生成模拟聊天截图并返回给前端
java·开发语言·前端
郝晨妤5 小时前
鸿蒙ArkTS和TS有什么区别?
前端·javascript·typescript·鸿蒙
AvatarGiser6 小时前
《ElementPlus 与 ElementUI 差异集合》Icon 图标 More 差异说明
前端·vue.js·elementui
喝旺仔la6 小时前
vue的样式知识点
前端·javascript·vue.js
别忘了微笑_cuicui6 小时前
elementUI中2个日期组件实现开始时间、结束时间(禁用日期面板、控制开始时间不能超过结束时间的时分秒)实现方案
前端·javascript·elementui