vue2企业级项目(六)
自定义指令
-
创建
src/directive/index.js
const directives = require.context("./modules", true, /\.js$/); export default { install: (Vue) => { directives.keys().forEach((key) => { let directive = directives(key).default; let name = key.replace(/^\.\/(.*)\.\w+$/, "$1"); directive && Vue.directive(name, directive); }); }, };
-
main.js
注入使用import directives from "./directive"; ... Vue.use(directives);
-
创建
src/directive/modules
目录
1、v-loading
element-ui
有,就不需要
2、v-draggable
创建src/directive/draggable.js
import { checkDataType } from "@/utils/utils";
let recoverStyle = null;
export default {
update(el, binding) {
let config = binding.value;
let visible = false;
let target = ".el-dialog__header";
let control = ".el-dialog";
if (checkDataType(config, "object")) {
visible = config.visible;
target = config.target;
control = config.control;
} else {
visible = config;
}
const dialogHeaderEl = el.querySelector(target);
const dragDom = el.querySelector(control);
dialogHeaderEl.style.cursor = "move";
function readStyle(dom, attr) {
if (document.body.currentStyle) return dom.currentStyle[attr];
return getComputedStyle(dom, false)[attr];
}
if (visible) {
recoverStyle = {
left: readStyle(dragDom, "left"),
top: readStyle(dragDom, "top"),
margin: readStyle(dragDom, "margin"),
};
dialogHeaderEl.onmousedown = function(e) {
// 获取鼠标相对当前元素内部位置
const disX = e.clientX - dragDom.offsetLeft;
const disY = e.clientY - dragDom.offsetTop;
// 获取当前窗口视图大小
const screenWidth = document.body.clientWidth;
const screenHeight = document.documentElement.clientHeight;
// 获取当前元素大小
const dragDomWidth = dragDom.offsetWidth;
const dragDomheight = dragDom.offsetHeight;
// 计算获取最大偏移量
const maxLeft = screenWidth - dragDomWidth;
const maxTop = screenHeight - dragDomheight;
document.onmousemove = function(e) {
let left = e.clientX - disX;
let top = e.clientY - disY;
// 碰撞判断
if (left <= 0) left = 0;
if (left >= maxLeft) left = maxLeft;
if (top <= 0) top = 0;
if (top >= maxTop) top = maxTop;
left = ((left * 100) / screenWidth).toFixed(2) + "%";
top = ((top * 100) / screenHeight).toFixed(2) + "%";
dragDom.style.left = left;
dragDom.style.top = top;
dragDom.style.margin = 0;
};
document.onmouseup = function() {
document.onmousemove = null;
document.onmouseup = null;
};
};
} else {
// 还原初始化的样式
dragDom.style.left = recoverStyle.left;
dragDom.style.top = recoverStyle.top;
dragDom.style.margin = recoverStyle.margin;
dialogHeaderEl.onmousedown = null;
recoverStyle = null;
}
},
};
3、v-click-outside
创建src/directive/click-outside.js
export default {
bind(el, binding) {
// 点击事件回调函数
const onClickOutside = (event) => {
if (!(el === event.target || el.contains(event.target))) {
binding.value(event);
}
};
// 将回调函数绑定到元素上
el._clickOutsideCallback = onClickOutside;
// 添加点击事件监听器
document.addEventListener("click", onClickOutside);
},
unbind(el) {
// 移除点击事件监听器
document.removeEventListener("click", el._clickOutsideCallback);
delete el._clickOutsideCallback;
},
};
4、v-ripple
(简单的)
创建src/directive/ripple.js
function createRipple(el, radius, color, disX, disY) {
new Promise((resolve) => {
const rippleDom = document.createElement("span");
rippleDom.className = "ripple";
rippleDom.setAttribute(
"style",
`
display: block;
position: absolute;
border-radius: 50%;
transform: translate(-50%, -50%) scale(0);
transition: 0.5s;
background-color: ${color || "rgba(0, 0, 0, 0.6)"};
top: ${disY}px;
left: ${disX}px;
width: ${radius * 3}px;
height: ${radius * 3}px;
animation: ripple-animation 0.5s linear;
`,
);
el.appendChild(rippleDom);
let timer = setTimeout(() => {
el.removeChild(rippleDom);
resolve(timer);
}, 520);
}).then((timer) => {
clearTimeout(timer);
});
}
function rippleShow(e) {
const targetDom = e.target;
const radius =
targetDom.offsetHeight > targetDom.offsetWidth
? targetDom.offsetHeight
: targetDom.offsetWidth;
const disX = e.clientX - targetDom.offsetLeft;
const disY = e.clientY - targetDom.offsetTop;
createRipple(targetDom, radius, e?._ripple, disX, disY);
}
export default {
bind(el, binding) {
el._ripple = binding.value;
el.style.position = "relative";
el.style.overflow = "hidden";
el.style.userSelect = "none";
el.addEventListener("mousedown", rippleShow);
},
unbind(el) {
el._ripple = null;
el.removeEventListener("mousedown", rippleShow);
},
};
创建src/styles/animation.less
,并在scr/styles/index.less
内引入
// ripple动画
@keyframes ripple-animation {
to {
transform: translate(-50%, -50%) scale(1);
opacity: 0;
}
}
5、v-intersect
交叉观察者
创建src/directive/intersect.js
export default {
bind(el, binding) {
let info = null;
let callback = null;
if (typeof binding.value === "object") {
info = binding.value?.info;
callback = binding.value?.callback;
} else {
callback = binding.value;
}
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
callback && callback(info);
}
});
},
{ root: el.parentElement },
);
observer.observe(el);
},
};
6、v-scroll
滚动监听
创建src/directive/scroll.js
export default {
bind(el, binding) {
el.onscroll = function() {
binding.value({
x: el.scrollLeft,
y: el.scrollTop,
});
};
},
unbind(el) {
el.onscroll = null;
},
};
7、v-scroll-to
滚动设置
创建src/directive/scroll-to.js
export default {
update(el, binding) {
let x = binding.modifiers?.x || false;
if (binding.value && binding.value === binding.oldValue) return;
if (typeof binding.value === "number") {
x ? (el.scrollLeft = binding.value) : (el.scrollTop = binding.value);
} else if (typeof binding.value === "string") {
let targetDom = document.querySelector(binding.value);
if (x) {
let border = getComputedStyle(el, false)["borderLeftWidth"];
let padding = getComputedStyle(el, false)["paddingLeft"];
border = border.replace("px", "");
padding = padding.replace("px", "");
el.scrollLeft = targetDom.offsetLeft - el.offsetLeft - border - padding;
} else {
let border = getComputedStyle(el, false)["borderTopWidth"];
let padding = getComputedStyle(el, false)["paddingTop"];
border = border.replace("px", "");
padding = padding.replace("px", "");
el.scrollTop = targetDom.offsetTop - el.offsetTop - border - padding;
}
}
},
};
8、v-auto-scroll
自动滚动
创建src/directive/scroll-auto.js
function active(el, scroll, step, max, x) {
window.requestAnimationFrame(() => {
x ? (el.scrollLeft = scroll) : (el.scrollTop = scroll);
scroll += step;
if (scroll >= max) scroll = 0 + step;
active(el, scroll, step, max, x);
});
}
export default {
inserted(el, binding) {
const x = binding.modifiers?.x || false;
let step = 0;
let scroll = 0;
if (typeof binding.value === "object") {
step = binding.value?.step || 5;
scroll = binding.value?.begin || 0;
} else {
step = binding.value || 5;
}
const scrollMax = x ? el.scrollWidth : el.scrollHeight;
el._children = [].slice.call(el.children);
el._children.forEach((child) => {
el.appendChild(child.cloneNode(true));
});
active(el, scroll, step, scrollMax, x);
},
unbind(el) {
el.innerHtml = "";
let children = [].slice.call(el.children);
children.forEach((child) => {
el.removeChild(child);
});
el._children.forEach((child) => {
el.appendChild(child.cloneNode(true));
});
},
};