基于HTML5的下拉刷新效果

基于HTML5的下拉刷新效果

效果示例图

示例代码

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width,initial-scale=1.0" />
		<title></title>
		<style type="text/css">
			* {
				padding: 0;
				margin: 0;
				box-sizing: border-box;
			}

			.main {
				width: 100%;
				height: 100vh;
				background-color: #f5f5f5;
				position: relative;
			}

			.dot-wrap {
				width: 100%;
				height: 30px;
				position: absolute;
				background-color: #fff;
				top: -30px;
			}

			.dot {
				position: absolute;
				width: 10px;
				height: 10px;
				border-radius: 10px;
				background-color: rgb(0, 0, 0, 0.3);
				top: 0;
				left: 50%;
				margin-left: -5px;
			}

			.dot:last-child {
				display: none;
			}

			.dot.loading {
				display: unset;
				animation: dot-move 1.5s infinite;
			}

			@keyframes dot-move {
				0% {
					transform: translateX(-20px);
				}

				33.33% {
					transform: translateX(-20px);
				}

				33.34% {
					transform: translateX(0px);
				}

				66.66% {
					transform: translateX(0px);
				}

				66.67% {
					transform: translateX(20px);
				}

				100% {
					transform: translateX(20px);
				}
			}

			.block-item {
				width: 100%;
				height: 200px;
				background-color: red;
				margin-bottom: 12px;
			}
		</style>
	</head>
	<body>


		<div class="main">
			<div class="dot-wrap">
				<div class="dot"></div>
				<div class="dot"></div>
				<div class="dot"></div>
				<div class="dot"></div>
			</div>

			<div class="block-item"></div>
			<div class="block-item"></div>
			<div class="block-item"></div>
			<div class="block-item"></div>
			<div class="block-item"></div>
			<div class="block-item"></div>
			<div class="block-item"></div>
			<div class="block-item"></div>
			<div class="block-item"></div>
			<div class="block-item"></div>
			<div class="block-item"></div>
			<div class="block-item"></div>
		</div>
	</body>
	<script type="text/javascript">
		let timerHandle = null;
		const mainDom = document.querySelector(".main");
		const dots = document.querySelectorAll(".dot");
		const rootDocument = document.documentElement;
		//当滚动条在最顶部时可以下拉刷新
		if (rootDocument.scrollTop === 0) {
			addTouchEvent();
		}

		//滚动监听
		window.onscroll = function() {
			//当滚动条到达顶部时,可以触发下拉拖动事件
			if (rootDocument.scrollTop <= 0) {
				rootDocument.scrollTop = 0;
				addTouchEvent();
			} else {
				removeTouchEvent();
			}
		}

		let distance, ox, oy, x, y = 0;

		function startEvent(e) {
			console.log("[start]", e)
			//获取手指起始值
			ox = e.touches[0].clientX;
			oy = e.touches[0].clientY;
		}

		function moveEvent(e) {
			if (!e.cancelable) {
				return; // 如果事件不可取消,直接返回,避免报错
			}
			//获取手指移动值
			x = e.touches[0].clientX;
			y = e.touches[0].clientY;

			//判断下拉意图
			if (y - oy > 0) {
				const deg = Math.atan(Math.abs(x - ox) / Math.abs(y - oy)) / Math.PI * 180;
				if (deg > 40) {
					ox = x;
					oy = y;
					return false;
				}
			} else {
				removeTouchEvent();
				if (rootDocument.scrollTop === 0) {
					addTouchEvent();
					return false;
				}
			}

			//手指移动的距离
			distance = y - oy;

			//添加阻尼效果
			let percent = (100 - distance * 0.5) / 100;
			percent = percent < 0.5 ? 0.5 : percent;

			distance = distance * percent

			//设置页面偏移距离
			mainDom.style.transform = `translateY(${distance}px)`
			console.log("[move]", distance)
			if (distance > 20 && distance <= 50) {
				dots[0].style.transform = `translateX(-${(distance-20)*2/3}px)`;
				dots[2].style.transform = `translateX(${(distance-20)*2/3}px)`;
			} else if (distance > 50) {
				dots[0].style.transform = `translateX(-20px)`;
				dots[2].style.transform = `translateX(20px)`;
			}

			e.preventDefault();
		}

		function endEvent() {
			//当拖动的相对值大于50时
			if (distance >= 50) {
				//可以触发下拉刷新
				dots[3].classList.add('loading')
				mainDom.style.transition = `all 0.2s`;
				mainDom.style.transform = `translateY(50px)`
				if (timerHandle) {
					clearTimeout(timerHandle)
				}
				timerHandle = setTimeout(() => {
					if (timerHandle) {
						clearTimeout(timerHandle)
					}
					removeEffect()
				}, 2000);
			} else {
				removeEffect()
			}

		}

		function removeEffect() {
			//清除过渡效果
			mainDom.style.transition = `all 0.2s`;
			mainDom.style = '';
			dots[3].classList.remove('loading')
		}


		function addTouchEvent() {
			/**
			 * 通过将`passive`设置为`false`,表示在触发`touchmove`事件时,事件处理函数可以调用`preventDefault()`来阻止默认行为。
			 * 这意味着在移动过程中,可以阻止浏览器执行默认的滚动行为。
			 */
			mainDom.addEventListener('touchstart', startEvent, {
				passive: false
			});
			mainDom.addEventListener("touchmove", moveEvent, {
				passive: false
			})
			mainDom.addEventListener('touchend', endEvent, {
				passive: false
			})
		}

		function removeTouchEvent() {
			mainDom.removeEventListener('touchstart', startEvent);
			mainDom.removeEventListener("touchmove", moveEvent)
			mainDom.removeEventListener('touchend', endEvent)
		}
	</script>
</html>
相关推荐
檀越剑指大厂9 分钟前
【Python系列】异步 Web 服务器
服务器·前端·python
我是Superman丶11 分钟前
【前端】js vue 屏蔽BackSpace键删除键导致页面后退的方法
开发语言·前端·javascript
Hello Dam13 分钟前
基于 Spring Boot 实现图片的服务器本地存储及前端回显
服务器·前端·spring boot
小仓桑14 分钟前
利用 Vue 组合式 API 与 requestAnimationFrame 优化大量元素渲染
前端·javascript·vue.js
Hacker_xingchen15 分钟前
Web 学习笔记 - 网络安全
前端·笔记·学习
天海奈奈16 分钟前
前端应用界面的展示与优化(记录)
前端
多多*37 分钟前
后端并发编程操作简述 Java高并发程序设计 六类并发容器 七种线程池 四种阻塞队列
java·开发语言·前端·数据结构·算法·状态模式
mubeibeinv1 小时前
列表代码思路
前端
过期的H2O21 小时前
【H2O2|全栈】JS进阶知识(十)ES6(6)
开发语言·前端·javascript·ecmascript·es6
White graces1 小时前
Spring MVC练习(前后端分离开发实例)
java·开发语言·前端·后端·spring·java-ee·mvc