Vue——轮播图

APP.Vue

<template>
  <div>
   
    <div class="banner-wrap">
      <my-bananer :images="banners"></my-bananer>
    </div>

  </div>
</template>

<script>

import Vue from 'vue';
import MyBananer from'./components/my-banan/index.js';
Vue.use(MyBananer)//注册轮播图插件
export default {
  name: 'App',
    data(){
      return {
        banners:[]
      }
    },

    created(){
      setTimeout(()=>{
        this.banners=[
        {image:require("./assets/images/banner/banner1.jpg")},
        {image:require("./assets/images/banner/banner2.jpg")},
        {image:require("./assets/images/banner/banner3.jpg")}
      ]},3000)
},


    }
</script>

<style>
.banner-wrap{width:700px;height:350px;}
</style>

components/my-banan/banan.Vue

banan.Vue

<template>
  <div class="banner-main" @mouseover="stop()" @mouseout="play()">
    
    <transition-group name="banner">
      <div class="banner-slide" v-for="(item,index) in data" :key="index" v-show="item.checked"><img :src="item.image" alt="" /></div>
    </transition-group>

    <div class="spot-wrap">
      <div :class="{spot:true, active:item.checked}" v-for="(item,index) in data" :key="index" @click="changeImage(index)"></div>
    </div>
 
  </div>
</template>

<script>
export default {
  name: 'my-banner',
  data(){
    return {
      data:[]
    }
  },
  props:{
    images:{
      type:Array,
      required:true
    },
    autoplay:{
      type:Number,
      default:2000
    }
  },
  created(){
    this.index=0;
    this.timer=null;
    this.play();
  },
  methods:{
    changeImage(index){
      this.index=index;
      for(let i=0;i<this.data.length;i++){
        if(this.data[i].checked){
          this.data[i].checked=false;
          break;
        }
      }
      this.data[this.index].checked=true;
      this.$set(this.data,this.index,this.data[this.index]);
    },
    stop(){
      clearInterval(this.timer)
    },
    play(){
      this.timer=setInterval(()=>{
        if(this.index<this.data.length-1){
          this.index++;
        }else{
          this.index=0;
        }
        this.changeImage(this.index);
      },this.autoplay)
    }
  },
  destroyed() {
    this.stop();
  },
  watch:{
    images(val){
      this.data=[...val];
      if(this.data.length>0){
        for(let i=0;i<this.data.length;i++){
          if(i==0){
            this.data[i].checked=true;
          }else{
            this.data[i].checked=false;
          }
        }
      }
    }
  }
};
</script>

<style scoped>
.banner-main {width:100%;height:100%;position:relative;z-index:1;}
.banner-slide{width:100%;height:100%;position: absolute;z-index:1;left:0px;top:0px;}
img{width:100%;height:100%;}
.spot-wrap{width:auto;height:auto;position: absolute;z-index:2;left:50%;bottom:8%;transform: translateX(-50%);display: flex;justify-content: center}
.spot{width:25px;height:8px;background-color:rgba(0,0,0,0.6);margin:0px 3px;cursor:pointer}
.spot.active{background-color:rgba(255,255,255,0.6);}

.banner-enter-active,.banner-leave-active{transition:all 1s}
.banner-enter{opacity:0}
.banner-enter-to{opacity:1}
.banner-leave{opacity:1}
.banner-leave-to{opacity:0}
</style>

components/my-banan/index.js

index.js

import MyBanan from "./banan.vue";
export default {
  install(Vue) {
    Vue.component("my-bananer", MyBanan);
  },
};

问题1:父组件如何通过 props 向子组件传递数据?

父组件有一个数据 banners,想要传递给子组件。父组件的模板中使用子组件时,可以通过 :propName=“banners” 的方式传递。

v-bind :Vue 的指令,用于动态绑定数据到子组件的属性(props)上,这里 images就属于子组件的props上的一个属性
简写:v-bind:propName=“data” 可以简写为 :propName=“data”。如这里的
< my-bananer :images=“banners” >< /my-bananer >

问题2:父组件明明没有给子组件传checked属性,那checke属性哪来的?

当父组件传递新的 images 数据时,watch 会触发。
子组件内部将父组件传递的 images 数据复制到本地的 data 数组中(this.data = […val])即浅拷贝。浅拷贝的好处是,子组件自己新开辟了一个数组,只把父组件传递的数组中的数据拿走,这样子组件和父组件就可对自己的数组修改,但不会相互影响。

然后子组件遍历 data 数组,为每张图片添加 checked 属性:
第一张图片的 checked 设为 true(表示默认显示第一张图片)。
其他图片的 checked 设为 false。
因此,虽然父组件没有传递 checked 属性,但子组件会在内部动态添加这个属性。

问题3:点击轮播图下方的小圆点区域,实现轮播图切换是怎么做到的?

轮播图下方的小圆点区域,每个小圆点对应一张图片。通过 v-for 遍历 data 数组生成,点击小圆点会触发 changeImage(index) 方法切换到对应的图片。

问题4:为什么 index=0; timer=null; play();不写在data(){ }中,而写在Created(){ }中。 且在Created中index,timer,play()没定义,为什么可以用this调用呢?

(1)data 中存放什么?只存放需要响应式的数据,例如:data 数组:存储图片信息,它的变化会影响视图渲染

created 中初始化什么?初始化非响应式数据或执行逻辑操作,例如:this.index:当前图片索引。this.timer:定时器对象。调用 this.play() 方法启动自动播放

那 什么是响应式数据?
响应式数据会被 Vue 自动跟踪,任何对它的修改都会触发视图更新。
如果一个数据只是用于逻辑控制(如计数器、定时器),而不需要 Vue 跟踪它的变化,则不 需要放在 data 中。
示例:
index 用于记录当前图片索引,虽然它会变化,但它的变化并不会直接导致视图更新,因此不需要响应式。timer 是一个普通的定时器对象,完全不需要 Vue 跟踪。

(2)
在 Vue 中,当你通过 this.xxx 的方式给组件实例添加一个属性时,即使这个属性没有提前在 data 中定义,Vue 也会允许你这样做,并将它作为一个普通的 JavaScript 属性附加到组件实例上如

data() {
  return {
    message: 'Hello, Vue!'
  };
},
created() {
  this.newProperty = 'This is a new property'; // 动态添加属性
  console.log(this.newProperty); // 输出:This is a new property
}

在上面的例子中,newProperty 并没有在 data 中定义,但它仍然可以通过 this.newProperty 被访问和修改。这是因为 Vue 组件的 this 是一个普通的 JavaScript 对象,你可以随时给它添加新的属性。
虽然你可以通过 this.xxx 动态添加属性,但这些属性是非响应式的。也就是说,它们的变化不会触发视图更新。
在你的代码中,this.timer 和 this.index 是非响应式属性,因为它们的变化不会直接影响视图。
它们只是用于逻辑控制(如计数器、定时器等),而不需要 Vue 自动跟踪。

问题5:autoplay:{type:Number,default:2000},父组件中并没有传递给子组件autoplay,那autoplay的值是多少?

(1)autoplay 的作用:autoplay 是一个 数字类型 的属性,用于控制轮播图的自动播放间隔时间(以毫秒为单位)。换句话说,它决定了轮播图每隔多久切换到下一张图片。

default: 2000 表示如果父组件没有传递 autoplay 属性,那么默认值为 2000 毫秒即 2 秒)。默认值的作用是确保即使父组件没有提供这个属性,子组件也能正常运行

问题6:轮播图组件的几个核心方法,包括图片切换逻辑、自动播放和定时器管理。是如何实现的?

轮播图自动播放:利用 setInterval 创建一个定时器,每隔 this.autoplay 毫秒执行一次回调函数。setInterval :每隔指定的时间间隔重复执行某个函数。返回一个定时器 ID,用于标识这个定时任务,这里用timer接收。
clearInterval :清除由 setInterval 创建的定时任务。需要传入 setInterval 返回的定时器 ID。

点击轮播图小圆点切换:利用单击事件changeImage(index),这个方法用于切换到指定索引的图片,并更新对应的 checked 状态。

destroyed() 是 Vue 的生命周期钩子。它是由 Vue 提供的标准方法,不是自定义的。
当组件被销毁时,destroyed 钩子会被触发。这个钩子通常用于清理资源,例如清除定时器、取消事件监听器等,当组件被销毁时,调用 this.stop() 方法清除定时器。
这是为了避免内存泄漏:如果定时器没有被清除,即使组件已经销毁,定时器仍然会继续运行,占用不必要的资源。

先实现静态的,轮播图,在实现轮播图下方的圆点区域点击切换事件
轮播图下标点击切换图片

changeimg(index){
    for(let i=0;i<this.data.length;i++){
      if(this.data[i].checked){   ----------找到之前选中的图片
        this.data[i].checked=false ;--------取消选中状态
          break;
      }
    }

   this.index=index; -----------------更新当前显示的图片索引
   this.data[this.index].checked=true;----- 设置当前图片为选中状态
   this.$set(this.data,this.index, this.data[this.index])-------强制更新视图

  }

问题7:Vue 中的 过渡动画 的 CSS 样式,是如何实现的

Vue 的过渡系统
Vue 提供了一套内置的过渡机制,用于在元素插入、更新或移除时添加动画效果。通过结合 CSS 类名,可以轻松实现各种动画。

当一个元素被插入或移除时,Vue 会自动为该元素添加特定的类名
这些类名可以用来定义动画的不同阶段(如进入、离开等)。

1.图片切换时的动画流程

(1)新图片进入 (enter) :
Vue 会给新图片添加 .banner-enter 类,此时 opacity: 0,图片是完全透明的。
随后,Vue 添加 .banner-enter-active 类,并开始执行过渡动画。
动画结束后,Vue 替换为 .banner-enter-to 类,此时 opacity: 1,图片完全显示。

(2)旧图片离开 (leave) :
Vue 会给旧图片添加 .banner-leave 类,此时 opacity: 1,图片是完全可见的。
随后,Vue 添加 .banner-leave-active 类,并开始执行过渡动画。
动画结束后,Vue 替换为 .banner-leave-to 类,此时 opacity: 0,图片完全消失。

2.实际效果
淡入淡出效果:
新图片从完全透明(opacity: 0)逐渐变为完全可见(opacity: 1)。
旧图片从完全可见(opacity: 1)逐渐变为完全透明(opacity: 0)。

动画时间:
整个动画过程持续 1 秒(由 transition: all 1s; 定义)。

 .banner-enter-active,.banner-leave-active{transition:all 1s}
   作用:定义进入和离开动画的持续时间和过渡效果。表示所有属性的变化都会在 1 秒内完成平滑过渡
  
  .banner-enter{opacity:0}
   作用:定义新元素刚插入时的初始状态。opacity: 0; 表示新图片在刚开始时是完全透明的(不可见)。
   
  .banner-enter-to{opacity:1}
    作用:定义新元素进入动画结束后的最终状态。opacity: 1; 表示新图片在动画结束后变为完全可见。
    
  .banner-leave{opacity:1}
  作用:定义旧元素离开动画开始时的初始状态。opacity: 1; 表示旧图片在刚开始离开时是完全可见的
 
 .banner-leave-to{opacity:0}
 作用:定义旧元素离开动画结束后的最终状态。opacity: 0; 表示旧图片在动画结束后变为完全透明(不可见)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值