Vue中下拉刷新和上拉加载更多 发表于 2021-08-01 | 分类于 vue Vue中下拉刷新和上拉加载更多 下拉刷新ZDropDownRefresh.vue 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262<template lang="html"> <div class="refresh-moudle" @touchstart="touchStart($event)" @touchmove="touchMove($event)" @touchend="touchEnd($event)" ref="myrefresh" :style="{ transform: 'translate3d(0,' + top + 'px, 0)' }" > <header class="pull-refresh"> <slot name="pull-refresh"> <div class="down-tip" v-if="dropDownState === 1 || dropDownState === 2"> <i ref="icon" class="css-icon icon-upward" :class="{ active: dropDownState === 2 }" ></i> <span class="down-text" v-if="dropDownState === 1">{{ dropDownInfo.downText }}</span> <span class="down-text" v-if="dropDownState === 2">{{ dropDownInfo.upText }}</span> </div> <div class="refresh-tip" v-if="dropDownState === 3"> <span class="refresh-text">{{ dropDownInfo.refreshText }}</span> </div> </slot> </header> <slot></slot> </div></template><script>export default { props: { onRefresh: { type: Function, required: false, }, }, data() { return { defaultOffset: 40, // 默认高度, 相应的修改.releshMoudle的margin-top和.down-tip, .up-tip, .refresh-tip的height top: 0, scrollIsToTop: 0, startY: 0, isDropDown: false, // 是否下拉 isRefreshing: false, // 是否正在刷新 isDropInTop: false, //开始下拉时是否在滚动条已在最上面 dropDownState: 1, // 显示1:下拉可以刷新, 2:松开立即刷新, 3:正在刷新数据中... dropDownInfo: { downText: "下拉可以刷新", upText: "松开立即刷新", refreshText: "正在刷新数据...", refreshImg: "loading.png", }, }; }, created() { if (document.querySelector(".down-tip")) { // 获取不同手机的物理像素(dpr),以便适配rem this.defaultOffset = document.querySelector(".down-tip").clientHeight || this.defaultOffset; } }, methods: { /** * 触摸开始,手指点击屏幕时 * @param {object} e Touch 对象包含的属性 */ touchStart(e) { let dom = this.$refs["myrefresh"]; this.scrollIsToTop = dom.scrollTop || window.pageYOffset || document.body.scrollTop; // safari 获取scrollTop用window.pageYOffset if (this.scrollIsToTop === 0) { this.startY = e.targetTouches[0].pageY; this.isDropInTop = true; } else { this.isDropInTop = false; } }, /** * 接触点改变,滑动时 * @param {object} e Touch 对象包含的属性 */ touchMove(e) { if (this.isDropInTop) { if (e.targetTouches[0].pageY > this.startY) { // 下拉 this.isDropDown = true; if (!this.isRefreshing) { // 拉动的距离 let diff = e.targetTouches[0].pageY - this.startY - this.scrollIsToTop; this.top = Math.pow(diff, 0.8) + (this.dropDownState === 3 ? this.defaultOffset : 0); if (this.top >= this.defaultOffset) { this.dropDownState = 2; e.preventDefault(); } else { this.dropDownState = 1; // 去掉会导致ios无法刷新 e.preventDefault(); } } } else { this.isDropDown = false; this.dropDownState = 1; } } }, /** * 触摸结束,手指离开屏幕时 */ touchEnd() { if (this.isDropInTop) { if (this.isDropDown && !this.isRefreshing) { if (this.top >= this.defaultOffset) { this.refresh(); this.isRefreshing = true; } else { // cancel refresh this.isRefreshing = false; this.isDropDown = false; this.dropDownState = 1; this.top = 0; } } } }, /** * 刷新 */ refresh() { this.dropDownState = 3; this.top = this.defaultOffset; setTimeout(() => { this.onRefresh(this.refreshDone); }, 300); }, /** * 刷新完成 */ refreshDone() { this.isRefreshing = false; this.isDropDown = false; this.dropDownState = 1; this.top = 0; }, },};</script><!-- Add "scoped" attribute to limit CSS to this component only --><style scoped>.refresh-moudle { width: 100%; height: calc(100% + 40px); margin-top: -40px; -webkit-overflow-scrolling: touch; /* ios5+ */ overflow-y: scroll;}.pull-refresh { width: 100%; color: #999; height: 40px; transition-duration: 200ms; font-size: 18px;}.refresh-moudle .down-tip,.up-tip,.refresh-tip { display: flex; align-items: center; justify-content: center; height: 40px;}.css-icon { display: inline-block; height: 1em; width: 1em; font-size: 20px; margin-right: 10px; box-sizing: border-box; text-indent: -9999px; vertical-align: middle; position: relative;}.css-icon::before,.css-icon::after { content: ""; box-sizing: inherit; position: absolute; left: 50%; top: 50%; -ms-transform: translate(-50%, -50%); transform: translate(-50%, -50%);}.icon-upward::before { height: 0.65em; width: 0.65em; border-style: solid; border-width: 2px 0 0 2px; -ms-transform: translate(-50%, -50%) rotate(45deg); transform: translate(-50%, -50%) rotate(45deg);}.icon-upward::after { height: 0.8em; border-left: 2px solid; top: 55%;}.icon-upward.active { transform: rotate(180deg); transition: transform 0.3s;}@keyframes anticlockwise { 0% { transform: rotate(-180deg); } 100% { transform: rotate(0deg); }}@keyframes clockwise { 0% { transform: rotate(0deg); } 100% { transform: rotate(-180deg); }}.refresh-img { width: 35px; height: 35px; margin-right: 15px; animation: rotating 1.5s linear infinite;}@keyframes rotating { 0% { transform: rotate(0deg); } 100% { transform: rotate(1turn); }}</style> 使用方式12345678910111213141516171819202122232425262728293031323334353637383940<template> <div class="home"> <div class="msg_list"> <z-refresh :onRefresh="onRefresh"> <div class="msg_item" @click="notice_click"> <div class="msg_top">11111</div> <div class="msg_info"> 123456 </div> <div class="msg_time">2021-08-28</div> </div> <div class="loadmore" @click="onLoadMore">点击加载更多</div> </z-refresh> </div> </div></template><script>import ZDropDownRefresh from "../components/ZDropDownRefresh";export default { components: { "z-refresh": ZDropDownRefresh, }, methods: { /** * 下拉刷新 */ onRefresh(done) { // 如果下拉刷新和上拉加载同时使用,下拉时初始化上拉的数据 console.info("下拉刷新"); done(); }, onLoadMore() { console.info("加载更多"); }, },};</script>