vue輪播元件實現,$children和$parent,附帶好用的gif錄製工具

ht_sauce發表於2019-09-26

個人感情部分(不喜歡的童鞋請忽略,純屬個人吐槽):

1、先提前預祝大家國慶節玩的愉快,我國慶要見家長去了(忐忑)

2、忍不住想要為小米正名,雖然我是米粉但是我是理智粉。

24號不是mix alpha釋出會啊,看了真滴是驚豔(現場直接有人喊“牛逼”,看過好多釋出會,就沒有看到這樣直接喊出來“牛逼”的)。不知道大家還記不記得13年那會吹蘋果的時候的一塊ppt手機(其實是媒體做的圖),但是現在小米做出來了,甚至更好。但是我最近在uc上面那真是到處黑。以前我不相信水軍說法,現在信了。如果關注小米mix alpha新聞的在uc下面經常能看到一個叫“決勝千里”的人,每次都是千篇一律的複製黏貼黑。

我想說,沒買過沒資格。就像寫程式碼,沒寫過別說懂。寫了你才知道這裡面有哪些細節和關鍵因素。

寫這個元件起因

記得就這兩天有掘友發一個沸點說找女票千萬別找同行,好了,曾經我是erp軟體實施,現在轉前端。女票也是前端。然後她發我一個連結(打不開看gif圖):http://ipark.jsboon.com/static/dashboard/yjw/yjw.html

vue輪播元件實現,$children和$parent,附帶好用的gif錄製工具

這個連結的最右側有一個輪播的效果。說起來這整個頁面是不咋的,不過裡面涉及的東西都比較複雜。附帶gif錄製工具:http://www.zdfans.com/html/1754.html

回到剛才說的,女票,對女票。不懂問我,就像之前評論的,乾脆讓我寫得了。這下好了報應來了。(說起來真是無語,她比我早幹一年,結果還不如我寫了1年前端)

說起來我還沒寫過這種輪播元件,但是之前去看過原始碼,並且瞭解過。如果是寫死的那種確實比較簡單。但是我們今天用vue封裝一個元件。

一、知識點涉及

1、vue元件化開發

2、vue元件巢狀組,就是兩個元件相互耦合,然後必須配合使用的那種。參考elementUI裡面的表單元件(分為from元件,item元件)或者輪播元件。

vue的$children和$parent

3、css動畫和形變

二、開始寫元件

1、先寫你的框,主元件

這個是容器,負責元件定位和元件的整體運作的

html部分

<div
  @mouseenter.stop="handleMouseEnter"
  @mouseleave.stop="handleMouseLeave"
  class="dht-swiper-side"
  :style="{ zIndex: zIndex }"
>
  <slot></slot>
</div>複製程式碼

主要兩個滑鼠事件:mouseenter和mouseleave

第一個就是滑鼠在元素上負責停止定時器,第二個就是滑鼠離開重啟定時器

對應的props和監聽

props: {
  // 時間間隔
  interval: {
    type: Number,
    default: 8000
  },
  //是否自動播放
  autoplay: {
    type: Boolean,
    default: true
  },
  zIndex: {
    type: Number,
    default: 2000
  },
  // x軸變化
  axisx: {
    type: Number,
    default: 1000
  }
},
watch: {
  autoplay(val) {
    val ? this.startTimer() : this.stopTimer();
  }
},
data() {
  return {
    // 計時器
    timer: "",
    //子元素
    items: [],
    // 當前顯示的元素
    active: 0
  };
},複製程式碼

看看就好,沒啥多說的,我感覺挺清晰的

2、寫你的子元件

這裡必須跳躍一下,為什麼呢?

因為:主元件主要負責動畫運作和容器的作用。定義好你要的引數之後,其實主元件你直接看程式碼是不不符合編寫邏輯的

有了主元件之後,我需要有子元素才能動起來,所以先把子元素載入進來

html部分

<div class="dht-swiper-side-item" :style="itemStyle">
  <slot></slot>
</div>複製程式碼

js核心部分

created() {
  //元素建立和需要更新父元素屬性
  this.$parent && this.$parent.updateItems();
},
beforeMount() {},
mounted() {},
destroyed() {
  //元素銷燬和需要更新父元素屬性
  this.$parent && this.$parent.updateItems();
},複製程式碼

這裡主要是建立元素的時候需要把元素加入主元件的items中,銷燬的時候同樣進行更新

主元件的更新程式碼

// 更新元素
updateItems() {
  this.items = this.$children.filter(
    // 更新元素需要確認為指定的子元素
    child => child.$options.name === "dhtSwiperSideItem"
  );
},複製程式碼

css核心部分

css部分主要是定義動畫效果,和基礎css,主要是看動畫部分

.dht-swiper-side-item {
  position: absolute;
  transition: all 1s ease;
  transform: translateX(1000px);
  // 抖動動畫
  @keyframes mymove {
    0% {
      left: 0;
    }
    50% {
      left: 15px;
    }
    100% {
      left: 0;
    }
  }
}複製程式碼

3、一般彈窗動畫之類的編寫原理講解

1、不能用display:none,因為那樣元素是直接顯示出來的,動畫是無法有的。

2、舉例:下方彈窗劃出

其實在寫這些彈窗的時候元素已經在頁面上面載入好了,只是被我們隱藏到顯示器之外了。

所以我們要做的是在點選顯示的時候把元素位移回來

3、所以其實頁面上基本的動畫都是先放在你看不到的地方,然後再通過transform

形變css給移動回來的。我這次的元件也是一樣的。

4、主元件操作

1、回顧一下,剛才我們先寫了主元件,主元件載入子元件,子元件會呼叫主元件函式,讓主元件去更新自己的items,提前存好。方便使用

2、既然我們主元件拿到了子元件了,那麼就可以直接操作子元件進行操作,其實核心原理在於主元件之間操作子元件。(我看了elementUI原始碼的走馬燈部分,寫的比我複雜。)

3、定時器部分

//開始計時器
startTimer() {
  //預先執行一次,保證不會出現第一次執行延遲雙倍實際
  this.play();
  // 攔截處理
  if (this.interval <= 0 || !this.autoplay || this.timer) return;
  this.timer = setInterval(() => {
    this.play();
  }, this.interval);
},複製程式碼

這塊其實沒啥,除了預先的攔截剩下的就是啟動定時器,然後執行動畫播放函式

4、核心播放函式部分

//播放實際執行函式
play() {
  let len = this.items.length - 1;
  let now = this.active > len ? 0 : this.active;
  let old = this.active - 1 < 0 ? 0 : this.active - 1;
  //console.log("當前", now, "老的", old);
  //關閉老元素
  this.items[old].show = false;
  this.items[old].itemStyle = {
    transition: "all 1.5s ease",
    transform: `translateX(${this.axisx}px)`
  };
  //顯示新元素
  this.items[now].show = true;
  this.items[now].itemStyle = {
    transition: "all 1.5s ease",
    transform: "translateX(0)",
    animation: "mymove 1.5s 2"
  };
  //記錄資料
  this.active = now + 1;
}複製程式碼

這個其實很簡單,每次執行的時候處理一下資料,拿到當前要執行的子元素id和老的元素,當前的展示,老的移動回去。最後記錄一下新的id

這裡有一個坑點:就是animation部分,記得執行2次,不然只是一次會導致下面的元素看不到抖動效果。原因是在移動的時候就抖動完畢了。

5、主元件css部分

.dht-swiper-side {
  position: absolute;
  z-index: 2000;
  right: 0;
  display: flex;
  flex-flow: row;
  width: 100%;
}複製程式碼

三、元件文件

dht-swiper-side側邊輪播元件intervalNumber5000時間間隔,預設5秒轉換一次必須給該元件指定寬度,否則無法正常顯示。內部子元素展示做最側位置主要由該元件的寬度定義
autoplayBooleanTRUE是否自動播放,我們不支援false
zIndexNumber2000元件層級
axisxNumber1000隱藏的子元素位置,px單位,預設1000。當內部元素寬度過大時可以調節該引數
dht-swiper-side-itemdht-swiper-sidedht-swiper-side的子元件,用於存放內容


四、個人元件效果展示

<dht-swiper-side class="main">
  <dht-swiper-side-item>
    <div class="item">我是元件1</div>
  </dht-swiper-side-item>
  <dht-swiper-side-item>
    <div class="item">我是元件2</div>
  </dht-swiper-side-item>
  <dht-swiper-side-item>
    <div class="item">我是元件3</div>
  </dht-swiper-side-item>
  <dht-swiper-side-item>
    <div class="item">我是元件4</div>
  </dht-swiper-side-item>
</dht-swiper-side>
複製程式碼
.main {
  width: 500px;
  .item {
    width: 100px;
    height: 100px;
    background: #009966;
    border: #409eff 1px solid;
    text-align: center;
    line-height: 100px;
  }
}複製程式碼

vue輪播元件實現,$children和$parent,附帶好用的gif錄製工具

主元件全部程式碼

<template>
  <div
    @mouseenter.stop="handleMouseEnter"
    @mouseleave.stop="handleMouseLeave"
    class="dht-swiper-side"
    :style="{ zIndex: zIndex }"
  >
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: "dhtSwiperSide",
  props: {
    // 時間間隔
    interval: {
      type: Number,
      default: 8000
    },
    //是否自動播放
    autoplay: {
      type: Boolean,
      default: true
    },
    zIndex: {
      type: Number,
      default: 2000
    },
    // x軸變化
    axisx: {
      type: Number,
      default: 1000
    }
  },
  watch: {
    autoplay(val) {
      val ? this.startTimer() : this.stopTimer();
    }
  },
  data() {
    return {
      // 計時器
      timer: "",
      //子元素
      items: [],
      // 當前顯示的元素
      active: 0
    };
  },
  beforeCreate() {},
  created() {
    this.$nextTick(() => {
      this.updateItems();
      this.startTimer();
      this.$children[0].show = true;
    });
  },
  beforeMount() {},
  mounted() {},
  destroyed() {
    clearInterval(this.timer);
  },
  methods: {
    handleMouseEnter() {
      this.stopTimer();
    },

    handleMouseLeave() {
      this.startTimer();
    },
    //開始計時器
    startTimer() {
      //預先執行一次,保證不會出現第一次執行延遲雙倍實際
      this.play();
      // 攔截處理
      if (this.interval <= 0 || !this.autoplay || this.timer) return;
      this.timer = setInterval(() => {
        this.play();
      }, this.interval);
    },
    // 停止計時器
    stopTimer() {
      clearInterval(this.timer);
    },
    // 更新元素
    updateItems() {
      this.items = this.$children.filter(
        // 更新元素需要確認為指定的子元素
        child => child.$options.name === "dhtSwiperSideItem"
      );
    },
    //播放實際執行函式
    play() {
      let len = this.items.length - 1;
      let now = this.active > len ? 0 : this.active;
      let old = this.active - 1 < 0 ? 0 : this.active - 1;
      //console.log("當前", now, "老的", old);
      //關閉老元素
      this.items[old].show = false;
      this.items[old].itemStyle = {
        transition: "all 1.5s ease",
        transform: `translateX(${this.axisx}px)`
      };
      //顯示新元素
      this.items[now].show = true;
      this.items[now].itemStyle = {
        transition: "all 1.5s ease",
        transform: "translateX(0)",
        animation: "mymove 1.5s 2"
      };
      //記錄資料
      this.active = now + 1;
    }
  }
};
</script>

<style lang="scss">
.dht-swiper-side {
  position: absolute;
  z-index: 2000;
  right: 0;
  display: flex;
  flex-flow: row;
  width: 100%;
}
</style>複製程式碼

子元件全部程式碼

<template>
  <div class="dht-swiper-side-item" :style="itemStyle">
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: "dhtSwiperSideItem",
  data() {
    return {
      show: false,
      defaultStyle: {},
      itemStyle: {}
    };
  },
  watch: {},
  beforeCreate() {},
  created() {
    //元素建立和需要更新父元素屬性
    this.$parent && this.$parent.updateItems();
  },
  beforeMount() {},
  mounted() {},
  destroyed() {
    //元素銷燬和需要更新父元素屬性
    this.$parent && this.$parent.updateItems();
  },
  methods: {}
};
</script>

<style lang="scss">
.dht-swiper-side-item {
  position: absolute;
  transition: all 1s ease;
  transform: translateX(1000px);
  // 抖動動畫
  @keyframes mymove {
    0% {
      left: 0;
    }
    50% {
      left: 15px;
    }
    100% {
      left: 0;
    }
  }
}
</style>複製程式碼

致謝

感謝elementUI開原始碼,本元件有部分是直接拷貝的elementUI的Carousel的程式碼。



相關文章