Vue3+TypeScript项目构建之实现自定义指令v-loading, axios请求时的加载动画
最终实现效果可以实现提示文字和背景颜色的自定义xzw-loading-text=‘别急嘛~’// 默认为加载中xzw-loading-background=‘rgba(0, 0, 0, .4)’ // 默认背景色为rgba(255,255,255,.8)创建自定义指令函数路径:/src/directives/loading/index.tsVue3与Vue2的自定义指令钩子有了很大的改变改变,详情
·
最终实现效果
可以实现提示文字和背景颜色的自定义
xzw-loading-text=‘别急嘛~’ // 默认为加载中
xzw-loading-background=‘rgba(0, 0, 0, .4)’ // 默认背景色为rgba(255,255,255,.8)
创建自定义指令函数
路径:/src/directives/loading/index.ts
Vue3与Vue2的自定义指令钩子有了很大的改变改变,详情戳链接
Vue3自定义指令钩子
created - 新的!在元素的 attribute 或事件侦听器应用之前调用。
bind → beforeMount
inserted → mounted
beforeUpdate:新的!这是在元素本身更新之前调用的,很像组件生命周期钩子。
update → 移除!有太多的相似之处要更新,所以这是多余的,请改用 updated。
componentUpdated → updated
beforeUnmount:新的!与组件生命周期钩子类似,它将在卸载元素之前调用。
unbind -> unmounted
const MyDirective = {
beforeMount(el, binding, vnode, prevVnode) {},
mounted() {},
beforeUpdate() {}, // 新
updated() {},
beforeUnmount() {}, // 新
unmounted() {}
}
上TS代码 然后解析
// 全局加载组件
const handleMove = (e: TouchEvent) => {
e.preventDefault()
}
export default {
beforeMount(el: HTMLElement, binding: any) {},
mounted() {},
beforeUpdate() {}, // 新
updated(el: HTMLElement, binding: any) { // 更新的时候用这个
if (binding.value) {
/**
* binding.value就是v-loading='true'传过来的那个值
* 如果传过来的值为true 你们就是要展示loading
* el是当前指令绑定的对象 binding是传过来的值
*/
// 在移动端页面禁用滚动
document.body.addEventListener('touchmove', handleMove, { passive: false })
// 判断当前页面是否存在没有消失的loading
// hasLoading 值为false 时表明没有loading 可以添加loading
const hasLoading = el.getElementsByClassName('xzw-loading').length !== 0
if (hasLoading) return '日你温哦,有loading了还添加个锤子'
// 判断是否传入了自定义提示文字
const hasCustomText = el.getAttribute('xzw-loading-text')
// 判断是否传入了自定义背景色
const hasCustomBgColor = el.getAttribute('xzw-loading-background')
el.style.position = 'relative'
const mask = document.createElement('div') // 创建最外层div 高度占满(包含滚动)
const loadingBox = document.createElement('div') // 显示loading的div 高度是100vh
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') // svg标签 用来实现旋转的圆
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle') // 旋转的那个圆
const text = document.createElement('p') // 加载的文字
// 下面所添加的class样式都会在后面贴出
mask.setAttribute('class', 'xzw-loading')
mask.style.background = !hasCustomBgColor ? 'rgba(255, 255, 255, .8)' : hasCustomBgColor
loadingBox.setAttribute('class', 'xzw-loading-box')
svg.setAttribute('class', 'xzw-loading_circular refleash')
svg.setAttribute('viewBox', '25 25 50 50')
circle.setAttribute('cx', '50')
circle.setAttribute('cy', '50')
circle.setAttribute('r', '20')
circle.setAttribute('fill', 'none')
text.innerText = !hasCustomText ? '加载中' : hasCustomText
svg.appendChild(circle)
loadingBox.appendChild(svg)
loadingBox.appendChild(text)
mask.appendChild(loadingBox)
el.appendChild(mask)
} else {
/**
* 传过来的值为false 不需要展示loading
* 移除loading相关dom
*/
for (let i = 0; i < el.childNodes.length; i++) {
if ((el.childNodes[i] as any).className === 'xzw-loading') {
const childNodes = el.getElementsByClassName('xzw-loading')[0]
el.removeChild(childNodes)
break
}
}
// 弹窗消失时移除'禁用滑动事件'
document.body.removeEventListener('touchmove', handleMove)
}
},
beforeUnmount() {}, // 新
unmounted() {}
}
样式代码(修改类名后可直接使用)
// 自定义loading相关样式
.xzw-loading{
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 100;
text-align: center;
p{
color: $primary-color;
}
.xzw-loading-box{
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
}
// 旋转整个svg容器
.refleash{
animation: RotateCricle 1s linear infinite;
}
// svg容器样式 以及 里面的circle的旋转
.xzw-loading_circular {
width: 40px;
height: 40px;
color: $primary-color;
margin-bottom: 20px;
circle{
animation: xzw-circular 1.5s ease-in-out infinite;
stroke: currentColor;
stroke-width: 3;
stroke-linecap: round;
}
}
@keyframes RotateCricle{
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes xzw-circular{
0% {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -20;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120;
}
}
main.ts里注册为全局指令
import loading from '@/directives/loading/index'
app.directive('loading', loading)
在页面中使用(简单演示)
<div
class='container'
v-loading='loading'
xzw-loading-text='别急嘛~'
xzw-loading-background='rgba(0, 0, 0, .4)'
>
</div>
<button @click='handleClickGetData '>Click To Get Data</button>
export default defineComponent({
setup(props, ctx) {
const loading = ref<boolean>(false)
const handleClickGetData = async() => {
// 开启loading
loading.value = true
const res = await GetData({id})
if (res.data.code === 200) {
// 关闭loading
loading.value = false
}
}
return{
handleClickGetData ,
loading
}
}
})
实现起来其实也不复杂。如果对你有帮助的话 请点个赞再收藏一下吧~
更多推荐
已为社区贡献5条内容
所有评论(0)