实现vue加载指令 v-loading

文章目录

    • 为什么使用指令实现 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);
    	}
    }
    

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于SSM的班级事务管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用Vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

未来已来,Ai原生应用与人高度结合!学习就在现在?

原生应用&#xff1a;OpenAI™ChatGPT、Baidu.Inc™文心一言 也可以体验CSDN的INSCODE AI&#xff0c;集成多个国内GPT内容。 文章目录 前言----编程语言的未来&#xff1f;一、编程语言的教育1.1 学校所见所闻1.2 开启我们的Ai行程~io&#xff01;1.3 Ai结果评论 二、Ai编程教…

生成式AI:软件工程的未来伙伴

随着技术不断进步&#xff0c;软件工程正在经历一场革命性的变革。从最初的穿孔卡片和汇编语言编程&#xff0c;到现代集成开发环境和高级编程语言&#xff0c;软件工程已经走过了一条漫长的路。现在&#xff0c;生成式人工智能(AI)正打开新的篇章&#xff0c;不仅对传统的编码…

阿里云ECS服务器无法访问端口(防火墙在关闭状态也启作用)

问题&#xff1a;一直用得好好的端口&#xff0c;突然在某一时间不可以访问这个端口了 &#xff0c;在服务器录入外网地址访问如下图&#xff1a; 先按正常流程检测&#xff1a; 1 先云服务商的管理网站查看防火墙端口是否开放 看了正常开放了端口&#xff0c;如下图&#xff…

常见位运算总结

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 目录 前言 1.基础位运算 &运算 |运算 ^运算 >>运算 <<运算 ~运算 2.给一个数n&#xff0c;确定他的二进制表示中的第x位&#xff0c;是0还是1 3.将一个数n的二进制表示的第x位修改成1 4.将一个数…

jupyter更改默认路径到其它的目录或者到其它的盘 比如D盘

1.打开终端 输入jupyter notebook --generate-config 如下 2.在C:\Users\mb5958\.jupyter路径下 3.用记事本打开它&#xff0c;搜索directory 4.在你想要的路径下新建一个文件夹&#xff0c;如‘D:\jupyterFile’&#xff0c;然后将路径名放在c.NotebookApp.notebook_dir"…

深入了解 Vite:快速、简洁、高效的前端构建工具(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

HackTheBox - Medium - Linux - BroScience

BroScience BroScience 是一款中等难度的 Linux 机器&#xff0c;其特点是 Web 应用程序容易受到“LFI”的攻击。通过读取目标上的任意文件的能力&#xff0c;攻击者可以深入了解帐户激活码的生成方式&#xff0c;从而能够创建一组可能有效的令牌来激活新创建的帐户。登录后&a…

鸿蒙原生应用/元服务开发-Serverless账户验证码的问题

在应用/元服务早期使用过程中&#xff0c;-Serverless账户验证码的格式是[AGC][应用/元服务名称]&#xff0c;如下图。 但是&#xff0c;在最近&#xff0c;[应用/元服务]名称直接变成了【default】,用户收到这种验证码后&#xff0c;心里存有疑虑的&#xff0c;这是哪里配置…

【Spring】16 ApplicationContextAware 接口

文章目录 1. 简介2. 作用3. 使用3.1 创建并实现接口3.2 配置 Bean 信息3.3 创建启动类3.4 启动 4. 应用场景总结 Spring 框架提供了许多回调接口&#xff0c;用于在 Bean 的生命周期中执行特定的操作。ApplicationContextAware 接口是其中之一&#xff0c;它允许 Bean 获取对 A…

Python-1-字符串类型及方法

众所周知&#xff0c;Python面向对象&#xff0c;功能强大 | ू•ૅω•́)ᵎᵎᵎ

常见的并查集题目

总结 并查集逻辑实现的优化有两种&#xff0c;第一种是查找时路径压缩&#xff0c;第二种是按秩合并&#xff0c;合并时将高度较小的树作为较高树的子树,从代码量来看&#xff0c;推荐使用路径压缩&#xff0c;可以参考lc 547. 省份数量的两种UnionFind写法 题目 1 LC990. 等…

气膜篮球馆——智能场馆助力篮球梦想

篮球&#xff0c;作为青少年热爱的运动之一&#xff0c;不仅锻炼身体、塑造良好体态&#xff0c;更为结交朋友提供了丰富机会。如今&#xff0c;随着气膜篮球馆的兴起&#xff0c;这一运动在智能场馆中展现出更为舒适的一面&#xff0c;为篮球梦想的实现提供了强大助力。 随着人…

c语言:用结构体求平均分|练习题

一、题目 用c语言的结构体&#xff0c;求4位学生成绩的平均分 如图&#xff1a; 二、代码截图【带注释】 三、源代码【带注释】 #include <stdio.h> float aver();//声明平均分函数 void printScore();//声明打印函数 //设置结构体&#xff0c; struct student { …

【Linux Shell】4. 数组

文章目录 【 1. 数组的定义 】【 2. 读取数组 】【 3. 关联数组 】【 4. 获取数组中的所有元素 】【 5. 获取数组的长度 】 数组中可以存放多个值。 Bash Shell 只支持一维数组&#xff08;不支持多维数组&#xff09;&#xff0c;初始化时不需要定义数组大小。与大部分编程语言…

linux释放交换空间-Swap

确保剩余内存比swap内存空间大&#xff0c;再执行以下操作&#xff0c;否则会宕机&#xff01; 查看swap分区 swapon -s 会查看到你的swap分区位置 停止swap分区 停止swap分区是将swap内存释放到实际内存中 swapoff /dev/dm-1开启swap分区 swap分区内存成功释放到实际内…

年末汇总⭐️ 我是如何从学生切换到职场人身份的

目录 今日天气 阴 温度较低 一、Learning 二、Working 三、Living 章末 今日天气 阴 温度较低 小伙伴们大家好&#xff0c;冬已至 年将末 身为逮虾户的我看到大家的年末总结心中也不由得涌起一股创作热情&#xff0c;奈何没文化&#xff0c;只能按照…

linux中最常用的目录导航命令

文章目录 Linux中最常用的目录导航命令探索未知世界的cd进入刚才的目录快速返回家目录进入某用户的家目录结合CDPATH的妙用!$用shopt –s cdspell自动纠正cd命令的目录名输入错误 最常用的且没有之一的 ls命令格式不加任何参数使用-l显示更多细节使用-t按照时间排序使用-r按照时…

JAVA的引用与C++的指针有什么区别

JAVA的引用与C的指针有什么区别 1. Java值类型与引用类型1.1 变量初始化1.2 变量赋值1.3 函数传参 2. Java数据存储方式2.1 Java局部变量&&Java方法参数2.2 Java数组类型引用和对象2.3 String类型数据 3. Java引用类型3.1 强引用3.2 软引用3.3 弱引用3.4 虚引用 4. JAV…

关于TypeScript Interface你需要知道的10件事

TypeScript接口的10种使用场景——可能只有20%的web开发人员完全掌握它们 TypeScript中的接口是一个非常灵活的概念。除了抽象类的部分行为外&#xff0c;它还经常用于描述“对象的形状”。 必需的属性 在定义接口时&#xff0c;需要使用 interface 关键字: interface Use…