lightningtgc / material-refresh Goto Github PK
View Code? Open in Web Editor NEWGoogle Material Design swipe(pull) to refresh by using JavaScript and CSS3.
Home Page: http://lightningtgc.github.io/material-refresh/
Google Material Design swipe(pull) to refresh by using JavaScript and CSS3.
Home Page: http://lightningtgc.github.io/material-refresh/
Hello,
The line
e.preventDefault();
in touchEnd method prevent inputs from being focused on some phones (not working on some Android phones, while on other there is no problem).
Not sure how to fix that. Just commented for now.
Thanks
Hi, is it possible to get this working with angular / angular material? I've been trying to fiddle with it but not winning
there are so many times that when using the material refresh,the click event doesn't trigger any more,pls fix the problem.
When page scrolled to top .. clicks not firing , it's going as touch event not a click event and when you scroll 1 px down all click fire normal
i read the source code that, when swipe to the right or left, it refreshes too,can i cancel it ?
material refresh not working with jquery
please help
Error: Cannot find module 'material-refresh' from 'PROJECT/FOLDER'
seems require not working.
amazing work!
is this component support react-native?
i like it very much , great work! , but its still buggy und dont trigger at short fast swipes like googles loader does.
it's seem you didn't test with jquery, and you add jquery dependency in doc...
Is there a way to get this plugin to work in a Universal (SSR) React app using Webpack?
i want to only activate it when it reaches the top of my page how can i achieve this?
Don't work with iOS . :(
Unable to simultaneously satisfy constraints.
on button click to refresh the page, page is refresh, but after refreshing app will be closed or crashed...
The way the pull down worked was a bit too particular. I relaxed the requirements a bit and I also changed the movement animation to use transform: translate instead of .animate. It should feel more fluid to the user.
I modified it quickly for my app, don't have too much time to clean it up, you may find some junk variables and console.logs in there. I mainly edited, touchStart, touchMove, touchEnd, and moveCircle.
/**
* Google Material Design Swipe To Refresh.
* By Gctang(https://github.com/lightningtgc)
*
* Three types of refresh:
* 1. Above or coplanar with another surface
* 2. Below another surface in z-space.
* 3. Button action refresh
*
*/
;(function($){
var $scrollEl = $(document.body);
var $refreshMain, $spinnerWrapper, $arrowWrapper, $arrowMain;
var scrollEl = document.body;
var noShowClass = 'mui-refresh-noshow';
var mainAnimatClass = 'mui-refresh-main-animat';
var blueThemeClass = 'mui-blue-theme';
var isShowLoading = false;
var isStoping = false;
var isBtnAction = false;
var NUM_POS_START_Y = -85;
var NUM_POS_TARGET_Y = 0; // Where to stop
var NUM_POS_MAX_Y = 120; // Max position for the moving distance
var NUM_POS_MIN_Y = -25; // Min position for the moving distance
var NUM_NAV_TARGET_ADDY = 20; // For custom nav bar
var touchCurrentY;
var touchStartY = 0;
var customNavTop = 0;
var verticalThreshold = 15;
var maxRotateTime = 6000; //Max time to stop rotate
var basePosY = 60;
var onBegin = null;
var onBtnBegin= null;
var onEnd = null;
var onBtnEnd = null;
var stopAnimatTimeout = null;
var refreshNav = '';
var lastTime = new Date().getTime();
var isIOS = !!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
var tmpl = '<div id="muiRefresh" class="mui-refresh-main">\
<div class="mui-refresh-wrapper ">\
<div class="mui-arrow-wrapper">\
<div class="mui-arrow-main"></div>\
</div>\
<div class="mui-spinner-wrapper" style="display:none;">\
<div class="mui-spinner-main" >\
<div class="mui-spinner-left">\
<div class="mui-half-circle"></div>\
</div>\
<div class="mui-spinner-right">\
<div class="mui-half-circle"></div>\
</div>\
</div>\
</div>\
</div>\
</div>';
// Defined the object to improve performance
var touchPos = {
top: 0,
x1: 0,
x2: 0,
y1: 0,
y2: 0
}
// Default options
/* var opts = { */
/* scrollEl: '', //String */
/* nav: '', //String */
/* top: '0px', //String */
/* theme: '', //String */
/* index: 10001, //Number*/
/* maxTime: 3000, //Number */
/* freeze: false, //Boolen */
/* onBegin: null, //Function */
/* onEnd: null //Function */
/* } */
/* Known issue:
* 1. iOS feature when scrolling ,animation will stop
* 2. Animation display issue in anfroid like miui小米
*
*
* TODO list:
* 1. Using translate and scale together to replace top
* 2. Optimize circle rotate animation
*/
// Main function to init the refresh style
function mRefresh(options) {
options = options || {};
scrollEl = options.scrollEl ? options.scrollEl :
isIOS ? scrollEl : document;
$scrollEl = $(scrollEl);
// extend options
onBegin = options.onBegin;
onEnd = options.onEnd;
maxRotateTime = options.maxTime || maxRotateTime;
refreshNav = options.nav || refreshNav;
if ($('#muirefresh').length === 0) {
renderTmpl();
}
$refreshMain = $('#muiRefresh');
$spinnerWrapper = $('.mui-spinner-wrapper', $refreshMain);
$arrowWrapper = $('.mui-arrow-wrapper', $refreshMain);
$arrowMain = $('.mui-arrow-main', $refreshMain);
// Custom nav bar
if (!isDefaultType()) {
$refreshMain.addClass('mui-refresh-nav');
basePosY = $(refreshNav).height() + 20;
if($(refreshNav).offset()){
customNavTop = $(refreshNav).offset().top;
// Handle position fix
if($(refreshNav).css('position') !== 'fixed'){
basePosY += customNavTop;
}
// Set the first Y position
$refreshMain.css('-webkit-transform', 'translate3d(0px,'+customNavTop+'px,0px)');
// $refreshMain.css('top', customNavTop + 'px');
}
//Set z-index to make sure ablow the nav bar
var navIndex = $(refreshNav).css('z-index');
$refreshMain.css('z-index', navIndex - 1);
}
//Set custom z-index
if(options.index){
$refreshMain.css('z-index', ~~options.index);
}
//Set custom top, to change the position
if(options.top){
$refreshMain.css('-webkit-transform', 'translate3d(0px,'+options.top+'px,0px)');
//$refreshMain.css('top', options.top);
}
// Extract theme
if (options.theme) {
$refreshMain.addClass(options.theme);
} else {
$refreshMain.addClass(blueThemeClass);
}
// Add Animation Class
$refreshMain.addClass(mainAnimatClass);
if(!options.freeze){
bindEvents();
}
}
// Public Methods
// Finish loading
mRefresh.resolve = function() {
if(!isStoping && stopAnimatTimeout){
clearTimeout(stopAnimatTimeout);
stopAnimatTimeout = null;
recoverRefresh();
}
}
// Destory refresh
mRefresh.destroy = function(){
unbindEvents();
$refreshMain.remove();
}
// Type3: Button action refresh
mRefresh.refresh = function(opt) {
// Do rotate
if(!isShowLoading){
var realTargetPos = basePosY + NUM_POS_TARGET_Y - 20;
isShowLoading = true;
isBtnAction = true;
opt = opt || {};
onBtnBegin = opt.onBegin;
onBtnEnd = opt.onEnd;
if (!isDefaultType()) {
realTargetPos = realTargetPos + NUM_NAV_TARGET_ADDY;
}
// Handle freeze
$refreshMain.show();
//Romove animat time
$refreshMain.removeClass(mainAnimatClass);
// move to target position
$refreshMain.css('-webkit-transform', 'translate3d(0px,'+realTargetPos+'px,0px)');
// $refreshMain.css('top', realTargetPos + 'px');
// make it small
$refreshMain.css('-webkit-transform', 'scale(' + 0.01 + ')');
setTimeout(doRotate, 60);
}
}
// Unbind touch events,for freeze type1 and type2
mRefresh.unbindEvents = function(){
unbindEvents();
}
mRefresh.bindEvents = function(){
bindEvents();
}
// Render html template
function renderTmpl(){
document.body.insertAdjacentHTML('beforeend', tmpl);
}
function touchStart(e){
if(isIOS && scrollEl == document.body){
touchPos.top = window.scrollY;
}else if(scrollEl != document){
scollPos = $(scrollEl).scrollTop();
} else {
touchPos.top = (document.documentElement || document.body.parentNode || document.body).scrollTop;
}
if (touchPos.top > 0 || isShowLoading) {
return;
}
//touchCurrentY = basePosY + NUM_POS_START_Y;
hasStarted = false;
// Fix jQuery touch event detect
e = e.originalEvent || e;
if (e.touches[0]) {
//touchPos.x1 = e.touches[0].pageX;
//touchStartY = touchPos.y1 = e.touches[0].pageY;
//touchCurrentY = e.touches[0].pageY;
//previousY = currentY;
}
}
var hasStarted = false;
var startY = 0;
var previousY = 0;
var currentY = 0;
var movePct = .25;
var scollPos = 0;
var distanceY = 0;
function touchMove(e){
var thisTouch;//, distanceY;
var now = new Date().getTime();
e = e.originalEvent || e;
scrollPos = $(scrollEl).scrollTop();
var maxHeight = $(scrollEl).height();
if ( isShowLoading || !e.touches || e.touches.length !== 1) {
// Just allow one finger
return;
}
thisTouch = e.touches[0];
touchCurrentY = thisTouch.pageY;
distanceY = (touchCurrentY - touchStartY);
console.log("Start="+touchStartY+", Current="+touchCurrentY+", Dist="+distanceY);
if( !hasStarted && scrollPos < (verticalThreshold/2) ) {
//if( distanceY > verticalThreshold ) {
if( !hasStarted ) {
hasStarted = true;
touchStartY = touchCurrentY;
}
touchStartY = touchCurrentY;
$refreshMain.show();
distanceY = 0;
console.log("HIT THRESHOLD");
//}
}
if( hasStarted && distanceY > verticalThreshold) {
e.preventDefault();
// Some android phone
// Throttle, aviod jitter
if (now - lastTime < 90) {
return;
}
console.log("MOVING CIRCLE, DISTANCE = " + distanceY);
moveCircle(distanceY);
}
/*
touchPos.x2 = thisTouch.pageX;
touchPos.y2 = thisTouch.pageY;
// Distance for pageY change
distanceY = touchPos.y2 - touchPos.y1;
if (touchPos.y2 - touchStartY + verticalThreshold > 0) {
e.preventDefault();
// Some android phone
// Throttle, aviod jitter
if (now - lastTime < 90) {
return;
}
if (touchCurrentY < basePosY - customNavTop + NUM_POS_MAX_Y) {
touchCurrentY += distanceY ;
moveCircle(touchCurrentY);
} else {
// Move over the max position will do the rotate
doRotate();
return;
}
}
*/
// y1 always is the current pageY
touchPos.y1 = thisTouch.pageY;
lastTime = now;
}
function touchEnd(e){
hasStarted = false;
if (scrollPos > 0 || isShowLoading) {
return;
}
e.preventDefault();
if (distanceY >= (maxDistance - verticalThreshold)) {
// Should move over the min position
doRotate();
} else {
backToStart();
}
}
/**
* backToStart
* Return to start position
*/
function backToStart() {
var realStartPos = basePosY + NUM_POS_START_Y;
if ( isDefaultType() ) {
$refreshMain.css('-webkit-transform', 'translate3d(0px,'+realStartPos+'px,0px)');
// $refreshMain.css('top', realStartPos + 'px');
$refreshMain.css('-webkit-transform', 'scale(' + 0 + ')');
} else {
// Distance must greater than NUM_POS_MIN_Y
$refreshMain.css('-webkit-transform', 'translate3d(0px,'+customNavTop+'px,0px)');
//$refreshMain.css('top', customNavTop + 'px');
/* $refreshMain.css('-webkit-transform', 'translateY(' + realStartPos + 'px)'); */
}
setTimeout(function(){
// Handle button action
if(!isShowLoading){
$refreshMain.css('opacity', 0);
$refreshMain.hide();
}
}, 300);
}
/**
* moveCircle
* touchmove change the circle style
*
* @param {number} y
*/
var maxDistance = 110;
function moveCircle(y){
if( y > maxDistance )
y = maxDistance;
var scaleRate = maxDistance/4;
var scalePer = y / scaleRate > 1 ? 1 : y / scaleRate < 0 ? 0 : y / scaleRate;
var currMoveY = basePosY + NUM_POS_START_Y + y;
if (isDefaultType()) {
// Small to Big
$refreshMain.css('-webkit-transform', 'scale(' + scalePer + ')');
}
/* $refreshMain.css('-webkit-transform', 'translateY('+ y + 'px)'); */
$refreshMain.css('opacity', scalePer);
// Change the position
$refreshMain.css('-webkit-transform', 'translate3d(0px,'+currMoveY+'px,0px)');//currMoveY + 'px');
$arrowMain.css('-webkit-transform', 'rotate(' + -(y * 3) + 'deg)');
/* $arrowMain.css('transform', 'rotate(' + -(y * 3) + 'deg)'); */
}
/**
* doRotate
* Rotate the circle,and you can stop it by `mRefresh.resolve()`
* or it wil stop within the time: `maxRotateTime`
*/
function doRotate(){
isShowLoading = true;
// Do button action callback
if (isBtnAction && typeof onBtnBegin === 'function') {
onBtnBegin();
} else if (typeof onBegin === 'function') {
// Do onBegin callback
onBegin();
}
// Make sure display entirely
$refreshMain.css('opacity', 1);
if (!isBtnAction) {
var realTargetPos = basePosY + NUM_POS_TARGET_Y - 20;
if (!isDefaultType()) {
realTargetPos = realTargetPos + NUM_NAV_TARGET_ADDY;
}
$refreshMain.css('-webkit-transform', 'translate3d(0px,'+realTargetPos+'px,0px)');
//$refreshMain.css('top', realTargetPos + 'px');
/* $refreshMain.css('-webkit-transform', 'translateY(' + realTargetPos + 'px)'); */
} else {
$refreshMain.addClass(mainAnimatClass);
$refreshMain.css('-webkit-transform', 'scale(' + 1 + ')');
}
$arrowWrapper.hide();
// Start animation
$spinnerWrapper.show();
// Timeout to stop animation
stopAnimatTimeout = setTimeout(recoverRefresh, maxRotateTime);
}
/**
* Recover Refresh
* Hide the circle
*/
function recoverRefresh(){
// For aviod resolve
isStoping = true;
// Stop animation
$refreshMain.addClass(noShowClass);
$spinnerWrapper.hide();
setTimeout(function(){
$refreshMain.removeClass(noShowClass);
$refreshMain.hide();
backToStart();
$arrowWrapper.show();
isShowLoading = false;
isStoping = false;
if (isBtnAction && typeof onBtnEnd === 'function') {
onBtnEnd();
} else if (typeof onEnd === 'function') {
onEnd();
}
isBtnAction = false;
}, 500);
}
/**
* isDefaultType
* Check is type1: Above surface
*
* @return {Boolen}
*/
function isDefaultType() {
return $(refreshNav).length === 0;
}
function bindEvents() {
$scrollEl.on('touchstart', touchStart);
$scrollEl.on('touchmove', touchMove);
$scrollEl.on('touchend', touchEnd);
}
function unbindEvents() {
$scrollEl.off('touchstart', touchStart);
$scrollEl.off('touchmove', touchMove);
$scrollEl.off('touchend', touchEnd);
}
window.mRefresh = mRefresh;
})(window.Zepto || window.jQuery);
links on the page isn't working when using material refresh for a scrollable page, especially when the page is at the top.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.