您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

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

2025/11/7 14:26:32发布26次查看
本篇文章给大家带来的内容是关于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('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        }    };
以上就是vue指令如何实现气泡提示(附代码)的详细内容。
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product