美文网首页饥人谷技术博客VueJS
「造轮子」—Xue-ui (轮播)

「造轮子」—Xue-ui (轮播)

作者: 灵魂治愈 | 来源:发表于2018-11-19 03:30 被阅读95次

在使用 Vue 之后,写了两个版本的轮播,一个版本利用 Vue 的动画很轻松地写出了无缝轮播,但是由于 flextransform 一些错综复杂的关系,导致图片切换时图片间会有缝隙,强迫症受不了,于是写了第二个版本利用克隆 dom 的方式实现无缝轮播。简单总结两个版本轮播的实现思路。

第一个版本

这个版本是课程里实现轮播的思路,特点是充分利用 Vue 的动画,完全不操作 dom ,只操作数据。

组件基本结构
<x-slides>
        <x-slides-item :index="0"><div class="img">1</div></x-slides-item>
        <x-slides-item :index="1"><div class="img">2</div></x-slides-item>
        <x-slides-item :index="2"><div class="img">3</div></x-slides-item>
        <x-slides-item :index="3"><div class="img">4</div></x-slides-item>
</x-slides>

组件分为容器组件和单个 item 子组件,均带有一个插槽。

容器组件

容器组件的关键 css 属性:

overflow: hidden;
position: relative;
display: flex;

容器组件在 data 设置一个属性 currentitem 子组件接受props

        props: {
            index: {
                type: Number,
                required: true
            }
        }

由于 index 是组件切换的依赖,因此在使用时是必须传的。想过尝试在子组件的 data 中声明 index ,然后在父组件 mounted 钩子中遍历 this.$children 去设置每个子组件的 index ,这样就可以不传这个 props 了。但是这种做法并不好,首先,Vue 并不保证 mounted 钩子里子组件也一并挂载完毕,有可能会出现某个子组件未挂载完毕而没有被设置 index 的问题,要确保每个子组件都被遍历到,需要使用 nextTick 钩子;其次 this.$children 也不保证顺序,可能会出现图片顺序混乱的问题。
PS:另一种使用 nextTick 钩子的方式:
通常我们使用 nextTick 是这样,提供一个回调函数,比如在 mounted 中使用:

        mounted() {
            this.$nextTick(() => {
                doSomething()
            })
        }

根据官方文档,2.1.0 起,如果没有提供回调, nextTick 将返回一个 promise ,因此上述代码也可以这样写:

        async mounted() {
            await this.$nextTick()
            doSomething()
        }
子组件

子组件模板:

    <transition name="slide">
        <div class="x-slides-item" v-show="index===current" :class="{reverse}">
            <slot></slot>
        </div>
    </transition>

简单的通过 index===current 来个控制子组件的显示, index 是子组件接受的 propscurrent 是子组件通过计算属性映射的容器组件的 current

        computed: {
            current() {
                return this.$parent.current
            }
        }

这样,容器通过改变 current 的值,就可以控制子组件的切换。

动画效果

此版本轮播主要就在于切换时的动画效果。
首先给子组件加上过渡效果,Vue 过渡类名对应 css 如下:

    .slide-leave-active {
        position: absolute;
        top: 0;
        left: 0;
    }
    .slide-enter {
        transform: translateX(100%);
    }
    .slide-leave-to {
        transform: translateX(-100%);
    }
    .slide-enter.reverse {
        transform: translateX(-100%);
    }
    .slide-leave-to.reverse {
        transform: translateX(100%);
    }

由于绝对定位会让元素脱离文档流,如果所有子组件根元素都使用绝对定位,那么容器组件将出现高度塌陷的问题,于是采用只让正在离开的子组件绝对定位的方式来保证容器组件的高度。

切换方向改变

通过在子组件中使用 watch ,可以判断切换的方向,从而控制动画的方向

        watch: {
            current(val, oldVal) {
                if (val - oldVal > 0) {
                    this.reverse = false;
                }
                if (val - oldVal < 0) {
                    this.reverse = true;
                }
                //最后一个到第一个
                if (val - oldVal === -(this.len - 1)) {
                    this.reverse = false;
                }
                //第一个到最后一个
                if (val - oldVal === this.len - 1) {
                    this.reverse = true;
                }
            }
        }

当切换方向改变时,子组件 reverse 类名激活,其切换动画也会相应改变。
这种方式实现轮播非常巧妙,契合 Vue 数据驱动的思想,无任何 dom 操作。缺点是图片切换时会有间隙,在尝试多种方式无果后,采用克隆 dom 的方式实现了第二个版本的轮播。

第二个版本

组件基本结构

第二个版本的轮播结构更为简单

 <x-slides>
        <div class="img">1</div>
        <div class="img">2</div>
        <div class="img">3</div>
        <div class="img">4</div>
 </x-slides>

组件内部会在挂载完毕之后,克隆第一张图片放在最后一张图片后,克隆最后一张图片放在第一张图片前。
克隆操作的代码:

            cloneDom() {
                let nodes = this.$slots.default.filter(node => node.elm.nodeType !== 3)   
                nodes.forEach(node => {
                    node.elm.style['flex-shrink'] = 0
                })
                this.length=nodes.length
                const first = nodes[0].elm.cloneNode(true)
                const last = nodes[nodes.length - 1].elm.cloneNode(true)
                this.$refs.view.prepend(last)
                this.$refs.view.append(first)
            }

此处两小坑,一是 Vue 单文件组件自动取消空格,因此在单文件组件中遍历插槽,其子元素个数和插入的图片数量一致,然而如果是在 HTML 文件中使用组件,插槽中将出现文本节点,因此在克隆操作前需要根据 nodeType 属性来过滤掉文本节点;二是组件使用 flex 布局,会出现压缩子元素的情况,因此在需设置子元素的 flex-shrink0
此版本轮播的切换原理为通过 translateX 属性的改变来实现,并且在第一张到最后一张和最后一张到第一张采用动画效果结束立即更换图片的方式实现,具体原理之前已经用原生 JS 实现过,不再赘述。

相关文章

  • 「造轮子」—Xue-ui (轮播)

    在使用 Vue 之后,写了两个版本的轮播,一个版本利用 Vue 的动画很轻松地写出了无缝轮播,但是由于 flex ...

  • 「造轮子」—Xue-ui (1)

    写在前面 Xue-ui 是我在饥人谷学习前端过程中制作的一个 UI 组件库。目前大大小小的组件已经写了20来个,并...

  • 造轮子-轮播

    1.最初的思路 当前窗口由第一个变成第二个:先把第二个放在第一个的后面,然后第一个做左滑动的动画,第二个也做向左滑...

  • 【Android】造轮子:轮播图

    前言 目前市场上的APP中,轮播图可以说是很常见的。一个好的轮播图,基本上适用于所有的APP。是时候打造一个自己的...

  • Rxjava + ViewPager 打造实用图片轮播

    背景 说到图片轮播,之前写过一篇文章《造轮子:android自定义专属广告轮播控件》,不过当时是采用ViewFli...

  • 2019-05-31 程序员修仙进阶标准,你到哪个阶段了?

    闭门造轮子 > 使用别人的轮子 > 开门造轮子 > 分享轮子

  • 造轮子之仿射变换

    有人说,我们不应该再造轮子;也有人说,学习怎么造轮子可以带来更深的理解。我说,用轮子有用轮子的乐趣,造轮子有造轮子...

  • 造了个小轮子(轮播图)

    "VLoopScrollView" 轮播图轮子 放个图压压惊 ... 好久以前写的。一直也没太想过要上传到gith...

  • 开源时代,一杯敬明天,一杯敬过往

    Reinvent the Wheel 从“我们不要重复造轮子”到,兄弟们“我们造轮子”吧 不要重复造轮子 意味着我...

  • 轮子

    不是想造轮子,而是想获得造轮子的能力,以便我需要造的时候造的出来

网友评论

    本文标题:「造轮子」—Xue-ui (轮播)

    本文链接:https://www.haomeiwen.com/subject/vonnfqtx.html