Giter Site home page Giter Site logo

lightningtgc / material-refresh Goto Github PK

View Code? Open in Web Editor NEW
649.0 28.0 111.0 2.65 MB

Google Material Design swipe(pull) to refresh by using JavaScript and CSS3.

Home Page: http://lightningtgc.github.io/material-refresh/

CSS 20.28% JavaScript 69.18% HTML 10.54%

material-refresh's People

Contributors

lightningtgc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

material-refresh's Issues

touchEnd freezes inputs

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

Angular / Angular Material

Hi, is it possible to get this working with angular / angular material? I've been trying to fiddle with it but not winning

click's when on top not working

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

not same behavior like google

i like it very much , great work! , but its still buggy und dont trigger at short fast swipes like googles loader does.

Unexpected behavior when pulling, fixed code inside

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);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.