陪你寫一個Vue全局拖拽組件

網站上這個代碼排版我真的是醉了,附上github地址,直接跑起來看效果吧~vue

前言

大佬們,都是手摸手帶大家學習,而我就摸不了大家的小手了。由於可能我分享的內容,是一些你們早已知悉的內容,只不過這個需求,挺有意思的,我實現完以後。寫這個文章爲了分享一些能幫助到別人的內容,也是爲了歸檔一下本身的知識點。有寫的很差的地方望指正。

需求

你們在瀏覽電商的 app 的時候,大多數在詳情頁的右下角上方,會有一個懸浮的按鈕。用來分享商品詳情、或者回到首頁等功能的實現。咱們的項目呢是,基於小程序的一款金融平臺,應用了小程序原聲+webview 的方案。由於 webview 裏面路由跳轉是 vue 的跳轉,小程序並無 navigateto,在不一樣系統,機型上,小程序的 webview 實現方式有所不一樣,並且頁面層級增長以後。就會致使用戶回退的時候,可能會點不少次才能回到小程序的原生頁面。因此咱們也採用了這一方案。

實現

組件好多的內容,不少大神都有文章來寫的用法概念什麼,我就不一一贅述了。首先這種組件在咱們項目裏面是 webview 裏面全部頁面都要用的,確定得是一個全局組件,若是一個個引入那真滴是太 low 了。我就直接給你們上代碼了。

vue 組件

<template>  <div class="yht-float-btn pos-r" v-show="showBtn" :style="{'width':itemWidth+'px','height':itemHeight+'px','left':left+'px','top':top-70+'px'}"    ref="floatDiv">    <div class="top-icon pos-a">      <transition name="slide-fade">        <div class="float-box text-center hide-box" v-show="show" @click="handleClickItem(0)">          <!-- 問題 -->          <img src="@/assets/img/yht/icon_question.png" alt="">        </div>      </transition>      <transition name="slide-fade">        <div class="float-box text-center hide-box" v-show="show" @click="handleClickItem(1)">          <img src="@/assets/img/yht/icon_home.png" alt="">          <!-- 首頁 -->        </div>      </transition>    </div>    <div class="float-box text-center pos-a bottom-icon" @click="handleClickMenu">      <div>        <img v-if="show" src="@/assets/img/yht/icon_close.png" alt="">        <img v-else src="@/assets/img/yht/icon_shortcut.png" alt="">      </div>    </div>  </div></template><script>import Util from "@/utils/common";import router from "../../router";export default {  name: "drag",  components: {},  props: {    text: {      type: String,      default: "默認文字"    },    itemWidth: {      type: Number,      default: 66    },    itemHeight: {      type: Number,      default: 66    },    gapWidth: {      type: Number,      default: 10    },    coefficientHeight: {      type: Number,      default: 0.8    },    showBtn: {      // 是否顯示此toast      default: false    }  },  data() {    return {      timer: null,      currentTop: 0,      clientWidth: 0,      clientHeight: 0,      left: 0,      top: 0,      show: false    };  },  created() {    this.clientWidth = document.documentElement.clientWidth;    this.clientHeight = document.documentElement.clientHeight;    this.left = this.clientWidth - this.itemWidth - this.gapWidth;    this.top = this.clientHeight * this.coefficientHeight;  },  mounted() {    window.addEventListener("scroll", this.handleScrollStart);    this.$nextTick(() => {      const div = this.$refs.floatDiv;      div.addEventListener("touchstart", e => {        div.style.transition = "none";      });      div.addEventListener("touchmove", e => {        e.preventDefault();        if (e.targetTouches.length === 1) {          let touch = event.targetTouches[0];          this.left = touch.clientX - this.itemWidth / 2;          this.top = touch.clientY - this.itemHeight / 2;        }      });      div.addEventListener("touchend", e => {        div.style.transition = "all 0.3s";        if (this.left > this.clientWidth / 2) {          // 浮動靠右邊          this.left = this.clientWidth - this.itemWidth - this.gapWidth;          console.log("這裏是右邊");        } else {          // 靠左邊          this.left = this.gapWidth;          console.log("這裏是左邊");        }        if (this.top < 0) {          // 防止滑到上方被隱藏          this.top = this.clientHeight * this.coefficientHeight;        }      });    });  },  beforeDestroy() {    window.removeEventListener("scroll", this.handleScrollStart);  },  methods: {    handleScrollStart() {      this.timer && clearTimeout(this.timer);      this.timer = setTimeout(() => {        this.handleScrollEnd();      }, 300);      this.currentTop =        document.documentElement.scrollTop || document.body.scrollTop;      if (this.left > this.clientWidth / 2) {        this.left = this.clientWidth - this.itemWidth / 2;      } else {        this.left = -this.itemWidth / 2;      }    },    handleScrollEnd() {      let scrollTop =        document.documentElement.scrollTop || document.body.scrollTop;      if (scrollTop === this.currentTop) {        if (this.left > this.clientWidth / 2) {          this.left = this.clientWidth - this.itemWidth - this.gapWidth;        } else {          this.left = this.gapWidth;        }        clearTimeout(this.timer);      }    },    handleClickMenu() {      this.show = !this.show;    },    handleClickItem(type) {      if (type === 0) {        router.push({ path: "/question/question" });      } else {        if (window.sessionStorage.getItem("isMiniEnvironment") !== "0") {          Util.toMiniIndex(); // 這裏是回到小程序首頁        } else {          router.replace({ path: "/yht/index" });        }      }    }  }};</script><style lang="less" scoped>.yht-float-btn {  z-index: 99999;  transition: all 0.3s;  position: fixed;  bottom: 20vw;  img {    width: 66px;    height: 66px;    object-fit: contain;    margin-bottom: 3px;  }  p {    font-size: 7px;  }}.float-box {  width: 66px;  height: 66px;}.slide-fade-enter-active {  transition: all 1s ease;}.slide-fade-leave-active {  transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);}.slide-fade-enter, .slide-fade-leave-to/* .slide-fade-leave-active for below version 2.1.8 */ {  transform: translateY(10px);  opacity: 0;}.top-icon {  left: 0;  bottom: 66px;}.bottom-icon {  bottom: 0;  left: 0;}</style>複製代碼


這個組件寫好以後 能夠先 引入到一個頁面裏面調一下樣式。

實現動態加載組件

import Float from './floatBtn.vue';let dom;const FloatBtn = {}; // 定義插件對象FloatBtn.install = function(Vue, options) {  // vue的install方法,用於定義vue插件  // 若是toast還在,則再也不執行  if (document.getElementsByClassName('yht-float-btn').length) {    return;  }  let Instance = Vue.extend(Float);  const initInstance = () => {    // 實例化vue實例    dom = new Instance();    dom.vm = dom.$mount();    let boxEl = dom.$mount().$el;    document.body.appendChild(boxEl);  };  Vue.prototype.$floatBtn = {    showFloat(options) {      if (!dom) {        initInstance();      }      dom.vm.showBtn = true; // 顯示toast    },    hideFloat(options = 0) {      if (!dom) {        initInstance();      }      dom.vm.showBtn = false; // 顯示總體    }  };};export default FloatBtn;複製代碼

在全局中的使用

需求是,當用戶進入頁面棧兩層以後纔給用戶顯示這個浮動組件。 因此在路由守衛中先定義了一個計數器,在路由跳轉的時候計數器增長。其實原本是想在路由守衛中來調用這個浮動組件的,可是筆者在路由守衛中,嘗試調用組件的時候,發現訪問不到這個組件。由於當時需求比較着急,就換了一種方式。也但願有大佬能指點一下這塊的知識點~

let count = 2; // 懸浮按鈕計數器count++;window.sessionStorage.setItem('floatCount', count);// 而後在router-view頁面裏this.floatCount = this.$storage.get('floatCount'); // 獲取懸浮按鈕計數器if (this.floatCount > 3) {  this.$floatBtn.showFloat();}// 固然若是沒有特別的需求的話,這個組件就能夠在任意一個vue頁面裏面去調用  this.$floatBtn.showFloat() 這個方法。 
// 還要在mainjs裏面import FloatBtn from '@/components/floatBtn/floatBtn';Vue.use(FloatBtn); // 增長 懸浮按鈕
複製代碼

效果以下圖:


到這裏就陪你寫完了一個vue全局的組件,獎勵本身一朵小發發~
相關文章
相關標籤/搜索