基于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>
相关推荐
IT_陈寒4 分钟前
Redis批量删除的大坑,差点让我加班到天亮
前端·人工智能·后端
帆张芳显18 分钟前
智表ZCELL产品V3.6 版发布,新增系统预置右键菜单操作、页签栏操作等功能
前端·canva可画·excel插件
漂流瓶jz24 分钟前
运行时vs编译时:CSS in JS四种主流方案介绍和对比
前端·javascript·css
Asmewill31 分钟前
uv包管理命令
前端
发现一只大呆瓜32 分钟前
深入浅出 Tree Shaking:Rollup 是如何“摇”掉死代码的?
前端·性能优化·vite
weixin199701080161 小时前
《转转商品详情页前端性能优化实战》
前端·性能优化
钮钴禄·爱因斯晨1 小时前
他到底喜欢我吗?赛博塔罗Java+前端实现,一键解答!
java·开发语言·前端·javascript·css·html
Watermelo6171 小时前
理解 JavaScript 中的“ / ”:路径、资源与目录、nginx配置、请求、转义的那些事
前端·javascript·vue.js·chrome·nginx·正则表达式·seo
Beingchou2 小时前
HTML头部元信息避坑指南大纲
前端·html
jserTang2 小时前
手撕 Claude Code-4: TodoWrite 与任务系统
前端·javascript·后端