摘要:自己寫了個(gè)組件,想加個(gè)氣泡提示。為了復(fù)用和省事特此寫了個(gè)指令來解決。的生命周期生成前生成后生成真正更新更新銷毀。而的指令則是依賴于的生命周期,無非也是有以上類似的鉤子。當(dāng)鼠標(biāo)放到上就會(huì)顯示出來,離開就會(huì)消失。
菜鳥學(xué)習(xí)之路
//L6zt github
自己在造組件輪子,也就是瞎搞。
自己寫了個(gè)slider組件,想加個(gè)氣泡提示。為了復(fù)用和省事特此寫了個(gè)指令來解決。
預(yù)覽地址
項(xiàng)目地址 github 我叫給它胡博
我對指令的理解: 前不久看過 一部分vnode實(shí)現(xiàn)源碼,奈何資質(zhì)有限...看不懂。
vnode的生命周期-----> 生成前、生成后、生成真正dom、更新 vnode、更新dom 、 銷毀。
而Vue的指令則是依賴于vnode 的生命周期, 無非也是有以上類似的鉤子。
代碼效果
指令掛A元素上,默認(rèn)生成一個(gè)氣泡容器B插入到 body 里面,B 會(huì)獲取 元素 A 的位置信息 和 自己的
大小信息,經(jīng)過 一些列的運(yùn)算,B 元素會(huì)定位到 A 的 中間 上 位置。 當(dāng)鼠標(biāo)放到 A 上 B 就會(huì)顯示出來,離開就會(huì)消失。
氣泡指令
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 已經(jīng)在頁面里 為了獲取el 位置信信 Vue.nextTick(() => { const { context } = vnode; const { expression } = binding; // 氣泡元素根結(jié)點(diǎn) 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; // 鼠標(biāo)放上去的 回調(diào)函數(shù) el._tip_hover_fn = function(e) { // 刪除節(jié)點(diǎn)函數(shù) 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); }; //鼠標(biāo)離開 元素 隱藏 氣泡 const handleLeave = function (e) { fWarpElm.style.opacity = 0; // transitionEnd 不太好應(yīng)該加入兼容 once({ el, type: "transitionEnd", fn: function() { console.log("hide"); addClass(fWarpElm, "hide"); } }) }; el._tip_leave_fn = handleLeave; // 解決 slider 移動(dòng)結(jié)束 提示不消失 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 }) }); } , // 氣泡的數(shù)據(jù)變化 依賴于 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 組件
../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}) }; // 最后一個(gè) export const fbTwice = ({fn, time = 300}) => { let [cTime, k] = [null, null] // 獲取當(dāng)前時(shí)間 const getTime = () => new Date().getTime() // 混合函數(shù) 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) { // 清除之前的函數(shù)堆 ---- 重新記錄 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 () => { // 當(dāng)前時(shí)間 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 } };
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/98442.html
摘要:滾動(dòng)插件設(shè)計(jì)原理獲取滾動(dòng)元素的視口高度和滾動(dòng)高度得出滾動(dòng)軸最大滾動(dòng)距離根據(jù)滾動(dòng)時(shí)計(jì)算滾動(dòng)時(shí)滾動(dòng)軸的滾動(dòng)高度與比較做差,判斷是否觸發(fā)動(dòng)作。 // github L6zt滾動(dòng)插件設(shè)計(jì)原理 獲取滾動(dòng)元素的視口高度viewH和滾動(dòng)高度scrollH,得出 滾動(dòng)軸最大滾動(dòng)距離 scrollMaxTop = viewH - scrollH; 根據(jù)滾動(dòng)時(shí)計(jì)算滾動(dòng)時(shí)滾動(dòng)軸的滾動(dòng)高度 scrollTo...
摘要:滾動(dòng)插件設(shè)計(jì)原理獲取滾動(dòng)元素的視口高度和滾動(dòng)高度得出滾動(dòng)軸最大滾動(dòng)距離根據(jù)滾動(dòng)時(shí)計(jì)算滾動(dòng)時(shí)滾動(dòng)軸的滾動(dòng)高度與比較做差,判斷是否觸發(fā)動(dòng)作。 // github L6zt滾動(dòng)插件設(shè)計(jì)原理 獲取滾動(dòng)元素的視口高度viewH和滾動(dòng)高度scrollH,得出 滾動(dòng)軸最大滾動(dòng)距離 scrollMaxTop = viewH - scrollH; 根據(jù)滾動(dòng)時(shí)計(jì)算滾動(dòng)時(shí)滾動(dòng)軸的滾動(dòng)高度 scrollTo...
摘要:我們巧妙的提示框打算使用屬性選擇器也就是方括號表示法。相對性這是用在提示框的父元素上的。向上向下提示框要用到關(guān)鍵幀,而向左向右提示框使用關(guān)鍵幀。注意,在這些關(guān)鍵幀中,我們只定義了提示框所需的終止?fàn)顟B(tài)。 原文:https://webdesign.tutsplus.co...原作:Jase Smith翻譯:Stypstive 當(dāng)你的用戶需要漂亮的圖標(biāo)給出額外的文字信息時(shí),亦或是當(dāng)他們在點(diǎn)擊...
摘要:我們巧妙的提示框打算使用屬性選擇器也就是方括號表示法。相對性這是用在提示框的父元素上的。向上向下提示框要用到關(guān)鍵幀,而向左向右提示框使用關(guān)鍵幀。注意,在這些關(guān)鍵幀中,我們只定義了提示框所需的終止?fàn)顟B(tài)。 原文:https://webdesign.tutsplus.co...原作:Jase Smith翻譯:Stypstive 當(dāng)你的用戶需要漂亮的圖標(biāo)給出額外的文字信息時(shí),亦或是當(dāng)他們在點(diǎn)擊...
閱讀 3525·2023-04-26 00:16
閱讀 1361·2021-11-25 09:43
閱讀 3824·2021-11-23 09:51
閱讀 2964·2021-09-24 09:55
閱讀 713·2021-09-22 15:45
閱讀 1387·2021-07-30 15:30
閱讀 3064·2019-08-30 14:04
閱讀 2237·2019-08-26 13:46