
公司为何要开发小程序_vue 指令之气泡提示效果的
2021-01-08 11:54
自己 在造组件轮子,也就是瞎搞。
自己写了个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-ti凡科抠图' , 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-ti凡科抠图-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({ 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({ type: 'mouseenter', fn: el._tip_hover_fn on({ 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 section /section : ref="dot" @mousedown="moveStart" v-jc-ti凡科抠图="createNode" /div /template script import {elemOffset, on, off, once} from "../../utils/dom"; const global = window; const doc = global.document; export default { pro凡科抠图: { 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 { // zh-CN/docs/Web/pareDocumentPosition return (pareDocumentPosition(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 指令之气泡提示效果的实现代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对凡科网站的支持!
扫描二维码分享到微信