日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > php >内容正文

php

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

發(fā)布時間:2025/4/16 php 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数字气泡 php,vue指令如何实现气泡提示(附代码) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本篇文章給大家?guī)淼膬?nèi)容是關于vue指令如何實現(xiàn)氣泡提示(附代碼),有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。

菜鳥學習之路

//L6zt github

自己 在造組件輪子,也就是瞎搞。

自己寫了個slider組件,想加個氣泡提示。為了復用和省事特此寫了個指令來解決。

預覽地址

項目地址github

我對指令的理解: 前不久看過 一部分vnode實現(xiàn)源碼,奈何資質(zhì)有限...看不懂。

vnode的生命周期-----> 生成前、生成后、生成真正dom、更新 vnode、更新dom 、 銷毀。

而Vue的指令則是依賴于vnode 的生命周期, 無非也是有以上類似的鉤子。

代碼效果

指令掛A元素上,默認生成一個氣泡容器B插入到 body 里面,B 會獲取 元素 A 的位置信息 和 自己的

大小信息,經(jīng)過 一些列的運算,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 已經(jīng)在頁面里 為了獲取el 位置信信

Vue.nextTick(() => {

const { context } = vnode;

const { expression } = binding;

// 氣泡元素根結(jié)點

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;

// 鼠標放上去的 回調(diào)函數(shù)

el._tip_hover_fn = function(e) {

// 刪除節(jié)點函數(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);

};

//鼠標離開 元素 隱藏 氣泡

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 移動結(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 組件

class="slider-active-bg"

:style="{width: `${left}px`}"

>

class="jc-slider-dot"

:style="{left: `${left}px`}"

ref="dot"

@mousedown="moveStart"

v-jc-tips="createNode"

>

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;

}

};

.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;

}

}

../utils/domconst 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()

// 混合函數(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 () => {

// 當前時間

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

}

};

總結(jié)

以上是生活随笔為你收集整理的数字气泡 php,vue指令如何实现气泡提示(附代码)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。