[Vue3]封装loading指令
·
封装loading指令
效果

代码
// loading.ts
import { nextTick } from 'vue'
import type { Directive, DirectiveBinding } from 'vue'
// 扩展HTMLElement类型,添加我们需要的属性
interface LoadingElement extends HTMLElement {
__v_loading_element?: HTMLElement
}
/**
* 自定义样式的加载指令
* 用法:v-loading="isLoading"
* 参数:布尔值,控制是否显示加载效果
*/
const loading: Directive<HTMLElement, boolean> = {
mounted(el: HTMLElement, binding: DirectiveBinding<boolean>) {
// 设置父元素为相对定位
if (getComputedStyle(el).position === 'static') {
el.style.position = 'relative'
}
// 创建加载层
const loadingElement = document.createElement('div')
// 设置加载层样式
Object.assign(loadingElement.style, {
position: 'absolute',
top: '0',
left: '0',
width: '100%',
height: '100%',
backgroundColor: 'rgba(255, 255, 255, 0.7)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
zIndex: '999'
})
// 创建新的立方体加载动画
const spinner = document.createElement('div')
spinner.className = 'spinner'
// 确保spinner元素本身有明确的宽高
Object.assign(spinner.style, {
width: '60px',
height: '60px'
})
// 创建立方体的六个面
for (let i = 0; i < 6; i++) {
const face = document.createElement('div')
spinner.appendChild(face)
}
// 添加动画样式
if (!document.getElementById('v-loading-styles')) {
const styleElement = document.createElement('style')
styleElement.id = 'v-loading-styles'
// 使用新提供的CSS样式
styleElement.textContent = `
.spinner {
--clr: rgb(94, 104, 255);
--clr-alpha: rgb(94, 104, 255,.1);
animation: spinner 1.6s infinite ease;
transform-style: preserve-3d;
position: relative;
}
.spinner > div {
background-color: var(--clr-alpha);
height: 100%;
position: absolute;
width: 100%;
border: 3.5px solid var(--clr);
}
.spinner div:nth-of-type(1) {
transform: translateZ(-30px) rotateY(180deg);
}
.spinner div:nth-of-type(2) {
transform: rotateY(-270deg) translateX(50%);
transform-origin: top right;
}
.spinner div:nth-of-type(3) {
transform: rotateY(270deg) translateX(-50%);
transform-origin: center left;
}
.spinner div:nth-of-type(4) {
transform: rotateX(90deg) translateY(-50%);
transform-origin: top center;
}
.spinner div:nth-of-type(5) {
transform: rotateX(-90deg) translateY(50%);
transform-origin: bottom center;
}
.spinner div:nth-of-type(6) {
transform: translateZ(30px);
}
@keyframes spinner {
0% {
transform: rotate(45deg) rotateX(-25deg) rotateY(25deg);
}
50% {
transform: rotate(45deg) rotateX(-385deg) rotateY(25deg);
}
100% {
transform: rotate(45deg) rotateX(-385deg) rotateY(385deg);
}
}
`
document.head.appendChild(styleElement)
}
// 添加加载图标到加载层
loadingElement.appendChild(spinner)
// 将加载层元素存储在原始元素上
;(el as LoadingElement).__v_loading_element = loadingElement
// 如果初始值为true,则显示加载效果
if (binding.value) {
el.appendChild(loadingElement)
}
},
updated(el: HTMLElement, binding: DirectiveBinding<boolean>) {
// 值变化时更新加载状态
if (binding.value !== binding.oldValue) {
nextTick(() => {
const loadingElement = (el as LoadingElement).__v_loading_element
if (binding.value) {
// 显示加载效果
if (loadingElement && !el.contains(loadingElement)) {
el.appendChild(loadingElement)
}
} else {
// 隐藏加载效果
if (loadingElement && el.contains(loadingElement)) {
el.removeChild(loadingElement)
}
}
})
}
},
unmounted(el: HTMLElement) {
// 清理加载层
const loadingElement = (el as LoadingElement).__v_loading_element
if (loadingElement && el.contains(loadingElement)) {
el.removeChild(loadingElement)
}
}
}
export default loading
使用
<script setup lang="ts">
import vLoading from '@/directives/loading'
</script>
<template>
<div v-loading="true" class="flex flex-1 flex-col">
<div class="w-full flex-1"></div>
</div>
</template>
更多推荐
所有评论(0)