vue挪動端側滑面板組件

以下這段都是空話,請跳過

公司挪動端開闢平台舉行了大變革,前端架構由DCloud大生態轉換為VUE,所以挪動端的UI組件庫從MUI改成運用MintUI,然後最先雷厲風行的把MintUI組件改成MUI組件的模樣,然後發明少了幾個較為經常使用的,个中一個就是,嗯,側滑面板(也叫側滑菜單,也叫抽屜面板-andriod官方是這麼翻譯的,很抽象)。然則,它就是一個規劃組件,詳細裡邊菜單什麼的,那都是浮雲(嗯,就是愛用幾年前的盛行辭彙,而且很喜歡在網上衝浪和踩他人的空間)

以上這段都是空話,感謝瀏覽

需求

  1. 開闢一個側滑面板(相似QQ、網易郵箱等app的)
  2. 能夠在左邊,也能夠在右邊
  3. 側滑面板內容隨便定製
  4. 側滑面板相對的就有主面板,那末衍生出差別的體位和姿態

    (1)主面板滑動,側滑面板不動
    (2)側滑面板動,主面板不動
    (3)它倆一塊動,一同——–

  5. 代碼作風只管和MintUI的其他組件作風相似(這個挺主要的)

參考

mintUI組件中一樣?️滑動操縱的tabContainer,為了滿足需求5,我連函數名都抄了過來。

不說空話,上代碼吧

The Waaaaaaaay

1. 設想組件構造

這個組件分為兩部份,一部份為側滑面板容器,另一部份為主面板容器,然後詳細容器內部直接放了插槽,然後還需要一個主面板容器的遮罩,為了側滑面板翻開的時刻顯現出來。上代碼了

<div class="mint-drawer-layout">

    <!--側滑欄-->
    <div
      ref="drawer"
      class=" mint-drawer-warp"
      @touchstart.stop="startDrag"
      @touchmove.stop="onDrag"
      @touchend.stop="endDrag"
      :style="drawerStyle">
      <slot name="drawer"></slot>
    </div>

    <!--主容器-->
    <div
      ref="content"
      @touchstart.stop="startDrag"
      @touchmove.stop="onDrag"
      @touchend.stop="endDrag"
      class=" mint-content-warp"
      :style="contentStyle">
      <!--主容器遮罩(側滑翻開狀況下顯現)-->
      <div class="content-mask" v-tap="toggle" ref="contentMask"></div>
      <slot name="content"></slot>
    </div>
  </div>

2. 設置設想

這塊加了一些我們公司的一些需求,能夠列位哥哥姐姐門用不到裡邊的一些props的設想,僅供參考

props: {
      // 側滑面板的寬度(單元px)
      'drawerWidth': {
        type: Number,
        default: 200
      },
      // 是不是可用
      'enable': {
        type: Boolean,
        default: true
      },
      // 側滑菜單是不是在右邊,默以為false,在左邊
      'isRight': {
        type: Boolean,
        default: false
      },
      // 側滑菜單滑動操縱範例
      // ['fixDrawer'——牢固側滑面板,主面板滑動]
      // ['fixContent'——牢固主面板,側滑面板滑動]
      // ['noFixed'——一同滑動!]
      'swipeType': {
        type: String,
        default: 'fixDrawer'
      },
      // 點擊湧現側滑菜單的按鈕的id ( @TODO 這裏怎樣處置懲罰異步襯着的題目 )
      'btnId': {
        type: String,
        default: ''
      },
      // 狀況位,側滑面板是不是為翻開狀況
      // (由於我們公司有這類一最先就把側滑菜單翻開的shabee場景,所以這才會有這麼個東西)
      //(假如這個不願望設置的話、能夠放在data裡邊)
      'isDrawerOpened': {
        type: Boolean,
        default: false
      },
      // 是不是可滑動,假如不可滑動的話,就只能經由歷程挪用toogle要領翻開側滑面板
      // 這個也是公司的一個運用場景,就是你甭滑,找個按鈕觸發一下側滑面板翻開的要領才翻開
      //(假如這個不願望設置的話、也能夠放在data裡邊)
      'swipeable': {
        type: Boolean,
        default: true
      }
    }

2. 款式設想

不能不認可,我的css寫的shit

關於全部的組件來講,它應當是默許充溢全部父容器的,而且這個組件,我以為,平常都是用來放在最外層的一個規劃組件,所以,默許充溢窗口就好了
所以組件的最外層來一個相對規劃,然後以下:

.mint-drawer-layout {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    overflow-x: hidden;
}

然後側滑面板,只需要縱向充溢就能夠了,寬度是設置

.mint-drawer-warp {
    position: absolute;
    top: 0;
    bottom: 0;
}

然後是主面板,沒說的

.mint-content-warp {
    position: relative;
    width: 100%;
    height: 100%;
}

4. 癥結完成

終究來到了引見究竟該怎樣完成的了

滑動處置懲罰三步走,start、drag、end

起首第一步上來就要設置一個紀錄滑動操縱的狀況變量——dragging,設置為false,輕易屏障滑動時觸發的其他操縱的實行

——最先的時刻紀錄最先滑動的位置
——滑動中,dragging狀況紀錄為true,最先舉行滑動位置和手指挪動的聯動
——滑動完畢,dragging狀況紀錄為false,盤算當前的滑動位置,推斷是劃開側滑面板照樣封閉,並舉行動畫處置懲罰

个中幾個細節小談一哈
(1)擺布滑動操縱觸發的推斷:我這邊是公司的範例,橫軸挪動位移大於五,豎軸位移不大於橫軸的1.73倍就能夠
(2)末了完畢時推斷側滑面板的翻開和封閉:是如許的,我這邊取的是三分之一的側滑面板的寬度,也就是從翻開到封閉,那末像封閉的方向滑動側滑面板寬度的三分之一就能夠了,假如是封閉到翻開,往翻開的方向滑動三分之一就能夠了
(3)左邊和右邊,另有三種差別的滑動體式格局:三種差別的滑動體式格局實際上就是掌握究竟哪一個面板跟着手指動,詳細的行動歷程和面板的偏移量實際上是一樣的。擺布兩側就更簡樸了,直接是對稱的操縱便可

滑動完畢的操縱,參考的tabcontainer,也挺奇妙的,列位請上眼~

      /**
       * 滑動完畢的動畫
       */
      swipeLeaveTransition() {
        let g = this, currentMovingDoms = [];
        let {swipeType, drawerWidth} = g;

        switch (swipeType) {
          case 'fixDrawer':
            currentMovingDoms.push(g.content);
            break;
          case 'fixContent':
            currentMovingDoms.push(g.drawer);
            break;
          case 'noFixed':
            currentMovingDoms.push(g.drawer);
            currentMovingDoms.push(g.content);
            break;
          default:
            break;
        }

        currentMovingDoms.forEach((val) => {
          val.classList.add('swipe-transition');
        });

        setTimeout(() => {
          if (g.isDO) {
            this.swipeMove(drawerWidth);
          } else {
            this.swipeMove(0);
            g.contentMask.style.opacity = 0;
            g.contentMask.style.display = 'none';
          }

          g.isToggle = false;

          currentMovingDoms.forEach((val) => {
            once(val, 'webkitTransitionEnd', _ => {
              val.classList.remove('swipe-transition');
              g.swiping = false;
            });
          });


        }, 0);
      },

      /**
       * 滑動操縱
       * @param offset 滑動位置
       */
      swipeMove(offset) {
        let g = this;
        let {swipeType, isRight} = g;

        g.contentMask.style.display = 'block';
        g.contentMask.style.opacity = Math.abs(offset) / g.drawerWidth * 0.4;

        switch (swipeType) {
          case 'fixDrawer':
            g.content.style.webkitTransform = `translate3d(${(!isRight ? '' : '-') + offset}px, 0, 0)`;
            g.swiping = true;
            break;
          case 'fixContent':
            g.drawer.style.webkitTransform = `translate3d(${(!isRight ? '' : '-') + offset}px, 0, 0)`;
            g.swiping = true;
            break;
          case 'noFixed':
            g.content.style.webkitTransform = `translate3d(${(!isRight ? '' : '-') + offset}px, 0, 0)`;
            g.drawer.style.webkitTransform = `translate3d(${(!isRight ? '' : '-') + offset}px, 0, 0)`;
            g.swiping = true;
            break;
          default:
            break;
        }
      },
      
      // 最先滑動
      startDrag(evt) {
        let g = this;
        if (!g.enable || !g.swipeable) return false;

        evt = evt.changedTouches ? evt.changedTouches[0] : evt;

        g.start.x = evt.pageX;
        g.start.y = evt.pageY;
      },

      // 滑動中
      onDrag(evt) {
        let g = this, swiping;
        if (!g.enable || !g.swipeable) return false;

        g.dragging = true;

        const e = evt.changedTouches ? evt.changedTouches[0] : evt;
        const offsetTop = e.pageY - g.start.y;
        const offsetLeft = e.pageX - g.start.x;
        const y = Math.abs(offsetTop);
        const x = Math.abs(offsetLeft);

        swiping = !(x < 5 || (x >= 5 && y >= x * 1.73));
        if (!swiping) return;
        evt.preventDefault();

        let offset;

        if (g.isDO) {
          offset = g.isRight ? (g.drawerWidth - offsetLeft) : (g.drawerWidth - (-offsetLeft));
        } else {
          offset = g.isRight ? -offsetLeft : offsetLeft;
        }

        if (offset < 0 || offset > g.drawerWidth) {
          g.swiping = false;
          return;
        }
        g.offset = offset;
        g.swipeMove(offset);
      },

      // 完畢滑動
      endDrag() {
        let g = this;

        if (!g.enable || g.isToggle || !g.dragging) {
          return false;
        }

        const tempWidth = g.drawerWidth / 3;

        if (g.isDO && g.offset < tempWidth * 2) {
          g.isDO = false;
        } else if (!g.isDO && g.offset > tempWidth) {
          g.isDO = true;
        }
        g.dragging = false;

        g.swipeLeaveTransition();
      }

好啦,到這應當就差不多了。。。
裡邊涉及到的v-tap指令是自定義的指令,為了處置懲罰挪動端的點擊操縱,我還整理了一片陋文:https://segmentfault.com/a/11… (挪動點擊長按滑動vue指令)

然後這個組件的源碼我放在了我fork出來的mintUI項目上
https://github.com/LylaYuKako…

感謝列位品味,

    原文作者:yurt
    原文地址: https://segmentfault.com/a/1190000014399086
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞