<input id="0qass"><u id="0qass"></u></input>
  • <input id="0qass"><u id="0qass"></u></input>
  • <menu id="0qass"><u id="0qass"></u></menu>

    使用vue3+vite+typescript編寫一個網易云首頁輪播插件

    vue3+vite已經成熟。就想著哪來練手項目,然后寫到頁面輪播部分時突然奇想,不如使用vue3編寫一個輪播組件練練手,于是網上找到了輪播圖的編寫思路,同時參考element-plus庫源碼的編寫思路,最后用render函數的形式完成了這個組件,今天分享給大家

    效果圖如下

    在這里插入圖片描述

    具體實現過程

    創建項目的過程忽略了,想要了解的稍后我放出git地址

    編寫Swiper組件

    <!--
     * @Description: 輪播組件
     * @Autor: ZmSama
     * @Date: 2021-05-28 15:00:37
    -->
    <script lang="ts">
    import { defineComponent, h, getCurrentInstance, onMounted, ref, VNode } from 'vue';
    import Slider from './Slider.vue';
    import throttle from '../utils/throttle';
    export interface ISlider {
      sty?: Object;
      className?: string;
    }
    export default defineComponent({
      name: 'Swiper',
      components: {
        Slider,
      },
      props: {
        initial: {
          type: Number,
          default: 0,
        },
        interval: {
          type: Number,
          default: 5000,
        },
        auto: {
          type: Boolean,
          default: true,
        },
      },
      setup(props, ctx) {
        // 因為setup中沒有this,使用方法得到實例
        const instance = getCurrentInstance();
    
        // 輪播子內容
        const childrenNode = ref([]);
        // 初始index
        const initial = ref(props.initial);
        // 間隔時間
        const interval = ref(props.interval);
        // 定時器對象
        let timer = null;
    
        // 自動輪播
        const autoPlay = () => {
          timer = setInterval(() => {
            initial.value++;
            if (initial.value >= childrenNode.value.length) {
              initial.value = 0;
            }
          }, interval.value);
        };
    
        // 上一頁(節流一下)
        const preSlider = throttle(() => {
          initial.value--;
          if (initial.value < 0) {
            initial.value = childrenNode.value.length - 1;
          }
        });
    
        // 下一頁(節流一下)
        const nextSlider = throttle(() => {
          initial.value++;
          if (initial.value >= childrenNode.value.length) {
            initial.value = 0;
          }
        });
    
        // 點擊指示器前往某一頁
        const gotoSlider = (i: number) => {
          initial.value = i;
        };
    
        // 鼠標進入
        const enterSlider = () => {
          clearInterval(timer);
        };
    
        // 鼠標離開
        const leaveSlider = () => {
          props.auto && autoPlay();
        };
    
        // 處理每一項的樣式
        const computedSty = (index: number, arr: Array<ISlider>) => {
          // 確保索引合法
          let len = arr.length;
          // 判斷初始值
          index < 0 ? 0 : index >= len ? len - 1 : initial;
          // 第一項
          let temp1 = index - 1;
          // 第二項(中間項)
          let temp2 = index;
          // 第三項
          let temp3 = index + 1;
    
          // 判斷起始值處于哪里
          temp1 < 0 && (temp1 = len + temp1);
          temp3 >= len && (temp3 = temp3 - len);
    
          // 修改每一項的樣式
    
          return arr.map((item: ISlider, index) => {
            // 初始樣式
            let transform = 'translate(-50%, -50%) scale(0.55)',
              zIndex = 0,
              className = '';
    
            // 根據索引確定項目的樣式
            switch (index) {
              case temp2:
                zIndex = 1;
                transform = 'translate(-50%, -50%) scale(1)';
                className = ' is-active';
                break;
              case temp1:
                zIndex = 0;
                transform = 'translate(-100%, -50%) scale(0.85)';
                className = '';
                break;
              case temp3:
                zIndex = 0;
                transform = 'translate(-0%, -50%) scale(0.85)';
                className = '';
                break;
            }
            // 給每一項綁定樣式
            item.sty = {
              transform,
              zIndex,
            };
            //  加類
            item.className = className;
    
            return item;
          });
        };
    
        onMounted(() => {
          // 這里就是要插入外部傳入內容的地方,instance.slots.default()返回的是一個數組,
          // 數組內就是所有未定義名字的插槽內容,
          // 如果想在render中獲得相同的內容則調用this.$solts. default()
          const arr = instance.slots.default();
          // 從上面取出所有的實際子節點,同時處理v-for循環和直接寫組件的形式,
          // 還要防止用戶使用非slider組件(使用v-for得到的將是數組、直接寫就是對象)
          const Collection = Array.from(arr).map((item: VNode) => {
            // 說明是v-for指令的
            if (typeof item.type == 'symbol') {
              return item.children;
            } else if (item.type['name'] == 'Slider') {
              return item;
            } else {
              throw new Error('swiper組件內部只允許使用<slider></slider>組件');
            }
          });
          // 將上面得到的可能是二維數組打散成一維數組既是所有的實際子節點
          childrenNode.value = Collection.flat().map((item: VNode) => item.children);
    
          props.auto && autoPlay();
        });
        return {
          initial,
          computedSty,
          childrenNode,
          preSlider,
          nextSlider,
          gotoSlider,
          enterSlider,
          leaveSlider,
        };
      },
      render() {
        const {
          initial,
          computedSty,
          childrenNode,
          preSlider,
          nextSlider,
          gotoSlider,
          enterSlider,
          leaveSlider,
        } = this;
        // 輪播主體內容
    
        const sliderList = computedSty(initial, childrenNode);
        //  渲染輪播內容
        const slider = h(
          'div',
          {
            class: 'slider-content',
          },
          [
            sliderList.map((item, index) => {
              return h(
                'div',
                {
                  class: {
                    slider: true,
                    'is-active': index === initial,
                  },
                  style: item.sty,
                  onClick: () => gotoSlider(index),
                  onMouseenter: () => enterSlider(),
                  onMouseleave: () => leaveSlider(),
                },
                item.default()
              );
            }),
          ]
        );
    
        // // 底部點點
        let dotItem = childrenNode.map((item, index) => {
          return h('div', {
            class: {
              dot: true,
              'is-active': index == initial,
            },
            onClick: () => throttle(gotoSlider(index)),
          });
        });
        const dot = h(
          'div',
          {
            class: 'dot-wrap',
          },
          [dotItem]
        );
    
        // // 左右按鈕
        const arrowLeft = h('div', {
          class: 'arrow-left',
          onClick: () => preSlider(),
          onMouseenter: () => enterSlider(),
          onMouseleave: () => leaveSlider(),
        });
    
        const arrowRight = h('div', {
          class: 'arrow-right',
          onClick: () => nextSlider(),
          onMouseenter: () => enterSlider(),
          onMouseleave: () => leaveSlider(),
        });
    
        //組合
        return h(
          'div',
          {
            class: 'slider-container',
          },
          [slider, dot, arrowLeft, arrowRight]
        );
      },
    });
    </script>
    <style lang="scss" scoped></style>
    
    

    接著寫Slider組件

    <!--
     * @Description: 
     * @Autor: ZmSama
     * @Date: 2021-05-28 15:00:52
    -->
    <template>
      <div>
        <slot>Slider</slot>
      </div>
    </template>
    
    <script lang="ts">
    import { defineComponent } from 'vue';
    
    export default defineComponent({
      name: 'Slider',
    });
    </script>
    <style lang="scss" scoped></style>
    
    

    接下來是樣式

    // 系統顏色
    $red: rgba(255, 0, 0, 0.816);
    
    // 垂直和水平居中
    @mixin jcc-aic {
      display: flex;
      justify-content: center;
      align-items: center;
    }
    // 水平居中
    @mixin jcc {
      display: flex;
      justify-content: center;
    }
    // 垂直居中
    @mixin aic {
      display: flex;
      align-items: center;
    }
    
    // 橫向排列,從頭開始
    @mixin jcs-row {
      display: flex;
      justify-content: flex-start;
      flex-direction: row;
    }
    // 橫向排列,從頭開始,垂直居中
    @mixin jcc-aic-row {
      display: flex;
      justify-content: flex-start;
      align-items: center;
      flex-direction: row;
    }
    // 輪播樣式
    .slider-container {
      height: 100%;
      width: 100%;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      position: absolute;
      .slider-content {
        position: relative;
        box-sizing: border-box;
        height: 100%;
        width: 100%;
        .slider {
          position: absolute;
          width: 55%;
          height: 82%;
          background-color: aqua;
          top: 50%;
          left: 50%;
          transform: translate(-50%, -50%);
          transition: all 0.3s ease-in-out;
          cursor: pointer;
          border-radius: 14px;
          overflow: hidden;
          box-shadow: 3px 3px 3px 1px rgba(0, 0, 0, 0.2);
          @include jcc-aic;
          img {
            width: 100%;
            height: 100%;
          }
        }
      }
      .dot-wrap {
        position: absolute;
        bottom: 0;
        left: 50%;
        transform: translate(-50%, -50%);
        z-index: 2;
        @include jcc-aic-row;
        .dot {
          width: 10px;
          height: 10px;
          border-radius: 50%;
          background-color: #ccc;
          margin-left: 10px;
          transition: all 0.3s ease-in-out;
          cursor: pointer;
        }
        .is-active {
          background-color: $red;
        }
      }
    
      .arrow-left {
        position: absolute;
        left: 10px;
        top: 50%;
        margin-top: -15px;
        cursor: pointer;
        border: solid #ffff;
        border-width: 0 3px 3px 0;
        display: inline-block;
        transform: rotate(135deg);
        padding: 10px;
        opacity: 0;
      }
      .arrow-right {
        position: absolute;
        right: 10px;
        top: 50%;
        margin-top: -15px;
        cursor: pointer;
        border: solid #ffff;
        border-width: 0 3px 3px 0;
        display: inline-block;
        transform: rotate(-45deg);
        padding: 10px;
        opacity: 0;
      }
      &:hover .arrow-left {
        opacity: 1;
      }
      &:hover .arrow-right {
        opacity: 1;
      }
    }
    

    最后是具體使用(這些圖片是從網易云的官網復制下來的,可能有天就失效了,自己找圖片換上去

    <!--
     * @Description: 
     * @Autor: ZmSama
     * @Date: 2021-05-28 14:40:41
    -->
    <template>
      <div class="app">
        <div class="swiper-container">
          <swiper :initial="2" :interval="3000" :auto="true">
            <slider name="slider2" v-for="item in source" :key="item.id">
              <img :src="item.pic" alt="" srcset="" />
            </slider>
          </swiper>
        </div>
      </div>
    </template>
    
    <script lang="ts">
    import { defineComponent } from 'vue';
    export default defineComponent({
      name: 'App',
      setup() {
        const source = [
          {
            id: 1,
            label: 'Slider1',
            color: '#ff3399',
            pic: 'http://p1.music.126.net/O5hmcHHdJpABcArdHOXXZw==/109951166006223055.jpg?imageView&quality=89',
          },
          {
            id: 2,
            label: 'Slider2',
            color: '#99ffff',
            pic: 'http://p1.music.126.net/uhxClMS2xY2b48a-PsoG9g==/109951166006290269.jpg?imageView&quality=89',
          },
          {
            id: 3,
            label: 'Slider3',
            color: '#66cc00',
            pic: 'http://p1.music.126.net/Rgqg8zqkKiewTUMjh0GMCg==/109951166005126541.jpg?imageView&quality=89',
          },
          {
            id: 4,
            label: 'Slider4',
            color: '#0066ff',
            pic: 'http://p1.music.126.net/oMXHYkRHy5XbE0905ljqZg==/109951166005813356.jpg?imageView&quality=89',
          },
          {
            id: 5,
            label: 'Slider5',
            color: '#00ff33',
            pic: 'http://p1.music.126.net/IXXt2TECvZIeEVNUr1E_gA==/109951166004785770.jpg?imageView&quality=89',
          },
        ];
        return {
          source,
        };
      },
    });
    </script>
    
    <style lang="scss" scoped>
    .app {
      width: 100%;
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
      .swiper-container {
        width: 80%;
        height: 300px;
        position: relative;
      }
    }
    </style>
    
    

    以上就是全部的源代碼了

    懶得自己寫的朋友可以訪問我的git地址。如果能點個start就更好了項目地址

    相關推薦
    ??2020 CSDN 皮膚主題: 書香水墨 設計師:CSDN官方博客 返回首頁
    多乐彩