使用vue 指令怎么实现气泡提示效果

发布时间:2021-06-01 17:47:35 作者:Leah
来源:亿速云 阅读:649

使用vue 指令怎么实现气泡提示效果?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

气泡指令

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) {
    // 删除节点函数
     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"
        :
      >
    </section>
      <i
          class="jc-slider-dot"
          :
          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
    }
  };

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。

推荐阅读:
  1. VUE常用插件库
  2. 使用CSS制作对话框气泡的方法

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

vue

上一篇:如何在vue中导出Excel表格

下一篇:怎么在Python中利用shuffle实现列表洗牌

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》