// 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"; } } }