前言
混合开发是我们经常使用的开发模式,快节奏的时代,app的发版流程缓慢为h5带来了更多的可能,带在体验上来说,h5还是有很长的路要走。那么今天我们就来看一个场景,下拉刷新操作,在app里面可以说是很常见的功能,但在h5中经常被忽略,那么今天就手写一个h5下拉刷新,让h5体验距离app更近一步。
git仓库
操作
用户下拉 |---->> 无效下拉 ---->> 自动回弹
用户下拉 |---->> 有效下拉 ---->> 触发回调方法(刷接口/刷页面)---->> 自动回弹
实现
主要通过监听touch事件获取手指滑动轨迹,并实时改变 translate 达到页面滑动效果,增加阻尼概念让下拉操作更加真实,结束时判断是否有效下拉来执行对应方法
/**
* @param {Function}pullCallback
* 下拉回调,不传默认为 reload();
* 有效下拉:指页面被下拉至文档顶部离开视口顶部
* 暂不支持 微信等 q系浏览器
* @使用方法
* const pullObj = pullRefresh(() => {
* // 刷数据接口
* // 刷新完成之后调用 pullObj.finish(); 顶部刷新区归位
* });
* pullObj.init(); 初始化
*/
export function pullRefresh(pullCallback) {
const body = document.body;
const app = document.getElementById('app');
const app_style_cache = app.style;
const pullTextEl = document.createElement('p');
pullTextEl.style = "width: 100%; text-align: center; position: fixed; z-index: -1; left: 0; top: 0; font-size: 12px; line-height: 40px; color: #999; display: none;";
const loadText = document.createElement('span');
// 是否执行有效下拉回调标志位
let refreshStatus = false;
const isQBrowser = () => { // qq系浏览器
const ua = navigator.userAgent.toLowerCase();
return !!ua.match(/mqqbrowser|qzone|qqbrowser/i);
};
const finish = () => {
if(isQBrowser()) return;
loadText.innerText = '刷新成功';
setTimeout(() => {
app.style['transition'] = 'transform 0.2s';
app.style['transform'] = 'translate(0, 0)';
setTimeout(() => {
pullTextEl.style['display'] = 'none';
app.style = app_style_cache;
}, 400);
}, 500);
};
const init = () => {
if(isQBrowser()) return;
body.appendChild(pullTextEl);
pullTextEl.appendChild(loadText);
let startP, moveLen;
// 默认有效下拉回调
const _pullCallback = () => {
window.location.reload();
};
// 下拉处理
const _pullHandler = (moveLen) => {
// 下拉元素距离视口顶部距离
const top_distance = app.getBoundingClientRect().top;
// 有效下拉才做处理
if(top_distance >= 0) {
if(pullTextEl.style['display'] === 'none') {
// 有效下拉时才展示提示文案
pullTextEl.style['display'] = 'block';
}
// 下拉效果
if(moveLen > 0 && moveLen < 50){
app.style['transform'] = 'translate(0, ' + moveLen + 'px)';
} else if(moveLen >= 50 && moveLen < 100) { // 到刷新标志,下拉阻尼增大
const _moveLen = 50 + (moveLen - 50) * 0.6;
app.style['transform'] = 'translate(0, ' + _moveLen + 'px)';
} else if(moveLen >= 100) { // 下拉超过 100,下拉阻尼再次增大
const _moveLen = 80 + (moveLen - 100) * 0.2;
app.style['transform'] = 'translate(0, ' + _moveLen + 'px)';
}
// 下拉触发
if(top_distance < 50) {
loadText.innerText = '下拉即可刷新...';
refreshStatus = false;
} else {
loadText.innerText = '松开立即刷新...';
refreshStatus = true;
}
}
};
app.addEventListener('touchstart', (e) => {
startP = e.touches[0].pageY;
app.style['transition'] = 'transform 0s';
});
app.addEventListener('touchmove', (e) => {
moveLen = e.touches[0].pageY - startP;
_pullHandler(moveLen);
});
app.addEventListener('touchend', (e) => {
// 下拉元素距离视口顶部距离
const top_distance = app.getBoundingClientRect().top;
if(top_distance > 0) { // 当有效下拉发生后动画归位,重置样式
if(refreshStatus) {
loadText.innerText = '数据加载中...';
pullCallback ? pullCallback() : _pullCallback();
app.style['transition'] = 'transform 0.4s';
app.style['transform'] = 'translate(0, 40px)';
} else {
app.style['transition'] = 'transform 0.4s';
app.style['transform'] = 'translate(0, 0)';
setTimeout(() => {
pullTextEl.style['display'] = 'none';
app.style = app_style_cache;
}, 400);
}
} else { // 未发生有效下拉的直接重置样式
pullTextEl.style['display'] = 'none';
app.style = app_style_cache;
}
})
};
return {
init,
finish
}
}
如何使用
es6 下直接 import 引用 pullRefresh_es6.js 文件即可
其他情况可引用 pullRefresh.js 或 pullRefresh.min.js 文件,直接使用 window 全局变量 pullRefresh,或通过 require 方式引用上述文件
具体使用:
配置
var pullObj = pullRefresh(function() {
// 下拉回调处理 your code
handler();
// 结束
pullObj.finish();
});
初始化
pullObj.init();
结语
用户体验是每一位前端人都应该注意的,我们是距离用户最近的一层,每一个细节,每一次交互,都会影响着用户对我们自己产品的态度,想要得到用户,就得给有’看似扭捏,实则拿捏‘的实力和态度
致敬每一位前端coder;Respect;