背景

  • 手写个组件算是基础操作,懂得原理后可以自己手写或者拿别人的组件进行修改。

思路

  • 用vue写轮播图主要就是靠vue动画。轮播效果靠v-if与设置下一个div的位置。

流程

  • 先建立基本的框架,swiper.vue swiperItem.vue App.vue
  • App.vue中写用户自己的内容,swiperItem为swiper.vue的子组件,用来放轮播项。

App.vue

<template>
  <swiper>
    <swiperItem>
      <div class="content">页面1</div>
    </swiperItem>
    <swiperItem>
      <div class="content">页面2</div>
    </swiperItem>
    <swiperItem>
      <div class="content">页面3</div>
    </swiperItem>
  </swiper>
</template>
<script>
import swiperItem from './components/SwiperItem'
import swiper from './components/Swiper'
export default {
  components: {
    swiper,
    swiperItem
  }
};
</script>
<style lang="stylus"></style>

swiper.vue

<template>
  <div class="swiper">
    <div class="viewport">
      <slot></slot>
    </div>
  </div>
</template>
<script>
export default {
    
}
</script>

swiperItem.vue

<template>
<div class="swiper-item">
    <slot></slot>
</div>
</template>
<script>
export default {
    
}
</script>
  • 这样基本骨架就有了,页面中会显示3段文字。
  • 下一步要使得页面只显示一项,我们需要加属性,使得条件渲染。
<template>
  <swiper v-model="selected">
    <swiperItem name = "box1">
      <div class="content">页面1</div>
    </swiperItem>
    <swiperItem name = "box2">
      <div class="content">页面2</div>
    </swiperItem>
    <swiperItem name = "box3">
      <div class="content">页面3</div>
    </swiperItem>
  </swiper>
</template>
<script>
import swiperItem from './components/SwiperItem'
import swiper from './components/Swiper'
export default {
    data:()=>{
        return {selected:'box3'}
    },
  components: {
    swiper,
    swiperItem
  }
};
</script>
<style lang="stylus"></style>
<template>
  <div class="swiper">
    <div class="viewport" >
      <slot></slot>
    </div>
  </div>
</template>

<script>
export default {
    props:{
        value:{
            type:String,
            default:''
        }
    },
    mounted(){
        this.$children.forEach(vm => {
            vm.selected = this.value
        });
    }
}
</script>
<template>
    <div class="swiper-item" v-if="name===selected?true:false" >
        <slot></slot>
    </div>
</template>

<script>
export default {
    props:{
        name:{
            type:String,
            required:true
        }
    },
    data(){
        return {selected:''}
    },
    mounted(){
    }
}
</script>
  • 这样就可以进行条件渲染了,变动App.vue的selected属性可以改变渲染的页面。
  • 然后稍微修改一下,增加点样式。试着去在App.vue中改变selected的值,如果颜色变了说明成功。
  • 由于swiper的value在mount下只调用一次,所以需要在watch里进行监控,如果值变了就更新。
<template>
  <swiper v-model="selected" >
    <swiperItem name = "box1">
      <div class="content" style="background:red">页面1</div>
    </swiperItem>
    <swiperItem name = "box2">
      <div class="content" style="background:green">页面2</div>
    </swiperItem>
    <swiperItem name = "box3">
      <div class="content" style="background:yellow">页面3</div>
    </swiperItem>
  </swiper>
</template>
<script>
import swiperItem from './components/SwiperItem'
import swiper from './components/Swiper'
export default {
   	data:()=>{
       return {selected:'box3'}
    },
  	components: {
    	swiper,
    	swiperItem
  	},
  	mounted(){
      setTimeout(() => {
          this.selected='box1'
      }, 1000);
  }
};
</script>
<style lang="stylus">
.content
    width 300px
    height 300px
    text-align center
    margin auto
</style>
<template>
  <div class="swiper">
    <div class="viewport" >
      <slot></slot>
    </div>
  </div>
</template>

<script>
export default {
    props:{
        value:{
            type:String,
            default:''
        }
    },
    methods:{
        showChild(){
            let currentName = this.value||this.$children[0].name
            this.$children.forEach(vm => {
                vm.selected = currentName
            });
        }
    },
    watch:{
        value(){
            this.showChild()
        }
    },
    mounted(){
        this.showChild()
    }
}
</script>
<style lang="stylus">
.swiper
    border 5px solid black 
    width 300px
    height  300px
    margin auto
    text-align center
</style>
  • 然后设置定时器用触发v-model的input来改变父组件的selected值。
  • 在swiper里通过this.$children拿到列表项名字。
  • 在列表项里用v-enter v-leave来设置动画。
  • vue的动画的离开和进入实际就是元素的离开和进入动画。这里需要元素移动,进入时候在swiper框右边一个身位,出来时候在swiper框左边一个身位。
<template>
  <transition>
    <div class="swiper-item" v-if="isShow">
      <slot></slot>
    </div>
  </transition>
</template>

<script>
export default {
  props: {
    name: {
      type: String,
      required: true
    }
  },
  data() {
    return { selected: "" };
  },
  mounted() {},
  computed: {
    isShow() {
      return this.name === this.selected;
    }
  }
};
</script>
<style lang="stylus">
.v-enter-active,.v-leave-active
  transition all 1s linear 
  position absolute
.v-leave-to
  transform  translate(-100%)
.v-enter
  transform  translate(100%)
</style>
<template>
  <div class="swiper">
    <div class="viewport">
      <slot></slot>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    value: {
      type: String,
      default: ""
    },
    autoplay:{
      type:Boolean,
      default:true
    }
  },
  methods: {
    showChild() {
      this.currentName = this.value || this.$children[0].name;
      this.$children.forEach(vm => {
        vm.selected = this.currentName;
      });
    },
    autoShow(){
      if(this.autoplay){
      let timer = setInterval(() => {
        let index = this.names.indexOf(this.currentName)
        let newIndex = ++index
        if(newIndex===this.names.length){
          newIndex=0
        } 
        if(newIndex===-1)newIndex=this.names.length-1
        this.$emit('input',this.names[newIndex])
      },2000);
      }
    }
  },
  watch: {
    value() {
      this.showChild();
    }
  },
  beforeDestroy(){
    clearInterval(this.timer)
  },
  data(){
    return {currentName:''}
  },
  mounted() {
    this.names = this.$children.map((vm)=>vm.name)
    this.showChild();
    this.autoShow()
  }
};
</script>
<style lang="stylus">
.swiper 
  border 5px solid black
  width 300px
  height 300px
  margin auto
  text-align center
  overflow hidden
.viewport
  position relative
</style>
<template>
  <swiper v-model="selected" autoplay>
    <swiperItem name="box1">
      <div class="content" style="background:red">页面1</div>
    </swiperItem>
    <swiperItem name="box2">
      <div class="content" style="background:green">页面2</div>
    </swiperItem>
    <swiperItem name="box3">
      <div class="content" style="background:yellow">页面3</div>
    </swiperItem>
  </swiper>
</template>
<script>
import swiperItem from "./components/SwiperItem";
import swiper from "./components/Swiper";
export default {
  data: () => {
    return { selected: "box3" };
  },
  components: {
    swiper,
    swiperItem
  },
  mounted() {
  }
};
</script>
<style lang="stylus">
.content {
  width: 300px
  height: 300px
  text-align: center
  margin: auto
}
</style>
  • 这样简易轮播图基本完成,下一篇再对这个进行优化。
Logo

前往低代码交流专区

更多推荐