最终实现效果

v-loading

可以实现提示文字背景颜色的自定义
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
		}
	}
})

实现起来其实也不复杂。如果对你有帮助的话 请点个赞再收藏一下吧~

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐