angular-ui / ui-scroll

Unlimited bidirectional scrolling over a limited element buffer for AngularJS applications
http://angular-ui.github.io/ui-scroll/demo/
MIT License
327 stars 107 forks source link

reload() causes lost of scroll thumb size #237

Closed ornic closed 4 years ago

ornic commented 4 years ago

What information (apart from min/max indexes) I should provide to datasource (or adapter?) for ui.router to be able to calculate the same top/bottom offsets as at start?

Situation: I have a toggle of grouping of list of elements by on of the fields. After changing the mode, I restore the position as in example, but the scrollbar is resetting to some "default" one-third middle position.

UPD: I had to recreate* ui-scroll elements, since just reload() can't restore the position (~may be I should insert more delay in .get function, but this is not the normal way of working~ - not helping anyway). And recreating viewport element just hung the browser.

In fact, all my problems will get away if I will be able just to "scroll to element with index #" :) But I wasn't able to find anything except some... not the very easy-to-use hach with starting index from the example mentioned above.

ornic commented 4 years ago

Ooook, I was able to remove "recreate" by adding Search'n'Scroll function of my own.

dhilt commented 4 years ago

@ornic datasource.minIndex and datasource.maxIndex should be enough. Also, autoscroll can be used instead of reload, but min/max indexes are still might be required. I created simple demo on stackblitz showing both approaches. Hope this helps.

ornic commented 4 years ago

Thanks a lot :) I simplified my component a bit using this.

But my items all have different heights, so .load(index) is unable to place the viewport to needed position. https://stackblitz.com/edit/angular-ui-scroll-v1-7-6-limited-datasource-and-scroll-t-vufzng this is modified example to show that.

I ended up with that function (it may contain some "never happens" if branches and some project-related stuff about editInGroups):

    $ctrl.scrollTo = function (index) {

        function scrollToElement(element, localIndex, coarseMode) {
            return $timeout(function (element, localIndex) {
                let elem = $(element);
                let parent = elem.parent();
                let scrollTop = elem.position().top - $(parent.children()[0]).position().top + 1; //+1 for border, apparently
                let pixelsToScroll = scrollTop - parent.scrollTop();
                if (pixelsToScroll < 1 && pixelsToScroll > -1) {
                    if ($rootScope.debug) { $rootScope.$window.console.log('ui.scroll not trying to scroll to', index, 'scrolling to', localIndex, 'by', pixelsToScroll, 'pixels'); }
                    return $q.when(true);
                }
                if ($rootScope.debug) { $rootScope.$window.console.log('ui.scroll trying to scroll to', index, 'scrolling to', localIndex, 'by', pixelsToScroll, 'pixels'); }
                let animationPromise = parent.animate({ scrollTop: scrollTop }, 10).promise();
                if (coarseMode) {
                    return animationPromise;
                }
                return animationPromise.then(function () { return scrollToElement(element, localIndex, coarseMode); });
            }, 0, true, element, localIndex);
        }

        if ($ctrl.scroll.isLoading) {
            if ($rootScope.debug) { $rootScope.$window.console.log('ui.scroll trying to scroll to:', index, 'wait for loading'); }
            return $timeout($ctrl.scrollTo, 50, 0, index);
        }

        let firstVisibleIndex = $ctrl.settings.editInGroups ? $ctrl.scroll.topVisible._groupIndex : $ctrl.scroll.topVisible._index;

        let firstBufferIndex = $ctrl.settings.editInGroups ? $ctrl.scroll.bufferFirst._groupIndex : $ctrl.scroll.bufferFirst._index;
        let lastBufferIndex = $ctrl.settings.editInGroups ? $ctrl.scroll.bufferLast._groupIndex : $ctrl.scroll.bufferLast._index;

        if ($rootScope.debug) {
            $rootScope.$window.console.log('ui.scroll trying to scroll from:', firstVisibleIndex, 'to:', index);
        }

        if (firstBufferIndex <= index && lastBufferIndex >= index) {
            return scrollToElement($rootScope.$window.document.getElementById('ui.scroll.element.' + index), index);
        } else if (index > lastBufferIndex) {
            return scrollToElement($rootScope.$window.document.getElementById('ui.scroll.element.' + lastBufferIndex), lastBufferIndex, true).then(function () { return $timeout($ctrl.scrollTo, 30, 0, index); });
        } else if (index < firstBufferIndex) {
            return scrollToElement($rootScope.$window.document.getElementById('ui.scroll.element.' + firstBufferIndex), firstBufferIndex, true).then(function () { return $timeout($ctrl.scrollTo, 30, 0, index); });
        }
    };
ornic commented 4 years ago

BTW, is it possible to mark item as visible only if more than 50% of its height is visible?

dhilt commented 4 years ago

@ornic No, currently no. Here's the place where this feature might be implemented:

https://github.com/angular-ui/ui-scroll/blob/01829539665a1b0b902d32ede96b3d3fb83ff539/src/modules/adapter.js#L199-L204

Something like (top > this.viewport.topVisiblePos() + itemHeight * coef) where coef is 0.5 if you want 50%. If you like, you may implement it, add few tests, update doc, open PR and I will accept it.

PS coef must be an attr option with default value 0 and allowed range [0; 1)