blob: d38c33b1579a2d00682b16cedb0ffc062a3b11c8 [file] [log] [blame]
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// @dart = 2.9
part of touch;
/**
* Adds a listener to the scroller with triggers events
* when a trigger point at the top, or bottom, of the screen is reached.
*
* To use this you will need to have an element with a scroller attached
* to it. You need to have defined (in pixels) how far from the top or
* bottom the scroll position must be in order to trigger (the "trigger
* point") The element using this must have functions for hitting the top
* trigger, and the bottom trigger. In general, these methods will
* ascertain whether we have more data to scroll to (i.e. when we hit
* the bottom trigger point but have reached the end of the data
* displayed in the element we should ignore it), make the call for
* more data and reposition the scroller - repositioning is key to
* good user experience.
*
* Triggers are generated by listening for the SCROLL_END event from the
* scroller, so data calls are not initiated whilst scrolling is happening,
* but after.
*
* Controls changing divs between the usual (non-loading) div and the
* loading div. To take advantage of this, callback function should return
* a boolean indicating whether the usual div should be replaced by the
* loading div.
*/
class InfiniteScroller {
Scroller _scroller;
/**
* Function to invoke when trigger point is reached at the top of the view.
*/
Function _onTopScroll;
/**
* Function to invoke when trigger point is reached at the bottom of the view.
*/
Function _onBottomScroll;
/** Offset for trigger point at the top of the view. */
double _offsetTop;
/** Offset for trigger point at the bottom of the view. */
double _offsetBottom;
/** Saves the last Y position. */
double _lastScrollY;
Element _topDiv;
Element _topLoadingDiv;
Element _bottomDiv;
Element _bottomLoadingDiv;
InfiniteScroller(Scroller scroller, Function onTopScroll,
Function onBottomScroll, double offsetTop,
[double offsetBottom = null])
: _scroller = scroller,
_onTopScroll = onTopScroll,
_onBottomScroll = onBottomScroll,
_offsetTop = offsetTop,
_offsetBottom = offsetBottom == null ? offsetTop : offsetBottom,
_lastScrollY = 0.0 {}
/**
* Adds the loading divs.
* [topDiv] The div usually shown at the top.
* [topLoadingDiv] is the div to show at the top when waiting for more
* content to load at the top of the page.
* [bottomDiv] is the div usually shown at the bottom.
* [bottomLoadingDiv] is the div to show at the bottom when waiting for more
* content to load at the end of the page.
*/
void addLoadingDivs(
[Element topDiv = null,
Element topLoadingDiv = null,
Element bottomDiv = null,
Element bottomLoadingDiv = null]) {
_topDiv = topDiv;
_topLoadingDiv = topLoadingDiv;
_bottomDiv = bottomDiv;
_bottomLoadingDiv = bottomLoadingDiv;
_updateVisibility(false, _topDiv, _topLoadingDiv);
_updateVisibility(false, _bottomDiv, _bottomLoadingDiv);
}
void initialize() {
_registerEventListeners();
}
/**
* Switch back the divs after loading complete. Delegate should call
* this function after loading is complete.
*/
void loadEnd() {
_updateVisibility(false, _topDiv, _topLoadingDiv);
_updateVisibility(false, _bottomDiv, _bottomLoadingDiv);
}
/**
* Called at the end of a scroll event.
*/
void _onScrollEnd() {
double ypos = _scroller.getVerticalOffset();
// Scroll is below last point.
if (ypos < _lastScrollY) {
double bottomTrigger = _scroller.getMinPointY() + _offsetBottom;
// And below trigger point.
if (ypos <= bottomTrigger) {
_updateVisibility(_onBottomScroll(), _bottomDiv, _bottomLoadingDiv);
}
} else {
if (ypos > _lastScrollY) {
// Scroll is above last point.
double topTrigger = _scroller.getMaxPointY() - _offsetTop;
// And above trigger point.
if (ypos >= topTrigger) {
_updateVisibility(_onTopScroll(), _topDiv, _topLoadingDiv);
}
}
}
_lastScrollY = ypos;
}
/**
* Register the event listeners.
*/
void _registerEventListeners() {
_scroller.onScrollerEnd.listen((Event event) {
_onScrollEnd();
});
}
/**
* Hides one div and shows another.
*/
void _updateVisibility(
bool isLoading, Element element, Element loadingElement) {
if (element != null) {
element.style.display = isLoading ? "none" : "";
}
if (loadingElement != null) {
loadingElement.style.display = isLoading ? "" : "none";
}
}
}