本篇文章给大家带来的内容是关于vue指令如何实现气泡提示(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

菜鸟学习之路

//L6zt github
自己 在造组件轮子,也就是瞎搞。
自己写了个slider组件,想加个气泡提示。为了复用和省事特此写了个指令来解决。
预览地址
项目地址 github

vue指令如何实现气泡提示(附代码)

我对指令的理解: 前不久看过 一部分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('p');       // 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>     <p 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>     </p> </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         }     };
标签
DT素材网

DT素材网

168

0

0

( 此人很懒并没有留下什么~~ )