实现vue加载指令 v-loading

news2024/11/28 19:00:19

文章目录

    • 为什么使用指令实现 loading
    • 具体实现
      • 封装准备
      • 实现 loading 效果
      • loading 显示与隐藏
      • 使用修饰符扩展
    • 完整代码与结语

本文不会详细的说明 vue 中指令这些知识点,如果存在疑问,请自行查阅文档或者其他资料

为什么使用指令实现 loading

  1. 在日常的开发中,加载效果是非常常见的,但是怎么才能方便的使用,那就还是值得思考一番的,
  2. 比如在 vue 中,最简单的方式就是封装为一个组件了,但是如果封装为组件的话,在不想注册为全局组件的时候,每次都需要引入、注册、使用;如果注册为全局组件,你也往往需要分析结构在合适的位置插入组件,貌似使用起来都会麻烦一点,loading 这种使用频率高的效果,使用一次麻烦一点,使用100次就会觉得更加麻烦
  3. 而使用指令只需要在需要的位置像使用属性一样即可;封装可以麻烦,但是使用越简单越好

具体实现

封装准备

  1. 首先需要一个 js 文件,因为指令实际上就是一个对象,通过在不同的钩子函数中执行对应的逻辑,在本文中,需要使用的钩子函数是 inserted 和 update,因此可以写一个基础的结构,如下:

    export default {
        inserted(el, binding){
            
        },
        
        update(el, binding){
            
        }
    }
    
  2. 然后将这个指令在入口文件 main.js 内进行全局注册,如下:

    import vLoading from '你封装指令js文件的路径'
    
    // 注册指令
    Vue.directive('jc-loading', vLoading)
    
  3. 创建一个 vue 文件来使用这个指令,如下:

    <template>
    	<div class="container">
    		<button
    			style="margin-bottom: 20px"
    			@click="handleClick">
    			开关
    		</button>
    		<div
    			class="box"
    			v-jcLoading="isLoading">
    			Lorem ipsum dolor sit amet consectetur adipisicing elit. Libero, temporibus veniam! Totam temporibus ipsam, atque
    			amet aliquid corporis molestiae, perspiciatis asperiores doloremque enim explicabo aperiam. Vel doloremque
    			voluptatibus incidunt quae suscipit cupiditate. Obcaecati sunt, consectetur voluptas sequi aliquam omnis, rem non
    			molestiae assumenda illum quasi excepturi error voluptatibus pariatur nulla.
    		</div>
    	</div>
    </template>
    
    <script>
    export default {
    	data() {
    		return {
    			isLoading: false
    		}
    	},
    
    	methods: {
    		handleClick() {
    			this.isLoading = !this.isLoading
    		}
    	}
    }
    </script>
    
    <style lang="less" scoped>
    .container {
    	width: 100vw;
    	height: 100vh;
    	display: flex;
    	flex-direction: column;
    	justify-content: center;
    	align-items: center;
    	.box {
    		width: 500px;
    		height: 300px;
    		padding: 20px;
    		border: 1px solid #999;
    		color: #f40;
    	}
    }
    </style>
    
  4. 查看一下指令内的输出语句是否正常执行,如图:

在这里插入图片描述

  1. 正常进行了打印,现在我们进行正式的编写

实现 loading 效果

  1. 实现这一步其实也非常的简单,找一个你觉得好看或者合适的加载效果,按照正常的 html+css+js 进行实现就好,当你实现好之后,需要做的就是使用 js 进行动态的创建这些元素,所以我们需要有一个函数帮助我们完成这一步,如下:

    // 导入模块化的 less 文件
    import styles from './loading.module.less'
    
    // 创建 loading 元素
    function createLoading() {
    	// 创建 load 遮罩
    	const loadingMask = document.createElement('div')
    	loadingMask.dataset.role = 'jc-loading'
    	loadingMask.classList.add(styles['jc-loading-mask'])
    
    	// 创建 loading 旋转容器元素
    	const loadingSpinner = document.createElement('div')
    	loadingSpinner.classList.add(styles['jc-loading-spinner'])
    	loadingMask.appendChild(loadingSpinner)
    
    	// 创建文本片段
    	const fragment = document.createDocumentFragment()
    	// 创建子元素进行旋转缩放
    	for (let i = 0; i < 12; i++) {
    		const div = document.createElement('div')
    		div.style = `--i:${i}`
    		div.classList.add(styles['jc-loading-spinner__circle'])
    		fragment.appendChild(div)
    	}
    	loadingSpinner.appendChild(fragment)
    
    	return loadingMask
    }
    
  2. 代码还是非常简单的,具体取决于你本身实现的 loading 效果,我这个是一个比较简单的动效,上面这个地方如果有疑问那应该就是证据导入语句,在 vue 中,如果希望一个 less 文件作为一个模块导入和使用,需要将文件命名改为 文件名.module.less 这种格式,即文件后缀为 .module.less,我们在 inserted 钩子函数中打印一下这个导入的 styles,如下:

    export default {
        inserted(el, binding){
            console.log(styles)
        },
        
        update(el, binding){
            
        }
    }
    
  3. 结果如图:

    在这里插入图片描述

  4. k 为 less 文件中开发时书写的类名,而后面的 v 表示实际的类名,本案例中 less 文件代码如下:

    .jc-loading-mask {
    	position: absolute;
    	inset: 0;
    	background-color: rgba(0, 0, 0, 0.7);
    }
    
    .jc-loading-spinner {
    	position: absolute;
    	left: calc(50% - 25px);
    	top: calc(50% - 25px);
    	width: 50px;
    	height: 50px;
    	animation: sp 4s linear infinite;
    }
    
    .jc-loading-spinner__circle {
    	position: absolute;
    	top: 0;
    	left: calc(50% - 3px);
    	width: 6px;
    	height: 6px;
    	transform: rotate(calc(var(--i) * (360deg / 12)));
    	transform-origin: center 25px;
    }
    
    .jc-loading-spinner__circle::before {
    	content: '';
    	inset: 0;
    	border-radius: 50%;
    	position: absolute;
    	background-color: #ff6348;
    	animation: zoom 2.5s linear infinite;
    	animation-delay: calc(var(--i) * 0.2s);
    }
    
    @keyframes sp {
    	to {
    		transform: rotate(360deg);
    	}
    }
    
    @keyframes zoom {
    	0% {
    		transform: scale(1.2);
    	}
    
    	50% {
    		transform: scale(0.5);
    	}
    
    	100% {
    		transform: scale(1.2);
    	}
    }
    
  5. 这些 css 样式我就不再赘述了,先不进行其他逻辑判断,只展示到页面上,看看效果,代码如下:

    export default {
        inserted(el, binding){
          	el.appendChild(createLoading())
        },
        
        update(el, binding){
            
        }
    }
    
  6. 效果如图:

    在这里插入图片描述

  7. 其实也不难对吧,这个效果你可以根据自己的需求来进行更换,但是实现方法都是可以套用的

loading 显示与隐藏

  1. 把这个需求梳理清楚之后,后面的就呼之欲出了,什么时候显示,必然是指令上的值为 true 的时候,隐藏则相反,这是一个先决条件

  2. 在这个条件之后呢?还需要考虑什么呢?是不是需要创建这个 loading 效果的元素啊,当指令的值为 true 且不存在当前的 loading 元素的时候,才需要创建,而指令的值为 false ,则是当前的 loading 元素存在的话,就需要移除啊

  3. 基于上面的条件,我们需要一个辅助函数,来帮助我们查找当前 loading 效果的元素是否存在,如下:

    function getLoading(container) {
    	return container.querySelector(`[data-role="jc-loading"]`)
    }
    
  4. 所以我们在 inserted 钩子函数中,应该进行判断,当指令的值为 true 且元素不存在时,就创建元素并添加,如下:

    inserted(el, binding){
    	// 如果为 true 且不存在加载元素就创建元素添加加载效果
    	if (!getLoading(el)) {
    		const loading = createLoading()
    		el.appendChild(loading)
    	}
    }
    
  5. 而 update 函数中的代码是不是也可以写出来了,进行条件判断来执行逻辑,而且不难发现其实这个条件与 inserted 中的条件是重合的,所以我们可以封装为一个函数,如下:

    // 开启加载效果
    function openLoading(el, binding) {
    	// 如果为 false 且存在加载元素就移除加载元素
    	if (!binding.value) {
    		const dom = getLoading(el)
    		dom && dom.remove()
    	} else {
    		// 如果为 true 且不存在加载元素就创建元素添加加载效果
    		if (!getLoading(el)) {
    			const loading = createLoading()
    			el.appendChild(loading)
    		}
    	}
    }
    
  6. 当然,还需要考虑当前显示加载元素的 dom 是不是存在相对定位,如果不存在则改为相对定位,最后指令对象的实际代码如下:

    export default {
        inserted(el, binding){
            // 检测绑定的元素的 position 属性是否为 static
    		if (window.getComputedStyle(el).position === 'static') {
    			// 如果是则改为相对定位
    			el.style.position = 'relative'
    		}
            
          	openLoading(el, binding)
        },
        
        update(el, binding){
            openLoading(el, binding)
        }
    }
    
  7. 我们看一下实际的效果,如图:

    在这里插入图片描述

使用修饰符扩展

  1. 通过 modifiers(修饰符) 进行一个扩展,当指令了添加了修饰符 body 的时候,loading 就会插入到 body 里面,填充 body,所以我们还需要进行一些额外的判断,如下:

    function getContainer(el, binding) {
    	return binding.modifiers.body ? document.body : el
    }
    
    export default {
    	inserted(el, binding) {
    		if (window.getComputedStyle(el).position === 'static') {
    			el.style.position = 'relative'
    		}
    		openLoading(getContainer(el, binding), binding)
    	},
    
    	update(el, binding) {
    		openLoading(getContainer(el, binding), binding)
    	}
    }
    
  2. 此时在组件中使用添加修饰符 body 即可,如下:

    <!-- 添加修饰符.body -->
    <div class="box" v-jcLoading.body="isLoading">
    ...
    </div>
    
  3. 查看效果,如图:

    在这里插入图片描述

  4. 元素实际插入的位置,如图:

    在这里插入图片描述

完整代码与结语

  1. 现在已经具备了一个 loading 指令基本的效果,如果还需要进行其他扩展,比如传递给 loading 指令的值不是一个单纯的布尔值,而是一个对象,如下:

    { loading:true, color: 'blue', text: '拼命加载中...' ... }
    
  2. 通过这些配置来增强指令的效果,有兴趣的可以自己试试

  3. 完整指令代码:

    import styles from './loading.module.less'
    
    function getLoading(container) {
    	return container.querySelector(`[data-role="jc-loading"]`)
    }
    
    function createLoading() {
    	const loadingMask = document.createElement('div')
    	loadingMask.dataset.role = 'jc-loading'
    	loadingMask.classList.add(styles['jc-loading-mask'])
    
    	const loadingSpinner = document.createElement('div')
    	loadingSpinner.classList.add(styles['jc-loading-spinner'])
    	loadingMask.appendChild(loadingSpinner)
    
    	const fragment = document.createDocumentFragment()
    	for (let i = 0; i < 12; i++) {
    		const div = document.createElement('div')
    		div.style = `--i:${i}`
    		div.classList.add(styles['jc-loading-spinner__circle'])
    		fragment.appendChild(div)
    	}
    	loadingSpinner.appendChild(fragment)
    
    	return loadingMask
    }
    
    function openLoading(el, binding) {
    	if (!binding.value) {
    		const dom = getLoading(el)
    		dom && dom.remove()
    	} else {
    		if (!getLoading(el)) {
    			const loading = createLoading()
    			el.appendChild(loading)
    		}
    	}
    }
    
    function getContainer(el, binding) {
    	return binding.modifiers.body ? document.body : el
    }
    
    export default {
    	inserted(el, binding) {
    		if (window.getComputedStyle(el).position === 'static') {
    			el.style.position = 'relative'
    		}
    		openLoading(getContainer(el, binding), binding)
    	},
    
    	update(el, binding) {
    		openLoading(getContainer(el, binding), binding)
    	}
    }
    
  4. less 样式代码:

    .jc-loading-mask {
    	position: absolute;
    	inset: 0;
    	background-color: rgba(0, 0, 0, 0.7);
    }
    
    .jc-loading-spinner {
    	position: absolute;
    	left: calc(50% - 25px);
    	top: calc(50% - 25px);
    	width: 50px;
    	height: 50px;
    	animation: sp 4s linear infinite;
    }
    
    .jc-loading-spinner__circle {
    	position: absolute;
    	top: 0;
    	left: calc(50% - 3px);
    	width: 6px;
    	height: 6px;
    	transform: rotate(calc(var(--i) * (360deg / 12)));
    	transform-origin: center 25px;
    }
    
    .jc-loading-spinner__circle::before {
    	content: '';
    	inset: 0;
    	border-radius: 50%;
    	position: absolute;
    	background-color: #ff6348;
    	animation: zoom 2.5s linear infinite;
    	animation-delay: calc(var(--i) * 0.2s);
    }
    
    @keyframes sp {
    	to {
    		transform: rotate(360deg);
    	}
    }
    
    @keyframes zoom {
    	0% {
    		transform: scale(1.2);
    	}
    
    	50% {
    		transform: scale(0.5);
    	}
    
    	100% {
    		transform: scale(1.2);
    	}
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1356749.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(Linux)虚拟机配置固定IP

Linux操作系统的IP地址是通过DHCP服务获取的&#xff0c;也就是动态获取IP地址&#xff0c;每次重启设备后都会获取一次&#xff0c;会导致IP地址频繁变更&#xff0c;为了不频繁更新映射关系&#xff0c;我们需要IP地址固定下来。 1.在VM中配置IP地址网关和网段 打开虚拟网络…

Prometheus-AlertManager 邮件告警

环境,软件准备 本次演示环境&#xff0c;我是在虚拟机上安装 Linux 系统来执行操作&#xff0c;以下是安装的软件及版本&#xff1a; System: CentOS Linux release 7.6Docker: 24.0.5Prometheus: v2.37.6Consul: 1.6.1 docker 安装prometheus,alertmanage,说明一下这里直接将…

2024-01-04 用llama.cpp部署本地llama2-7b大模型

点击 <C 语言编程核心突破> 快速C语言入门 用llama.cpp部署本地llama2-7b大模型 前言一、下载llama.cpp以及llama2-7B模型文件二、具体调用总结 使用协议: License to use Creative Commons Zero - CC0 该图片个人及商用免费&#xff0c;无需显示归属&#xff0c;但如果…

Proxy 与 defineProperty 的理解、区别、优势、劣势

一、Object.defineProperty() 文档&#xff1a;Object.defineProperty() - JavaScript | MDN 作用&#xff1a;对一个对象进行操作的方法。可以为一个对象增加一个属性&#xff0c;同时也可以对一个属性进行修改和删除。 它是在 ES5 中引入的&#xff0c;使用了 getter 和 s…

windows机器上安装mysql

0、mysql下载地址 1、参考文章 2、把Data数据目录迁移到其他盘 2.0 首先停止mysql&#xff08;任务管理器-详细信息-随便找个进程右击进入转入服务&#xff0c;找到MySQL服务&#xff0c;点击停止&#xff09; 2.1 windows的 mysql默认的data目录在C:\ProgramData\MySQL\MySQ…

【编译原理】期末预习PPT前四章笔记II

看了看学校的ppt&#xff0c;记的比较随意O.o 因为我的考试范围里边没有简答所以概念什么的没怎么记 没有简答只有选择真是太好了嘿嘿嘿 目录 I. 概述&#xff08;好多字。。&#xff09; 一、高级语言的分类 1、体裁 2、执行方式 二、各种语言的执行方式 三、编译程序…

52、全连接 - 特征与样本空间的对应关系

上一节说到经过全连接层之后,神经网络学习到的特征,会从隐层特征空间逐步映射到样本空间,这主要是由于全连接层可以融合全局的特征。 在经过全连接层之后,在 ResNet50 这个神经网络中会输出1000个特征的得分值,这1000个特征的得分值,便可以对应到图像的分类。 怎么对应…

一文讲透Linux应用编程—进程原理

文章目录 程序的开始和结束main函数由谁调用&#xff1f;程序是如何结束的&#xff1f;atexit注册进程终止处理函数return、 exit、_exit三者区别 进程环境环境变量进程运行的虚拟空间 进程的正式引入什么是进程&#xff1f;进程ID多进程调度原理 fork创建子进程为什么要创建子…

cnstd使用效果测试

使用参考&#xff1a;https://github.com/breezedeus/CnSTD/tree/master 原理参考&#xff1a;https://cnocr.readthedocs.io/zh/latest/intro-cnstd-cnocr.pdf 模型&#xff1a; 结论&#xff1a; 经过测试&#xff0c; 长文本检测效果不错&#xff0c;短文本可能角度不对 …

MO 2023 年度回顾

PART-ONE 行业态势 随着供需关系的变化&#xff0c;数据库的竞争在经历了 3 年 “百花齐放” 般的发展后&#xff0c;终于在 2023 年进入到了一个相对收拢的阶段。 2023 年&#xff0c;各个数据库厂商间很有默契地在两个方面达成了一致&#xff1a; HTAP 已经成为新一代数据…

vue保姆级教程----深入了解 Vue3计算属性

&#x1f4e2; 鸿蒙专栏&#xff1a;想学鸿蒙的&#xff0c;冲 &#x1f4e2; C语言专栏&#xff1a;想学C语言的&#xff0c;冲 &#x1f4e2; VUE专栏&#xff1a;想学VUE的&#xff0c;冲这里 &#x1f4e2; CSS专栏&#xff1a;想学CSS的&#xff0c;冲这里 &#x1f4…

引导过程和服务

宏内核(monolithic kernel)&#xff1a;又称单内核和强内核&#xff0c;Unix&#xff0c;Linux把所有系统服务都放到内核里&#xff0c;所有功能集成于同一个程序&#xff0c;分层实现不同功能&#xff0c;系统庞大复杂&#xff0c;Linux其实在单内核内核实现了模块化&#xff…

ROS 系列学习教程(总目录)

ROSLearning 一、ROS概览 1.1 ROS简介 To be continued… 1.2 ROS安装 Ubuntu 安装 ROS 详细教程&#xff08;以最后一个ROS1版本Noetic为例&#xff09; 1.3 ROS Hello World ROS创建工作空间添加包并编译 ROS Hello World 1.4 ROS架构 ROS架构&#xff1a;文件系统 …

three.js相机按照指定路线在建筑模型中漫游(支持开始,暂停)

three.js相机按照指定路线在模型中漫游&#xff08;支持开始&#xff0c;暂停&#xff09; 关键点 相机运动曲线 // 相机路线 const points [new THREE.Vector3(0, 40, 300),new THREE.Vector3(50, 40, 300),new THREE.Vector3(50, 40, 50),new THREE.Vector3(150, 40, 50),…

在js中foreach、for in和for of 的区别是什么

for in 是一种在 JavaScript 中遍历对象属性的方法&#xff0c;它可以遍历一个对象的所有可枚举属性&#xff0c;但不能遍历数组。 for of 是一种在 JavaScript 中遍历可迭代对象的方法&#xff0c;它可以遍历数组、字符串、Set、Map 等可迭代对象。 foreach 是 JavaScript 中…

华清远见作业第二十一天——IO(第四天)

思维导图&#xff1a; 创建出三个进程完成两个文件之间拷贝工作&#xff0c;子进程1拷贝前一半内容&#xff0c;子进程2拷贝后一半内容&#xff0c;父进程回收子进程的资源。 代码&#xff1a; #include<myhead.h> int main(int argc, const char *argv[]) {if(argc!3)…

GROUP_CONCAT报错解决

有如下表 其中awardee和awardee_unit都是保存的json类型的字符串, awardee是多个人员id, awardee_unit是部门的全路径 查询时要注意转换 需要将name拼接起来合并成一行,直接 GROUP_CONCAT 会报错 百度的大部分答案是修改数据库配置去掉严格模式,如果不方便修改数据库可以这样…

kubernetes(三)

文章目录 1. k8s弹性伸缩1.1 安装heapster监控1.2 弹性伸缩使用和验证 2. 持久化存储2.1 emptyDir 1. k8s弹性伸缩 k8s弹性伸缩&#xff0c;需要附加插件heapster 1.1 安装heapster监控 使用heapster(低版本)可以监控pod压力大不大 使用hpa调节pod数量&#xff0c;自动扩容或…

应用案例——音箱系统的芯片组成

人类的语言交流是人类交互的主要方式&#xff0c;从键盘&#xff0c;鼠标&#xff0c;触屏控制&#xff0c;人机交互的本质始终没有改变&#xff0c;无法完成对话式的交流&#xff0c;而语音交互的出现打破了这一现状&#xff0c;它可以解放双手&#xff0c;甚至解放双眼&#…

双碳目标---碳储量、碳收支、碳循环

以全球变暖为主要特征的气候变化已成为全球性环境问题&#xff0c;对全球可持续发展带来严峻挑战。2015年多国在《巴黎协定》上明确提出缔约方应尽快实现碳达峰和碳中和目标。2019年第49届 IPCC全会明确增加了基于卫星遥感的排放清单校验方法。随着碳中和目标以及全球碳盘点的现…