V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zhangxiaoshang66
V2EX  ›  前端开发

一个基于 vue 的图片轮播组件的实现

  •  
  •   zhangxiaoshang66 · 2018-05-02 09:02:48 +08:00 · 2592 次点击
    这是一个创建于 2397 天前的主题,其中的信息可能已经有所发展或是发生改变。

    1. 在线 DEMO

    http://va-carousel.xiaoshang.online

    Github

    2. 首先是一张思维导图

    导图

    3. 然后是以上属性的标注说明

    标注

    4. 代码层

    4.1 除去可从父组件接收的属性,组件自身有以下属性:

    data() {
        return {
          list: [],     // 当前显示的图片列表
          hover: false, // 鼠标是否悬浮在组件上
          timer: null,  // 自动切换的 setInterval
          itemWidth: '',// 每项元素的宽度
          isReverse: false // 是否是反向切换,触发 prev 时,该属性为 true
        }
      }
    

    4.2 组件挂载之前

    1. 计算每项元素的宽度,即 itemWidth 的值
    2. 初始化显示的图片列表,即 list。这里存储的数据才是真正会被在页面上渲染的。每次切换,实际上就是修改该 list 中的数据,对应的视图会自动更新,数据驱动视图嘛。
    beforeMount() {
        this.itemWidth = 100 / this.total + '%'
        this.list = this.items.slice(0, this.total)
      },
    
    

    4.3 组件挂在后,检查 autoplay 属性,若该属性为 true,则产生一个计时器

    mounted() {
        if (this.autoplay) {
          this.startTimer()
        }
      },
    

    4.4 startTimer 函数很简单,就是间隔一定时间触发一次 next(向后)切换

    // 开始计时器
    startTimer() {
        if (!this.interval || this.interval <= 0) {
            return
        }
        this.timer = setInterval(this.next, this.interval)
    }
    

    4.5 next 函数

        // 下一张
        next() {
          // 如果图片列表小于需要显示的数量,则不进行滚动
          if (this.items.length < this.total) {
            return
          }
    
          // 向后追加一个元素,该元素为:
          // 显示列表中最后一个元素在原数组中的后一个元素
          // 如果已经是最后一个元素,则使用第一个元素
    
          let indexOfItems = this.items.findIndex(
            item => item.id === this.list[this.list.length - 1].id
          )
    
          if (indexOfItems === this.items.length - 1) {
            // 使用第一个元素
            this.list.push(this.items[0])
          } else {
            // 使用后一个元素
            this.list.push(this.items[indexOfItems + 1])
          }
          // 移除当前显示图片中的第一个
          this.list.shift()
          this.isReverse = false
        },
    

    4.6 对应的还有一个 prev 函数,与 next 函数逻辑相反,这里就不展示代码了

    4.7 点击图片时,向父组件释放事件 selectedItem,传递两个参数 item 和 index 分别为当前点击的对象,和该对象在 list 中的位置

        // 点击图片
        selectedItem(item, index) {
          this.$emit('selectedItem', item, index)
        },
    

    4.8 鼠标悬浮在组件上时,停止自动切换(若 autoplay 为 ture ), 鼠标离开时,继续切换

        handleMouseEnter() {
          this.hover = true
          this.pauseTimer()
        },
    
        handleMouseLeave() {
          this.hover = false
          if (this.autoplay) {
            this.startTimer()
          }
        },
    

    4.9 然后是过渡效果的实现

    因为 arrow 元素也在 transition-group 中,所以当 arrow=‘ hover ’时,arrow 的显示、隐藏也会触发钩子函数,但是我们的钩子函数是针对 image-item 写的,所以需要在函数中检测是哪个元素触发的,这里通过检查 className 进行判断。 然后针对向前、向后两种情况设置不同的样式

     beforeEnter(el) {
          // 只对 image-item 使用过渡
          let isImageItem = el.className.indexOf('image-item') > -1
          if (isImageItem) {
            el.style.opacity = 0
            if (this.isReverse) {
              el.style.transform = 'translateX(-100%)'
            } else {
              el.style.transform = 'translateX(100%)'
            }
          }
        }
    

    4.10 这里使用了 Velocity,这是一个实现动画效果的 js 库,之所以使用这个库是因为试了 n 种方案都没能实现预期效果 emm

    enter(el, done) {
          // 只对 image-item 使用过渡
          let isImageItem = el.className.indexOf('image-item') > -1
          if (isImageItem) {
            Velocity(el, { opacity: 1, translateX: '0px' }, { complate: done })
          } else {
            done()
          }
        }
    

    4.11 然后是对应的 beforeLeave、leave 函数,这里就不展示了

    以上基本就是所有 js 部分,整体感受就是,一旦实现逻辑搞清楚,代码实现起来还是挺容易的,然后就是框架的熟悉程度。

    5. npm 包发布

    这本是工作业务中的一个功能需求,因为没能在网上找到现成的轮子,找个差不多效果的领导不满意,所以只能自己写了,做都做了不发出来感觉白写了. . .

    npm 发布流程简单概括就是

    1.注册 去 npm 官网注册个账号

    2.生成 npm 包
    文件夹中有 package.json 文件就是一个 npm 包

    3.在终端使用 npm publish 发布包,成功之后,该项目文件夹下所有文件都会上传至 npm 官网,当用户使用 npm install 安装后,就会将整个文件夹下载至 node_modules 文件夹中,对于这个项目,本是一个使用 vue-cli 生成的 vue 项目,组件路径src/components/VaCarousel.vue,所以使用 npm install va-carousel 安装之后,只需要在项目中像这样导入即可使用(前提是你的项目也是使用 vue-cli 生成的,对于其他方式搭建的项目可能会出现一些错误):import VaCarousel from 'va-carousel/src/components/VaCarousel.vue'

    以上

    11 条回复    2018-05-02 13:46:30 +08:00
    newbieo0O
        1
    newbieo0O  
       2018-05-02 09:11:17 +08:00
    请使用原生JS写一遍 :D
    aaronlam
        2
    aaronlam  
       2018-05-02 09:21:46 +08:00
    学习了
    falcon05
        3
    falcon05  
       2018-05-02 09:23:01 +08:00 via iPhone
    demo 图片成条状了,我在 iPhone Safari 打开的。
    v21an
        4
    v21an  
       2018-05-02 11:31:54 +08:00 via Android
    zhangxiaoshang66
        5
    zhangxiaoshang66  
    OP
       2018-05-02 12:10:46 +08:00
    @falcon05 针对 PC 端写的 emm
    zhangxiaoshang66
        6
    zhangxiaoshang66  
    OP
       2018-05-02 12:11:06 +08:00
    @newbieo0O 和自己过不去吗
    rabbbit
        7
    rabbbit  
       2018-05-02 12:37:29 +08:00
    跟自己过不去😂...
    lamada
        8
    lamada  
       2018-05-02 13:28:40 +08:00
    动画优化下,不要做成切换时卡顿的感觉
    zhangxiaoshang66
        9
    zhangxiaoshang66  
    OP
       2018-05-02 13:38:48 +08:00
    @lamada 卡顿使网络加载图片慢的问题,跑一会,图片都缓存下来就不会卡了,或者换成小图片会好一些
    zicla
        10
    zicla  
       2018-05-02 13:41:44 +08:00
    如果能做到 Swiper 丝般润滑就厉害了
    zhangxiaoshang66
        11
    zhangxiaoshang66  
    OP
       2018-05-02 13:46:30 +08:00
    @zhangxiaoshang66 优化的空间还是有的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2819 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 37ms · UTC 07:09 · PVG 15:09 · LAX 23:09 · JFK 02:09
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.