DCloud-MUI 下拉刷新与加载更多
下拉刷新
单WebView
实现原理
下拉刷新,触发的是原生下拉刷新控件,而整个webview位置不会发生变化,所以不会在拖动过程中发生DOM重绘,当控件拖动到一定位置触发动态加载数据以及刷新操作。此模式下拉刷新,相比双webview 模式,不创建额外 webview,性能更优。
单webview下拉刷新组件使用
优点:
性能更优,体现在两点:
相比双webview,不创建额外子 webview 性能消耗更少
下拉拖动过程中不会发生重绘,也减少了性能消耗
缺点:
仅支持‘cricle’样式以及该样式的颜色自定义
使用示例
S1.DOM中加入MUI下拉刷新布局组件
<!--下拉刷新容器--> <div id="pullrefresh" class="mui-content mui-scroll-wrapper"> <div class="mui-scroll"> <!--数据列表--> <ul class="mui-table-view mui-table-view-chevron"></ul> </div> </div> |
S2.JS代码中进行初始化配置
mui.init({ pullRefresh : { container:"#refreshContainer",//下拉刷新容器标识,querySelector能定位的css选择器均可,比如:id、.class等 down : { style:'circle',//必选,下拉刷新样式,目前支持原生5+ ‘circle’ 样式 color:'#2BD009', //可选,默认“#2BD009” 下拉刷新控件颜色 height:'50px',//可选,默认50px.下拉刷新控件的高度, range:'100px', //可选 默认100px,控件可下拉拖拽的范围 offset:'0px', //可选 默认0px,下拉刷新控件的起始位置 auto: true,//可选,默认false.首次加载自动上拉刷新一次 callback :pullfresh-function //必选,刷新函数,根据具体业务来编写,比如通过ajax从服务器获取新数据; } } }); |
下拉刷新主要API
N1.启用与禁用下拉刷新-官方已删除描述
单/双WebView模式使用方法类似,可以通过代码动态设置下拉刷新的启用与禁用。禁用下拉刷新后,下拉容器不会再对下拉手势作出相应,并且不会有下拉回弹效果。
// 禁用 mui('#refreshContainer').pullRefresh().disablePulldownToRefresh(); //启用 mui('#refreshContainer').pullRefresh().enablePulldownToRefresh(); |
N2.下拉刷新结束
单/双WebView模式使用方法类似,可以通过代码动态设置下拉刷新的启用与禁用。禁用下拉刷新后,下拉容器不会再对下拉手势作出相应,并且不会有下拉回弹效果。
mui('#pullrefresh').pullRefresh().endPulldownToRefresh(); |
双WebView
实现原理
webview 模式的下拉刷新,创建一个子 webview 添加列表;拖动时,拖动的是一个完整的 webview,避免了类似 DIV 拖动流畅度不好的问题,回弹动画使用原生动画。
模式说明:
优点:
可自定义下拉刷新样式,更改文字等等.参考关于自定义下拉刷新样式问答
缺点:
相比单 webview,性能消耗更大,不过都比 div 模式的要好用
DOM结构需要统一配置
双WebView模式使用
双WebView模式与单WebView模式使用差别主要在配置参数的不同。
双WebView模式配置参数:
mui.init({ pullRefresh : { container:"#refreshContainer",//下拉刷新容器标识,querySelector能定位的css选择器均可,比如:id、.class等 down : { height:50,//可选,默认50.触发下拉刷新拖动距离, auto: true,//可选,默认false.首次加载自动下拉刷新一次 contentdown : "下拉可以刷新",//可选,在下拉可刷新状态时,下拉刷新控件上显示的标题内容 contentover : "释放立即刷新",//可选,在释放可刷新状态时,下拉刷新控件上显示的标题内容 contentrefresh : "正在刷新...",//可选,正在刷新状态时,下拉刷新控件上显示的标题内容 callback :pullfresh-function //必选,刷新函数,根据具体业务来编写,比如通过ajax从服务器获取新数据; } } }); |
参考:
http://dev.dcloud.net.cn/mui/pulldown/
上拉加载
实现功能
页面滚动到底,显示“正在加载...”提示(mui框架提供)
执行加载业务数据逻辑(开发者提供)
加载完毕,隐藏"正在加载"提示(mui框架提供)
使用示例
S1.DOM中加入MUI下拉刷新布局组件
<!--下拉刷新容器--> <div id="pullrefresh" class="mui-content mui-scroll-wrapper"> <div class="mui-scroll"> <!--数据列表--> <ul class="mui-table-view mui-table-view-chevron"></ul> </div> </div> |
S2.JS代码中进行初始化配置
mui.init({ pullRefresh : { container:refreshContainer,//待刷新区域标识,querySelector能定位的css选择器均可,比如:id、.class等 up : { height:50,//可选.默认50.触发上拉加载拖动距离 auto:true,//可选,默认false.自动上拉加载一次 contentrefresh : "正在加载...",//可选,正在加载状态时,上拉加载控件上显示的标题内容 contentnomore:'没有更多数据了',//可选,请求完毕若没有更多数据时显示的提醒内容; callback :pullfresh-function //必选,刷新函数,根据具体业务来编写,比如通过ajax从服务器获取新数据; } } }); |
下拉刷新主要API
N1. 结束上拉加载
加载完新数据后,需要执行endPullupToRefresh()方法,结束转雪花进度条的“正在加载...”。
// endMore // true 没有更多了 // false 显示更多 mui('#pullrefresh').pullRefresh().endPullupToRefresh(endMore); // 重置上拉加载 // 调用endPullupToRefresh(true)后需要重新启用加载更多需要重载上拉加载 mui('#pullrefresh').pullRefresh().refresh(true); |
N2.禁用/启用上拉加载
在部分场景下希望禁用上拉加载,比如在列表数据过少时,不想显示“上拉显示更多”、“没有更多数据”的提示语,开发者可以通过调用disablePullupToRefresh()方法实现类似需求。
// 禁用 mui('#pullrefresh'').pullRefresh().disablePullupToRefresh(); // 启用 mui('#pullrefresh'').pullRefresh().enablePullupToRefresh(); |
N3.自动上拉加载
调用后触发上拉加载更多。
// Timeout延时必须 setTimeout(function() { mui('#pullrefresh').pullRefresh().pullupLoading(); },50); |
单WebView完整示例
<!DOCTYPE html> <html style="background: white;"> <head> <meta charset="utf-8"> <title>Hello MUI</title> <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <link rel="stylesheet" href="../../../css/mui.css"> <script src="../../../js/mui.js"></script> <style> /* list item style */ .item-main-container { width: 100%; height: 150px; } .item-img { width: 100%; height: 100px; } .item-text { width: 100%; height: 50px; text-align: center; } body { text-align: center; } /* empty view */ .list-empty-container { display: none; width: 100%; } .list-empty-img { width: 100%; } .list-empty-btn { width: 100px; margin: 0 auto; margin-top: 15px; padding: 3px; border-radius: 18px; font-size: 18px; align-content: center; } </style> </head> <body style="background: white;"> <!--下拉刷新容器--> <div id="pullrefresh" class="mui-content mui-scroll-wrapper" style="background: white;"> <div class="mui-scroll"> <!--数据列表--> <ul class="mui-table-view mui-table-view-chevron"> </ul> </div> </div> <!-- list空 --> <div id="listEmptyElm" class="list-empty-container"> <img class="list-empty-img" src="../../../images/img/list_empty.png" /> <button class="list-empty-btn">快去逛逛</button> </div> <script> var curPage = 1; /** * 下拉刷新使用场景 * E1.下拉刷新加载第一页,上拉加载更多直到最后一页; * E2.页面开始自动下拉/上拉一次; * E3.无数据页面展示; * */ mui.init({ pullRefresh: { container: '#pullrefresh', // 双WebView // down: { // height: 50, // 可选,默认50.触发下拉刷新拖动距离, // // 不起作用 // // auto: true, // 可选,默认false.首次加载自动下拉刷新一次 // contentdown: "下拉可以刷新", // 可选,在下拉可刷新状态时,下拉刷新控件上显示的标题内容 // contentover: "释放立即刷新", // 可选,在释放可刷新状态时,下拉刷新控件上显示的标题内容 // contentrefresh: "正在刷新...", // 可选,正在刷新状态时,下拉刷新控件上显示的标题内容 // callback: pulldownRefresh // }, // 单WebView down: { style: 'circle', // 必选,下拉刷新样式,目前支持原生5+ ‘circle’ 样式 color: '#2BD009', // 可选,默认“#2BD009” 下拉刷新控件颜色 height: '50px', // 可选,默认50px.下拉刷新控件的高度, range: '100px', // 可选 默认100px,控件可下拉拖拽的范围 offset: '0px', // 可选 默认0px,下拉刷新控件的起始位置 //auto: true, // 可选,默认false.首次加载自动上拉刷新一次 callback: pulldownRefresh //必选,刷新函数,根据具体业务来编写,比如通过ajax从服务器获取新数据; }, up: { auto: false, // 可选,默认false.true首次加载自动上拉刷新一次 contentrefresh: '正在加载...', callback: pullupRefresh } } }); mui.plusReady(function(){ // 代码实现首次自动加载 // Timeout延时必须 setTimeout(function() { mui('#pullrefresh').pullRefresh().pullupLoading(); },50); }); /** * 下拉刷新具体业务实现 */ function pulldownRefresh() { curPage = 1; loadPageData(); } var isFirst = true; /** * 上拉加载具体业务实现 */ function pullupRefresh() { if(isFirst) { // 首次加载第一页 curPage = 1; isFirst = false; } loadPageData(); } // if (mui.os.plus) { // mui.plusReady(function() { // setTimeout(function() { // mui('#pullrefresh').pullRefresh().pullupLoading(); // }, 10); // // }); // } else { // mui.ready(function() { // mui('#pullrefresh').pullRefresh().pullupLoading(); // }); // } /** * 页面数据加载 * 解耦页面事件 */ function loadPageData() { getJsonData("url", function(res) { console.log(res.data); parseListData(res.data); curPage++; }, function(err) { console.log(err); // endPullRefresh(false); }); } /** * 结束上/下拉刷新 */ function endPullRefresh(endMore) { mui('#pullrefresh').pullRefresh().endPullupToRefresh(endMore); if(!endMore) { mui('#pullrefresh').pullRefresh().refresh(true); } if(curPage == 1) { mui('#pullrefresh').pullRefresh().endPulldownToRefresh(); //refresh completed } } /** * 数据解析 * @param {Object} params */ function parseListData(params) { var table = document.body.querySelector('.mui-table-view'); if(curPage == 1) { table.innerHTML = ""; var pullrefreshElm = document.querySelector("#pullrefresh"); var listEmptyElm = document.querySelector("#listEmptyElm"); pullrefreshElm.style.display = "block"; listEmptyElm.style.display = "none"; } if(curPage == 1 && params.size == 0) { var pullrefreshElm = document.querySelector("#pullrefresh"); var listEmptyElm = document.querySelector("#listEmptyElm"); pullrefreshElm.style.display = "none"; listEmptyElm.style.display = "block"; } else { for(var i = 0; i < params.list.length; i++) { var itemData = params.list[i]; var itemElm = document.createElement('div'); itemElm.className = "item-main-container"; itemElm.innerHTML = getLayoutElement(itemData.imgUrl, itemData.title); table.appendChild(itemElm); } } var endMore = false; if(params.pageSize != params.size) { endMore = true; } endPullRefresh(endMore); } /** * 获取布局元素 * @param {Object} imgUrl * @param {Object} title */ function getLayoutElement(imgUrl, title) { var elmHtml = ` <img class="item-img" src="holderImgUrl" /> <div class="item-text">holderTitle</div> `; elmHtml = elmHtml.replace("holderImgUrl", imgUrl); elmHtml = elmHtml.replace("holderTitle", title); return elmHtml; } var isEmptyList = true; /** * 数据请求 * @param {Object} url * @param {Object} sucCall * @param {Object} failCall */ function getJsonData(url, sucCall, failCall) { setTimeout(function() { var jsonObj = { "page": 1, "pageSize": 3, "size": curPage != 3 ? 3 : 1, list: curPage != 3 ? [{ title: "自动化测试程序", imgUrl: "https://www.baidu.com/img/bd_logo1.png" }, { title: "自动化测试程序", imgUrl: "https://www.baidu.com/img/bd_logo1.png" }, { title: "自动化测试程序", imgUrl: "https://www.baidu.com/img/bd_logo1.png" }] : [{ title: "自动化测试程序", imgUrl: "https://www.baidu.com/img/bd_logo1.png" }] } if(isEmptyList) { isEmptyList = false; jsonObj = { "page": 1, "pageSize": 3, "size": 0, list: [] } } var result = { data: jsonObj } sucCall(result); }, 1000); } </script> </body> </html>
双WebView完整示例
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <link rel="stylesheet" href="../../../css/mui.css"> <script src="../../../js/mui.js"></script> <style> .float-menu-container{ position: absolute; top: 15px; right: 15px; } .float-menu-btn { width: 100px; height: 50px; font-size: 25px; border-radius: 25px; } </style> </head> <body> <header class="mui-bar mui-bar-nav"> <a id="back" class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a> <h1 id="title" class="mui-title">上下拉刷新完整实例</h1> </header> </body> <script type="text/javascript"> //启用双击监听 mui.init({ gestureConfig:{ doubletap:true }, subpages:[{ url:'dw_sub_page.html', id:'dw_sub_page', styles:{ top: '45px', bottom: '0px', } }] }); </script> </html>
子页面
<!DOCTYPE html> <html style="background: white;"> <head> <meta charset="utf-8"> <title>Hello MUI</title> <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <link rel="stylesheet" href="../../../css/mui.css"> <script src="../../../js/mui.js"></script> <style> /* list item style */ .item-main-container { width: 100%; height: 150px; } .item-img { width: 100%; height: 100px; } .item-text { width: 100%; height: 50px; text-align: center; } body { text-align: center; } /* empty view */ .list-empty-container { display: none; width: 100%; } .list-empty-img { width: 100%; } .list-empty-btn { width: 100px; margin: 0 auto; margin-top: 15px; padding: 3px; border-radius: 18px; font-size: 18px; align-content: center; } </style> </head> <body style="background: white;"> <!--下拉刷新容器--> <div id="pullrefresh" class="mui-content mui-scroll-wrapper" style="background: white;"> <div class="mui-scroll"> <!--数据列表--> <ul class="mui-table-view mui-table-view-chevron"> </ul> </div> </div> <!-- list空 --> <div id="listEmptyElm" class="list-empty-container"> <img class="list-empty-img" src="../../../images/img/list_empty.png" /> <button class="list-empty-btn">快去逛逛</button> </div> <script> var curPage = 1; /** * 下拉刷新使用场景 * E1.下拉刷新加载第一页,上拉加载更多直到最后一页; * E2.页面开始自动下拉/上拉一次; * E3.无数据页面展示; * */ mui.init({ pullRefresh: { container: '#pullrefresh', // 双WebView down: { height: 50, // 可选,默认50.触发下拉刷新拖动距离, // 不起作用 // auto: true, // 可选,默认false.首次加载自动下拉刷新一次 contentdown: "下拉可以刷新", // 可选,在下拉可刷新状态时,下拉刷新控件上显示的标题内容 contentover: "释放立即刷新", // 可选,在释放可刷新状态时,下拉刷新控件上显示的标题内容 contentrefresh: "正在刷新...", // 可选,正在刷新状态时,下拉刷新控件上显示的标题内容 callback: pulldownRefresh }, // 单WebView // down: { // style: 'circle', // 必选,下拉刷新样式,目前支持原生5+ ‘circle’ 样式 // color: '#2BD009', // 可选,默认“#2BD009” 下拉刷新控件颜色 // height: '50px', // 可选,默认50px.下拉刷新控件的高度, // range: '100px', // 可选 默认100px,控件可下拉拖拽的范围 // offset: '0px', // 可选 默认0px,下拉刷新控件的起始位置 // auto: true, // 可选,默认false.首次加载自动上拉刷新一次 // callback: pulldownRefresh //必选,刷新函数,根据具体业务来编写,比如通过ajax从服务器获取新数据; // }, up: { auto: true, // 可选,默认false.true首次加载自动上拉刷新一次 contentrefresh: '正在加载...', callback: pullupRefresh } } }); /** * 下拉刷新具体业务实现 */ function pulldownRefresh() { curPage = 1; loadPageData(); } var isFirst = true; /** * 上拉加载具体业务实现 */ function pullupRefresh() { if(isFirst) { // 首次加载第一页 curPage = 1; isFirst = false; } loadPageData(); } // if (mui.os.plus) { // mui.plusReady(function() { // setTimeout(function() { // mui('#pullrefresh').pullRefresh().pullupLoading(); // }, 10); // // }); // } else { // mui.ready(function() { // mui('#pullrefresh').pullRefresh().pullupLoading(); // }); // } /** * 页面数据加载 * 解耦页面事件 */ function loadPageData() { getJsonData("url", function(res) { console.log(res.data); parseListData(res.data); curPage++; }, function(err) { console.log(err); // endPullRefresh(false); }); } /** * 结束上/下拉刷新 */ function endPullRefresh(endMore) { mui('#pullrefresh').pullRefresh().endPullupToRefresh(endMore); if(!endMore) { mui('#pullrefresh').pullRefresh().refresh(true); } if(curPage == 1) { mui('#pullrefresh').pullRefresh().endPulldownToRefresh(); //refresh completed } } /** * 数据解析 * @param {Object} params */ function parseListData(params) { var table = document.body.querySelector('.mui-table-view'); if(curPage == 1) { table.innerHTML = ""; var pullrefreshElm = document.querySelector("#pullrefresh"); var listEmptyElm = document.querySelector("#listEmptyElm"); pullrefreshElm.style.display = "block"; listEmptyElm.style.display = "none"; } if(curPage == 1 && params.size == 0) { var pullrefreshElm = document.querySelector("#pullrefresh"); var listEmptyElm = document.querySelector("#listEmptyElm"); pullrefreshElm.style.display = "none"; listEmptyElm.style.display = "block"; } else { for(var i = 0; i < params.list.length; i++) { var itemData = params.list[i]; var itemElm = document.createElement('div'); itemElm.className = "item-main-container"; itemElm.innerHTML = getLayoutElement(itemData.imgUrl, itemData.title); table.appendChild(itemElm); } } var endMore = false; if(params.pageSize != params.size) { endMore = true; } endPullRefresh(endMore); } /** * 获取布局元素 * @param {Object} imgUrl * @param {Object} title */ function getLayoutElement(imgUrl, title) { var elmHtml = ` <img class="item-img" src="holderImgUrl" /> <div class="item-text">holderTitle</div> `; elmHtml = elmHtml.replace("holderImgUrl", imgUrl); elmHtml = elmHtml.replace("holderTitle", title); return elmHtml; } var isEmptyList = true; /** * 数据请求 * @param {Object} url * @param {Object} sucCall * @param {Object} failCall */ function getJsonData(url, sucCall, failCall) { setTimeout(function() { var jsonObj = { "page": 1, "pageSize": 3, "size": curPage != 3 ? 3 : 1, list: curPage != 3 ? [{ title: "自动化测试程序", imgUrl: "https://www.baidu.com/img/bd_logo1.png" }, { title: "自动化测试程序", imgUrl: "https://www.baidu.com/img/bd_logo1.png" }, { title: "自动化测试程序", imgUrl: "https://www.baidu.com/img/bd_logo1.png" }] : [{ title: "自动化测试程序", imgUrl: "https://www.baidu.com/img/bd_logo1.png" }] } if(isEmptyList) { isEmptyList = false; jsonObj = { "page": 1, "pageSize": 3, "size": 0, list: [] } } var result = { data: jsonObj } sucCall(result); }, 1000); } </script> </body> </html>
参考: