封装一个自己的轮播图组件(基于 Vue)

创建目录结构

vue-swiper/
├── src/
│   ├── components/ //内置组件
│   │   ├── indicator.vue  // 指示器组件
│   │   └── item.vue  // 单个轮播图容器组件
│   ├── main.js  // 项目出口
│   └── main.vue  //组件出口
├── README.md  
├── package.json
└── vue.config.js  // 组件打包配置文件

项目结构解析

我们知道一个轮播图是由其容器和内容构成的,这里首先把整个轮播图组件拆分为入口组件和其要用到的子组件(指示器组件可以根据自己的项目维护方式自由拆分,这里把它单独拆出来便于以后维护)。以往我们在使用第三方组件的时候可能会看到其组件就是单独的一个入口,所有的属性传入和事件监听都是放在了一个大的组件上了,其实这种封装的方式后期是不好维护的,而且在使用的时候也是不容易发现出现的问题,为了能让组件自身更具有语义化以及开发当中便于调试和后期组件的维护,我们把容器和内容分离开拆成两个组件,这样用户在模板中书写组件时候就能像使用普通的 HTML 标签一样嵌套使用了。

前提

以往我们开发一个页面中的轮播图可能会牵扯到大量手动的 DOM 样式操作,这里带来的问题就是我们关心的视图变化和逻辑操作混到了一起,关注点没有分离开,无论从维护还是代码的可读性这种方式都不是最优的。现在我们是基于 Vue 开发的这个组件,这样就可以利用它最核心的思想(数据驱动视图改变)开完成这个组件的开发。

数据驱动动画

有了数据驱动的这个主要思想,我们就可以围绕它展开组件开发了。首先把轮播图播放动画当中能用到的状态变量进行初始化

// main.vue
data(){
  return {
    reversing:false,//控制动画播放到首尾时无缝跳转的开关
    swiperItemCount:0,// 初始化传入的轮播图个数
    index:0 // 控制轮播图当前位置的索引
  }
},
computed:{
  scrollItemCount(){  // 内容实际存在的图片个数
    return this.swiperItemCount+2 // 组件初始化以后需要复制传进来的首尾两张图片到指定位置,所以这里需要加上2
  }
}

之所以要定义 reversing 这个变量是因为当动画播放到首尾端点的时候,我们要瞬间跳转到对应的首尾位置,然后就可以更改这个变量的值来关闭相应的动画以达到用户视觉上无缝滚动的效果。

至于 index 其语义大概已经描述了它所需要做的事情了,就是通过更改这个索引的值以驱动图片的位置移动,这样就有了视觉上动画的效果。相关的代码如下

 watch: {
    index(newIndex, oldIndex) {
      const endIndex = this.scrollItemCount - 1
      if (newIndex === endIndex && newIndex > oldIndex) {
        setTimeout(() => {
          this.reversing = true
          this.index = 1
          setTimeout(() => {
            this.reversing = false
          }, 100)
        }, this.duration)
      } else if (newIndex === 0 && newIndex < oldIndex) {
        setTimeout(() => {
          this.reversing = true
          this.index = endIndex - 1
          setTimeout(() => {
            this.reversing = false
          }, 100)
        }, this.duration)
      }
    },
 }

这里通过观测动画播放的当前位置这个变量,我们在相应的时机更改它的值来达到整个包装容器的瞬间移动,这样也就产生了图片播放连续的动画效果了。

相关的技术要点

前提

单个的图片内容是以 slot(相关 Api 查看这里,本片文章不做介绍)的方式接收的,我们知道当前组件的$slot 属性存储的是 vnode(不了解 vnode 的看这里,同样不过多介绍)。

匿名插槽内容筛选

有了 slot,组件内部可以接受外界传进来的一切内容,而我们这里只需要组件定义的指定子组件,所以在组件启动后还需要对默认的匿名插槽重新处理后才可以使用,让我们看代码吧

 created() {
    this.$slots.default = this.$slots.default.filter(vnode => {
      return (
        vnode.componentOptions && vnode.componentOptions.tag === 'swiper-item'
      )

      // swiper-item 取决于注册的指定组件名称
    })
    this.swiperItemCount = this.$slots.default.length
  },

了解 Vue 虚拟 DOM 渲染原理的同学应该知道,每一次的 vnode 更新都会导致页面组件的冲渲染,代码中通过过滤需要的 vnode 重新赋值到接收匿名插槽的接口上,这样 Vue 内部通过检测 vnode 的变更会渲染新的 vnode 到组件视图上,接下来就可以调整内容节点的结构了。

如何复制外界传入的首尾图片

vnode 有一个 tag 属性存储了它所渲染的真实 DOM 的引用,这样我们就可以通过相关的 DOM 操作 Api 来复制这些节点从而达到我们的目的,相关的代码片段在这里

mounted() {
  const firstItem = this.$slots.default[0].elm
  const lastItem = this.$slots.default[this.$slots.default.length - 1].elm
  this.$refs.wrapper.appendChild(firstItem.cloneNode(true))
  this.$refs.wrapper.insertBefore(lastItem.cloneNode(true), firstItem)
  this.autoplay && this.play()
},

组件效果展示

1.gif

总结

文章通过介绍 Vue 数据驱动的思想实现了一些动画效果,当我们想封装一些别的组件的时候同样可以利用这一点来达到各种各样的需求。
至于组件其他功能的具体实现过程这里就不在介绍了,有兴趣的同学可以查看本项目的 Github 仓库:good-swiper

备注:本篇文章属于作者原创,转载请标注出处,谢谢!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容