最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue 指令之氣泡提示效果的實現代碼

 更新時間:2018年10月18日 09:30:00   作者:L6zt  
這篇文章主要介紹了vue 指令之氣泡提示效果的實現代碼,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下

菜鳥學習之路

//L6zt github

自己 在造組件輪子,也就是瞎搞。

自己寫了個slider組件,想加個氣泡提示。為了復用和省事特此寫了個指令來解決。

預覽地址

項目地址 github 我叫給它胡博

效果圖片

我對指令的理解: 前不久看過 一部分vnode實現源碼,奈何資質有限...看不懂。

vnode的生命周期-----> 生成前、生成后、生成真正dom、更新 vnode、更新dom 、 銷毀。

而Vue的指令則是依賴于vnode 的生命周期, 無非也是有以上類似的鉤子。

代碼效果

指令掛A元素上,默認生成一個氣泡容器B插入到 body 里面,B 會獲取 元素 A 的位置信息 和 自己的
大小信息,經過 一些列的運算,B 元素會定位到 A 的 中間 上 位置。 當鼠標放到 A 上 B 就會顯示出來,離開就會消失。

以下代碼

氣泡指令

import { on , off , once, contains, elemOffset, position, addClass, removeClass } from '../utils/dom';
import Vue from 'vue'
const global = window;
const doc = global.document;
const top = 15;
export default {
 name : 'jc-tips' ,
 bind (el , binding , vnode) {
  // 確定el 已經在頁面里 為了獲取el 位置信信 
  Vue.nextTick(() => {
   const { context } = vnode;
   const { expression } = binding;
   // 氣泡元素根結點
   const fWarpElm = doc.createElement('div');
   // handleFn 氣泡里的子元素(自定義)
   const handleFn = binding.expression && context[expression] || (() => '');
   const createElm = handleFn();
   fWarpElm.className = 'hide jc-tips-warp';
   fWarpElm.appendChild(createElm);
   doc.body.appendChild(fWarpElm);
   // 給el 綁定元素待其他操作用
   el._tipElm = fWarpElm;
   el._createElm = createElm;
   // 鼠標放上去的 回調函數
   el._tip_hover_fn = function(e) {
    // 刪除節(jié)點函數
     removeClass(fWarpElm, 'hide');
     fWarpElm.style.opacity = 0;
     // 不加延遲 fWarpElm的大小信息 (元素大小是 0 0)---> 刪除 class 不是立即渲染
     setTimeout(() => {
      const offset = elemOffset(fWarpElm);
      const location = position(el);
      fWarpElm.style.cssText = `left: ${location.left - offset.width / 2}px; top: ${location.top - top - offset.height}px;`;
      fWarpElm.style.opacity = 1;
     }, 16);
   };
   //鼠標離開 元素 隱藏 氣泡
   const handleLeave = function (e) {
    fWarpElm.style.opacity = 0;
    // transitionEnd 不太好應該加入兼容
    once({
     el,
     type: 'transitionEnd',
     fn: function() {
      console.log('hide');
      addClass(fWarpElm, 'hide');
     }
    })
   };
   el._tip_leave_fn = handleLeave;
   // 解決 slider 移動結束 提示不消失
   el._tip_mouse_up_fn = function (e) {
    const target = e.target;
    console.log(target);
    if (!contains(fWarpElm, target) && el !== target) {
     handleLeave(e)
    }
   };
   on({
    el,
    type: 'mouseenter',
    fn: el._tip_hover_fn
   });
   on({
    el,
    type: 'mouseleave',
    fn: el._tip_leave_fn
   });
   on({
    el: doc.body,
    type: 'mouseup',
    fn: el._tip_mouse_up_fn
   })
  });
 } ,
 // 氣泡的數據變化 依賴于 context[expression] 返回的值
 componentUpdated(el , binding , vnode) {
  const { context } = vnode;
  const { expression } = binding;
  const handleFn = expression && context[expression] || (() => '');
  Vue.nextTick( () => {
   const createNode = handleFn();
   const fWarpElm = el._tipElm;
   if (fWarpElm) {
    fWarpElm.replaceChild(createNode, el._createElm);
    el._createElm = createNode;
    const offset = elemOffset(fWarpElm);
    const location = position(el);
    fWarpElm.style.cssText = `left: ${location.left - offset.width / 2}px; top: ${location.top - top - offset.height}px;`;
   }
  })
 },
 // 刪除 事件
 unbind (el , bind , vnode) {
  off({
   el: dov.body,
   type: 'mouseup',
   fn: el._tip_mouse_up_fn
  });
  el = null;
 }
}

slider 組件

<template>
  <div class="jc-slider-cmp">
    <section
        class="slider-active-bg"
        :style="{width: `${left}px`}"
      >
    </section>
      <i
          class="jc-slider-dot"
          :style="{left: `${left}px`}"
          ref="dot"
          @mousedown="moveStart"
          v-jc-tips="createNode"
      >
      </i>
  </div>
</template>
<script>
 import {elemOffset, on, off, once} from "../../utils/dom";
 const global = window;
 const doc = global.document;
 export default {
  props: {
   step: {
    type: [Number],
    default: 0
   },
   rangeEnd: {
    type: [Number],
    required: true
   },
   value: {
    type: [Number],
    required: true
   },
   minValue: {
    type: [Number],
    required: true
   },
   maxValue: {
    type: [Number],
    required: true
   }
  },
  data () {
   return {
    startX: null,
    width: null,
    curValue: 0,
    curStep: 0,
    left: 0,
    tempLeft: 0
   }
  },
  computed: {
   wTov () {
    let step = this.step;
    let width = this.width;
    let rangeEnd = this.rangeEnd;
    if (width) {
     if (step) {
      return width / (rangeEnd / step)
     }
     return width / rangeEnd
    }
    return null
   },
   postValue () {
    let value = null;
    if (this.step) {
     value = this.step * this.curStep;
    } else {
     value = this.left / this.wTov;
    }
    return value;
   }
  },
  watch: {
    value: {
     handler (value) {
      this.$nextTick(() => {
       let step = this.step;
       let wTov = this.wTov;
       if (step) {
        this.left = value / step * wTov;
       } else {
        this.left = value * wTov;
       }
      })
     },
     immediate: true
    }
  },
  methods: {
   moveStart (e) {
    e.preventDefault();
    const body = window.document.body;
    const _this = this;
    this.startX = e.pageX;
    this.tempLeft = this.left;
    on({
     el: body,
     type: 'mousemove',
     fn: this.moving
    });
    once({
     el: body,
     type: 'mouseup',
     fn: function() {
      console.log('end');
      _this.$emit('input', _this.postValue);
      off({
       el: body,
       type: 'mousemove',
       fn: _this.moving
      })
     }
    })
   },
   moving(e) {
    let curX = e.pageX;
    let step = this.step;
    let rangeEnd = this.rangeEnd;
    let width = this.width;
    let tempLeft = this.tempLeft;
    let startX = this.startX;
    let wTov = this.wTov;
    if (step !== 0) {
     let all = parseInt(rangeEnd / step);
     let curStep = (tempLeft + curX - startX) / wTov;
     curStep > all && (curStep = all);
     curStep < 0 && (curStep = 0);
     curStep = Math.round(curStep);
     this.curStep = curStep;
     this.left = curStep * wTov;
    } else {
     let left = tempLeft + curX - startX;
     left < 0 && (left = 0);
     left > width && (left = width);
     this.left = left;
    }
   },
   createNode () {
    const fElem = document.createElement('i');
    const textNode = document.createTextNode(this.postValue.toFixed(2));
    fElem.appendChild(textNode);
    return fElem;
   }
  },
  mounted () {
   this.width = elemOffset(this.$el).width;
  }
 };
</script>
<style lang="scss">
  .jc-slider-cmp {
    position: relative;
    display: inline-block;
    width: 100%;
    border-radius: 4px;
    height: 8px;
    background: #ccc;
    .jc-slider-dot {
      position: absolute;
      display: inline-block;
      width: 15px;
      height: 15px;
      border-radius: 50%;
      left: 0;
      top: 50%;
      transform: translate(-50%, -50%);
      background: #333;
      cursor: pointer;
    }
    .slider-active-bg {
      position: relative;
      height: 100%;
      border-radius: 4px;
      background: red;
    }
  }
</style>
../utils/dom
const global = window;
export const on = ({el, type, fn}) => {
     if (typeof global) {
       if (global.addEventListener) {
         el.addEventListener(type, fn, false)
      } else {
         el.attachEvent(`on${type}`, fn)
      }
     }
  };
  export const off = ({el, type, fn}) => {
    if (typeof global) {
      if (global.removeEventListener) {
        el.removeEventListener(type, fn)
      } else {
        el.detachEvent(`on${type}`, fn)
      }
    }
  };
  export const once = ({el, type, fn}) => {
    const hyFn = (event) => {
      try {
        fn(event)
      }
       finally {
        off({el, type, fn: hyFn})
      }
    }
    on({el, type, fn: hyFn})
  };
  // 最后一個
  export const fbTwice = ({fn, time = 300}) => {
    let [cTime, k] = [null, null]
    // 獲取當前時間
    const getTime = () => new Date().getTime()
    // 混合函數
    const hyFn = () => {
      const ags = argments
      return () => {
        clearTimeout(k)
        k = cTime = null
        fn(...ags)
      }
    };
    return () => {
      if (cTime == null) {
        k = setTimeout(hyFn(...arguments), time)
        cTime = getTime()
      } else {
        if ( getTime() - cTime < 0) {
          // 清除之前的函數堆 ---- 重新記錄
          clearTimeout(k)
          k = null
          cTime = getTime()
          k = setTimeout(hyFn(...arguments), time)
        }
      }}
  };
  export const contains = function(parentNode, childNode) {
    if (parentNode.contains) {
      return parentNode !== childNode && parentNode.contains(childNode)
    } else {
      // https://developer.mozilla.org/zh-CN/docs/Web/API/Node/compareDocumentPosition
      return (parentNode.compareDocumentPosition(childNode) === 16)
    }
  };
  export const addClass = function (el, className) {
    if (typeof el !== "object") {
      return null
    }
    let classList = el['className']
    classList = classList === '' ? [] : classList.split(/\s+/)
    if (classList.indexOf(className) === -1) {
      classList.push(className)
      el.className = classList.join(' ')
    }
  };
  export const removeClass = function (el, className) {
    let classList = el['className']
    classList = classList === '' ? [] : classList.split(/\s+/)
    classList = classList.filter(item => {
      return item !== className
    })
    el.className =   classList.join(' ')
  };
  export const delay = ({fn, time}) => {
    let oT = null
    let k = null
    return () => {
      // 當前時間
      let cT = new Date().getTime()
      const fixFn = () => {
        k = oT = null
        fn()
      }
      if (k === null) {
        oT = cT
        k = setTimeout(fixFn, time)
        return
      }
      if (cT - oT < time) {
        oT = cT
        clearTimeout(k)
        k = setTimeout(fixFn, time)
      }
    }
  };
  export const position = (son, parent = global.document.body) => {
    let top = 0;
    let left = 0;
    let offsetParent = son;
    while (offsetParent !== parent) {
      let dx = offsetParent.offsetLeft;
      let dy = offsetParent.offsetTop;
      let old = offsetParent;
      if (dx === null) {
        return {
          flag: false
        }
      }
      left += dx;
      top += dy;
   offsetParent = offsetParent.offsetParent;
      if (offsetParent === null && old !== global.document.body) {
        return {
          flag: false
        }
      }
    }
    return {
      flag: true,
      top,
      left
    }
  };
  export const getElem = (filter) => {
    return Array.from(global.document.querySelectorAll(filter));
  };
  export const elemOffset = (elem) => {
    return {
      width: elem.offsetWidth,
      height: elem.offsetHeight
    }
  };

總結

以上所述是小編給大家介紹的vue 指令之氣泡提示效果的實現代碼,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網站的支持!

相關文章

  • vue-router動態(tài)設置頁面title的實例講解

    vue-router動態(tài)設置頁面title的實例講解

    今天小編就為大家分享一篇vue-router動態(tài)設置頁面title的實例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • Vue項目配置跨域訪問和代理proxy設置方式

    Vue項目配置跨域訪問和代理proxy設置方式

    這篇文章主要介紹了Vue項目配置跨域訪問和代理proxy設置方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • Vuex之理解Mutations的用法實例

    Vuex之理解Mutations的用法實例

    本篇文章主要介紹了Vuex之理解Mutations的用法實例,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • 詳解element-ui動態(tài)限定的日期范圍選擇器代碼片段

    詳解element-ui動態(tài)限定的日期范圍選擇器代碼片段

    這篇文章主要介紹了element-ui動態(tài)限定的日期范圍選擇器代碼片段,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-07-07
  • Vue實現圖片預覽效果實例(放大、縮小、拖拽)

    Vue實現圖片預覽效果實例(放大、縮小、拖拽)

    現在項目中有這樣的一個需求,對上傳的圖片可以點擊之后在線預覽,這篇文章主要給大家介紹了關于Vue實現圖片預覽效果實例(放大、縮小、拖拽)的相關資料,需要的朋友可以參考下
    2021-05-05
  • 詳解vue3+element-plus實現動態(tài)菜單和動態(tài)路由動態(tài)按鈕(前后端分離)

    詳解vue3+element-plus實現動態(tài)菜單和動態(tài)路由動態(tài)按鈕(前后端分離)

    本文需要使用axios,路由,pinia,安裝element-plus,并且本文vue3是基于js而非ts的,這些環(huán)境如何搭建不做描述,需要讀者自己完成,感興趣的朋友跟隨小編一起看看吧
    2023-11-11
  • 基于Vue2實現數字縱向滾動效果

    基于Vue2實現數字縱向滾動效果

    這篇文章主要為大家詳細介紹了如何基于Vue2實現數字縱向滾動效果,從而達到顯示計時器滾動效果,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-03-03
  • 詳解Vue 實例中的生命周期鉤子

    詳解Vue 實例中的生命周期鉤子

    這篇文章主要介紹了詳解Vue 實例中的生命周期鉤子,在Vue的整個生命周期中,它提供了一些生命周期鉤子,給了我們執(zhí)行自定義邏輯的機會。
    2017-03-03
  • vue.js修改el-popover組件顯示位置

    vue.js修改el-popover組件顯示位置

    el-popover是一個基于element-ui框架設計的,用于浮動展示提示或菜單的UI組件,下面這篇文章主要給大家介紹了關于vue.js修改el-popover組件顯示位置的相關資料,需要的朋友可以參考下
    2023-06-06
  • VUE中的filters過濾器使用方法

    VUE中的filters過濾器使用方法

    這篇文章主要介紹了VUE中的filters過濾器使用方法,文章主要通過描述全局過濾器、全局過濾器之單一掛載、全局過濾器之批量掛載?、組件過濾器展開內容,具有一定的參考價值組要的小伙伴可以參考一下
    2022-03-03

最新評論

连云港市| 临泽县| 习水县| 泸西县| 芦溪县| 玛曲县| 东阿县| 凤城市| 达尔| 华亭县| 南岸区| 富锦市| 石楼县| 枞阳县| 延长县| 泰来县| 巴塘县| 准格尔旗| 大石桥市| 百色市| 托克托县| 桐梓县| 新宾| 开封县| 吴忠市| 五家渠市| 丰县| 临湘市| 莒南县| 巫溪县| 景洪市| 慈利县| 神农架林区| 北川| 涪陵区| 托克逊县| 朝阳县| 房山区| 昌平区| 申扎县| 福泉市|