IScroll Things - Drop-down refresh when content is insufficient

Posted by Trip1 on Wed, 12 Jun 2019 21:30:35 +0200

The list in the previous project was IScroll, but there is a problem with IScroll: when the content is not full-screen, there is a way to pull down, so that the purpose of refreshing can not be achieved. [This is what I met in my work, specific examples of specific analysis, here is only a reference]

The general example is as follows:

<style>
    * {
        margin: 0;
        padding: 0;
    }
    html,body,.container {
        width: 100%;
        height: 100%;
    }
    .container>ul>li {
        padding: 15px 20px;
        text-align: center;
        border-bottom: 1px solid #ccc;
    }
</style>

<div id="container" class="container">
    <ul class="scroller">
        <li>item1</li>
        <li>item2</li>
        <li>item3</li>
        <li>item4</li>
        <li>item5</li>
    </ul>
</div>

<script src="https://cdn.bootcss.com/iScroll/5.2.0/iscroll.min.js"></script>
<script>
    var myScroll = null;
    function onLoad() {
        myScroll = new IScroll('container');
    }
    window.addEventListener('DOMContentLoaded', onLoad, false);
</script>

Well, since more than one screen can be refreshed, let's go to the code. Search iscroll in github, open the first, and find core.js under src.

1. Thoughts

First, since you want to pull down, touchstart, touchmove, and touchend events will surely be triggered. Search touchmove, good, and register the event in _initEvents.

_initEvents: function (remove) {
        // ...
        // Some code is omitted here

        if ( utils.hasTouch && !this.options.disableTouch ) {
            eventType(this.wrapper, 'touchstart', this);
            eventType(target, 'touchmove', this);
            eventType(target, 'touchcancel', this);
            eventType(target, 'touchend', this);
        }

        // ...
},

Well, when I saw this, I said I was confused. Isn't this a binding event? What a ghost is this? Then I went to check the documents and found such a thing. Document address

target.addEventListener(type, listener[, options]);
target.addEventListener(type, listener[, useCapture]);
target.addEventListener(type, listener[, useCapture, wantsUntrusted  ]); 
//  
// Gecko/Mozilla only

listener
    When the type of event being monitored triggers, an event notification (an object that implements the Event interface) object is received. listener must be an object or a function that implements the EventListener interface

There's something wrong with listening. listener is an object or a function. The premise is that this object implements the EventListener interface. Let's look down and see an example of this.

var Something = function(element) {
    // |this| is a newly created object
    this.name = 'Something Good';
    this.handleEvent = function(event) {
        console.log(this.name); 
        // 'Something Good', as this is bound to newly created object
        switch(event.type) {
            case 'click':
                // some code here...
                break;
            case 'dblclick':
                // some code here...
                break;
        }
    };

    // Note that the listeners in this case are |this|, not this.handleEvent
    element.addEventListener('click', this, false);
    element.addEventListener('dblclick', this, false);

    // You can properly remove the listeners
    element.removeEventListener('click', this, false);
    element.removeEventListener('dblclick', this, false);
}
var s = new Something(document.body);

Then I went to the source code of IScroll and found the same way. There is a handleEvent.js in the default folder.

Okay, let's get to a close. Still keep looking at the source code. In handleEvent.js, there is such a paragraph.

handleEvent: function (e) {
        switch ( e.type ) {
            case 'touchstart':
            case 'pointerdown':
            case 'MSPointerDown':
            case 'mousedown':
                this._start(e);
                break;
            case 'touchmove':
            case 'pointermove':
            case 'MSPointerMove':
            case 'mousemove':
                this._move(e);
                break;
            case 'touchend':
            case 'pointerup':
            case 'MSPointerUp':
            case 'mouseup':
            case 'touchcancel':
            case 'pointercancel':
            case 'MSPointerCancel':
            case 'mousecancel':
                this._end(e);
                break;
            // ...
        }
    }
};

It was found that the internal method _start/_move/_end was invoked at start/move/end, respectively. Look at these three methods and see if they may cause points that do not slide.

In the _start method, if you see a few lines of code like this, will it be returned directly? Analysis and analysis:

if ( !this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated) {
    return;
}

// ...

var point = e.touches ? e.touches[0] : e,
    pos;

this.initiated  = utils.eventType[e.type];
this.moved      = false;

The initiated attribute is certainly not available at the beginning, and enabled defaults to true, so it will not return when the method is first executed, but will set the current eventType value for the initiated attribute, which will be used in the _move method. Focus on the _move method.

if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
    return;
}

First, type judgment is made, because this value is already defined in the _start method, so it will not be returned here. Then look down:

if ( timestamp - this.endTime > 300 && (absDistX < 10 && absDistY < 10) ) {
    return;
}

[Actually, it's a simulation of two click events] If the time of two slides is more than 300 ms and the displacement in one direction is less than 10 pixels, it will return. Well, if not, just interrupt and test it. There is no mapping here. The actual test result is that every move must be within 300 ms. The reason for judging 300 ms is that there will be a 300 ms delay in click event execution. Each move, because the finger contact is relatively large, or will be more than 10 pixels, even if not more than 10 pixels twice, it will not affect. So that won't go back. Then go on and see:

// If you are scrolling in one direction lock the other
if ( !this.directionLocked && !this.options.freeScroll ) {
    if ( absDistX > absDistY + this.options.directionLockThreshold ) {
        this.directionLocked = 'h';     // lock horizontally
    } else if ( absDistY >= absDistX + this.options.directionLockThreshold ) {
        this.directionLocked = 'v';     // lock vertically
    } else {
        this.directionLocked = 'n';     // no lock
    }
}

if ( this.directionLocked == 'h' ) {
    if ( this.options.eventPassthrough == 'vertical' ) {
        e.preventDefault();
    } else if ( this.options.eventPassthrough == 'horizontal' ) {
        this.initiated = false;
        return;
    }

    deltaY = 0;
} else if ( this.directionLocked == 'v' ) {
    if ( this.options.eventPassthrough == 'horizontal' ) {
        e.preventDefault();
    } else if ( this.options.eventPassthrough == 'vertical' ) {
        this.initiated = false;
        return;
    }

    deltaX = 0;
}

The first condition determines if the direction of the slide is defined. h represents the horizontal direction and v represents the vertical direction. We're going to slide down, so we're focusing on the vertical direction. Looking at the second condition, if it is in the vertical direction, then the deltaX value in the horizontal direction will be changed to 0. The aim is to maintain absolute vertical direction. Because the movement is actually based on the displacement of the element. When the version of probe is less than 2, it moves according to the transform ation attribute of css3, and when it is 3, it moves according to the decision of alignment. So as long as we don't set our deltaY to zero, it shows what's wrong with wood. Continue to look at the code:

deltaX = this.hasHorizontalScroll ? deltaX : 0;
deltaY = this.hasVerticalScroll ? deltaY : 0;

newX = this.x + deltaX;
newY = this.y + deltaY;
// ...

// Here's mobile.
this._translate(newX, newY);

The test found that the hasVertical Scroll has always been false, so deltaY has always been zero, that is, moving white. Find the cause of the problem. So where does this hasVertical Scroll come from? Look for it globally and find a few lines of code in refresh:

this.wrapperWidth   = this.wrapper.clientWidth;
this.wrapperHeight  = this.wrapper.clientHeight;

var rect = utils.getRect(this.scroller);
/* REPLACE START: refresh */

this.scrollerWidth  = rect.width;
this.scrollerHeight = rect.height;

this.maxScrollX     = this.wrapperWidth - this.scrollerWidth;
this.maxScrollY     = this.wrapperHeight - this.scrollerHeight;

/* REPLACE END: refresh */

this.hasHorizontalScroll    = this.options.scrollX && this.maxScrollX < 0;
this.hasVerticalScroll      = this.options.scrollY && this.maxScrollY < 0;

The refresh method is called once when IScroll is instantiated. At a rough glance, scrollY is built-in to true, so only maxScrollY will be greater than 0. Look up. This. wrapper Height - this. scroller Height must be greater than zero. That's the problem.

So look at our initial code, where wrapper Height is the document height and scroller Height is the content height, so wrapper Height is always higher than scroll Height. However, the list with mixed pages on the mobile phone usually has the head and the bottom, while the middle part usually uses padding to make the list scroll globally, so it does not need to calculate the height of the list in a specific way every time.

2. Solutions

To solve the above problems, as long as we can make the inner rolling part higher than the container height, then we can trigger the rolling.

2.1 Rough Approach

You can set a min-height attribute to 900px(900 is just an example, as long as it is large enough), so that you can ensure that you can slide.

2.2 Precision Practice

Calculate the current container height, and then one more pixel than the container height.

Topics: Javascript Attribute less Mobile github