swiper轮播图实现zoom效果

效果图:


安装swiper插件

创建Swiper.vue组件

<template>

  <div class="swiper-component" @touchstart="onTouchStart" @touchend="onTouchEnd" :style="{height:height}">

    <ul :style="[ulStyle, swiperStyle]">

      <li v-for="(item, ind) in list" :key="ind" :style="{width: itemWidth + 'px'}" :class="[index===ind?'active':'', effect]" @click="handleClick(item)">

        <img :src="item">

      </li>

    </ul>

    <div v-show="options.showDots" class="swiper-dots">

      <div v-for="(item, ind) in list" :key="ind" class="dots-item" :class="{active:index===ind}"></div>

    </div>

  </div>

</template>

<script>

export default {

  name: "swiper",

  data() {

    return {

      ulStyle: { width: "750px", paddingLeft: "20px" }, // 轮播图容器宽度

      itemWidth: 750, // 单个轮播图容器的宽度,默认屏幕宽度,

      swiperStyle: {}, // 控制轮播的样式

      index: 0, // 当前显示的轮播图索引,默认第一张

      touchStart: {}, // 触摸开始点位

      touchEnd: {}, // 触摸结束点位

      intervalTime: "" // 循环轮播定时器

    };

  },

  props: {

    height: {

      type: String,

      default: "3rem"

    },

    options: {

      type: Object,

/*这里的default报错可以去掉,因为是在父组件里定义过*/

      default: {

        showDots: true, // 是否显示分页器

        interval: 3000, // 轮播间隔时间,默认3s

        autoplay: true, // 是否自动播放

        loop: false // 是否循环轮播

      }

    },

    list: {

      type: Array,

/*这里的default报错可以去掉,因为是在父组件里定义过*/

      default: []

    },

    effect: {

      type: String,

      default: "zoom" // 轮播图的样式类型,默认正常类型 normal,可选:zoom(缩放)

    }

  },

  mounted() {

    this.calcWidth();

    this.handleLoopMove();

this.handleMove();

  },

  methods: {

    /**

    * 初始化时的一些宽度计算操作

    */

    calcWidth() {

      // 页面更新后执行宽度计算

      this.$nextTick(function() {

        if (this.effect === "normal") {

          // 如果是正常模式,一张图的宽度为屏幕宽度

          this.itemWidth = document.body.clientWidth; // 获取屏幕的宽度

        } else if (this.effect === "zoom") {

          // 如果是缩放模式,控制轮播图显示的宽度,两边流出空隙

          this.itemWidth = document.body.clientWidth - 40; // 获取屏幕的宽度

        }

        this.handleType();

        var length = this.list.length; // 获取列表的个数

        this.ulStyle.width = parseInt((this.itemWidth + 40) * length) + "px"; // 容器总宽度

      });

    },

    /**

    * 轮播图点击事件

    */

    handleClick(val) {

      // 触发外部事件,将点击的轮播图详情数据返回

      this.$emit("onClick", val);

    },

    /**

    * 判断轮播类型,根据类型执行对应的操作

    */

    handleType() {

      if (this.effect === "normal") {

        this.ulStyle.paddingLeft = 0; // 将起始位置左侧的padding置为0

      } else if (this.effect === "zoom") {

        this.ulStyle.paddingLeft = "20px"; // 保证左侧有一定的位移

      }

    },

    /**

    * 移动处理

    */

    handleMove() {

      let moveX = this.itemWidth * this.index;

      if (this.index === 0) {

        moveX = 0;

        this.handleType();

      } else {

        this.ulStyle.paddingLeft = 0; // 将起始位置左侧的padding置为0

        if (this.effect === "zoom") {

          moveX = moveX - 20;

        }

      }

      this.swiperStyle = {

        transform: "translateX(-" + moveX + "px)"

      };

    },

    /**

    * 循环移动处理

    */

    handleLoopMove() {

      // 当设置自动播放时,执行自动循环播放操作,否则,只执行下一次轮播效果

      if (this.options.autoplay) {

        let interval = this.options.interval ? this.options.interval : 3000;

        this.intervalTime = setInterval(() => {

          this.index++;

          if (this.index > this.list.length - 1) {

            this.index = 0; // 置为-1,下次轮播时index就会变成0,图片定位到起始位置

          }

          this.handleMove();

        }, interval);

      }

    },

    /**

    * 触摸开始事件,记录下触摸点

    */

    onTouchStart(e) {

      this.touchStart = e.changedTouches[0]; // 记录开始触摸点

      // 清除定时器

      clearInterval(this.intervalTime);

    },

    /**

    * 触摸结束事件,记录下触摸点,比较当前点和触摸开始点,判断触摸方向

    */

    onTouchEnd(e) {

      this.touchEnd = e.changedTouches[0];

      // 比较移动的点位差,正数就是右滑,负数就是左滑

      if (this.touchEnd.clientX - this.touchStart.clientX > 60) {

        this.index--;

        if (this.index <= 0) {

          this.index = 0;

        }

      } else if (this.touchEnd.clientX - this.touchStart.clientX < -60) {

        this.index++;

        if (this.index >= this.list.length - 1) {

          this.index = this.list.length - 1;

        }

      }

      // 处理当前的滑动

      this.handleMove();

      // 同时开启自动轮播

      this.handleLoopMove();

    }

  },

  watch: {

    list: function(e) {

      this.calcWidth();

    }

  },

  destroyed() {

    // 清除定时器

    if (this.autoplay) {

      clearInterval(this.intervalTime);

    }

  }

};

</script>

<style lang="less" scoped>

.swiper-component {

  overflow: hidden;

  height: 3rem;

  position: relative;

  ul {

    white-space: nowrap;

    height: 100%;

    transition: 0.5s ease;

    li {

      list-style: none;

      height: 100%;

      float: left;

      overflow: hidden;

      img {

        width: 100%;

        height: 100%;

      }

      &.zoom {

        border-radius: 0.16rem;

        transform: scale(0.93);

        transition: 0.5s ease;

        &.active {

          transform: scale(1);

        }

      }

    }

  }

  .swiper-dots {

    position: absolute;

    bottom: 0.16rem;

    left: 50%;

    transform: translateX(-50%);

    display: flex;

    .dots-item {

      width: 0.1rem;

      height: 0.1rem;

      border-radius: 50%;

      background: rgba(255, 255, 255, 0.7);

      margin: 0rem 0.04rem;

      &.active {

        background: #409eff;

      }

    }

  }

}

</style>


父组件swiperdemo.vue

<template>

  <div id="main">

    <div class="nav-header">

      <img @click="gohome" src="~@/assets/redeem-coupon/back.png" alt="" srcset="">

      <span>会员权益</span>

      <img id="nav-img-right" src="~@/assets/redeem-coupon/back.png" alt="" srcset="">

    </div>

    <div class="content">

      <div class="swiperimgs" style="width:100%;">

        <swiper @indexdata="indexdata" :vipCurrentProcess="vipCurrentProcess" height="3.4rem" :vipLevel="index" :options="swiperOption" :list="bannerList" effect="zoom" ></swiper>

      </div>

      <div class="grade-center" v-if="a.length > 0">

        <p class="title">会员权益</p>

        <div class="cart">

          <div class="cart-data"  v-for="(item,i) in a" :key="i" @click="popClick(item)">

            <img :src="item.rightsIcon" />

            <span>{{item.rightsName}}</span>

          </div>

        </div>

      </div>

      <div v-else class="grade-centert">

        <div class="grade-center-title">会员权益</div>

        <div class="grade-center-content">您目前暂无权益,继续努力哦~</div>

      </div>

      <div class="grade-footer">

        <div class="title">VIP说明</div>

        <div class="desc">{{desc}}</div>

      </div>

    </div>

    <div class="footer"></div>

    <div class="overlay">

      <van-overlay :show="show" @click="show = false">

        <div class="wrapper" @click.stop >

          <div class="block" >

            <div class="title-equity">

              <img src="@/assets/home-vip/title_left.png">

              <span>{{value.rightsName}}</span>

              <img src="@/assets/home-vip/title_right.png">

            </div>

            <div class="title-desc">

              {{value.rightsText}}

            </div>

          </div>

        </div>

        <div class="img-close" @click="closeClick">

          <img src="@/assets/home-vip/closed_pop.png" alt="" srcset="">

        </div>

      </van-overlay>

    </div>

  </div>

</template>

<script>

import Swiper from "@/components/Swiper";

export default {

  data() {

    return {



      eable: false,

      swiperOption: {

        showDots: false, // 是否显示分页器

        interval: 3000, // 轮播间隔时间,默认3s

        autoplay: false, // 是否自动播放

        loop: true // 是否循环轮播

      },

      bannerList: [

        { img: require('../../assets/home-vip/gray.png') },

        { img: require('../../assets/home-vip/img_lv.png') },

        { img: require('../../assets/home-vip/img_huangjin.png') },

        { img: require('../../assets/home-vip/img_hong.png') },

        { img: require('../../assets/home-vip/img_cheng.png') },

        { img: require('../../assets/home-vip/img_lan.png') },

        { img: require('../../assets/home-vip/img_zi.png') }

      ],

    }

  },

  components: {

    Swiper

  },

  created() {

  },

  mounted() {

  },

  methods: {

    //从子组件传递当前显示轮播图的索引

    indexdata(data) {

      this.index = data

      const flag = this.vipDetailList && this.vipDetailList.length

      this.a = flag ? this.vipDetailList[this.index].vipRightsList : []

      this.desc = flag ? this.vipDetailList[this.index].growthValueText : ''

    },



    popClick(val) {

      this.value = val

      this.show = true

    },

    closeClick() {

      this.show = false

    }

  }

}

</script>

<style lang="scss">

.block {

  border-radius: 5px;

  height: 180px;

  position: absolute;

  left: 37px;

  right: 37px;

  top: 248px;

  background-image: url(../../assets/home-vip/bg_pop_yellow.png);

  background-repeat: no-repeat;

  background-size: 100% 100%;

  > .title-equity {

    display: flex;

    align-items: center;

    justify-content: center;

    > img {

      width: 30px;

      height: 17px;

    }

    > span {

      font-size:16px;

      font-weight:bold;

      color:rgba(95,48,32,1);

      padding: 28px 10px;

    }

  }

  > .title-desc {

    padding: 0 17px;

    font-size:15px;

    color:rgba(148,98,57,1);

    line-height:22px;

  }

}

#main {

  height: 100%;

  background-color: #fafafa;

  > .nav-header {

    height: 40px;

    background-color: #fff;

    position: absolute;

    top: 0;

    left: 0;

    right: 0;

    display: flex;

    align-items: center;

    justify-content: space-between;

    padding: 0 15px;

    > span {

      font-size:19px;

      font-weight:bold;

      color:rgba(0,0,0,1);

    }

    > img {

      width: 21px;

      height: 19px;

    }

    > #nav-img-right {

      visibility: hidden;

    }

  }

  > .content {

    position: absolute;

    top: 30px;

    right: 0;

    left: 0;

    bottom: 40px;

    overflow-y: scroll;

    > .swiperimgs {

      height: 3.3rem;

      margin-top: 0.5rem;

    }

    > .grade-center {

      background-color: #fff;

      text-align: left;

      padding-top: 25px;

      > .title {

        text-align: center;

        width: 172px;

        height: 18px;

        margin: auto;

        background-image: url(../../assets/home-vip/title1.png);

        background-size: 100% 100%;

        background-repeat: no-repeat;

        font-size: 16px;

        font-weight: bold;


        margin-bottom: 20px;

      }

      > .cart {

        display: flex;

        align-items: center;

        flex-wrap: wrap;

        justify-content: space-around;

        padding-bottom: 5px;

        > .cart-data {

          // width: 25%;

          text-align: center;

          display: flex;

          align-items: center;

          flex-direction: column;

          > img {

            width: 45px;

            height: 45px;

          }

          > span {

            font-size: 13px;

            padding-top: 15px;

            padding-bottom: 20px;

          }

        }

      }

    }

    > .grade-centert {

      text-align: center;

      display: flex;

      align-items: center;

      flex-direction: column;

      background-color: #fff;

      .grade-center-title {

        width: 172px;

        height: 18px;

        background-image: url(../../assets/home-vip/title1.png);

        background-size: 100% 100%;

        background-repeat: no-repeat;

        font-size: 16px;

        font-weight: bold;

        margin-top: 25px;

        margin-bottom: 20px;

      }

      .grade-center-content {

        font-size:14px;

        color:rgba(204,204,204,1);

        margin-bottom: 24px;

      }

    }

    > .grade-footer {

      display: flex;

      align-items: center;

      flex-direction: column;

      background-color: #fff;

      margin-top: 10px;

      > .title {

        width: 172px;

        height: 18px;

        background-image: url(../../assets/home-vip/title1.png);

        background-size: 100% 100%;

        background-repeat: no-repeat;

        font-size: 16px;

        font-weight: bold;

        margin-top: 25px;

        margin-bottom: 20px;

      }

      > .desc {

        padding-left: 15px;

        padding-right: 28px;

        font-size:14px;

        color:rgba(51,51,51,1);

        text-align: left;

        padding-bottom: 25px;

      }

    }

  }

  > .footer {

    height: 10px;

    position: absolute;

    bottom: 0;

  }

  > .overlay {

    position: relative;

  }

}

.img-close {

  position: absolute;

  text-align: center;

  top: 459px;

  left: 172px;

  right: 172px;

}

.img-close img {

  width: 30px;

  height: 30px;

}

</style>

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