如何使用angularJS制作虚拟滚动?

时间:2022-05-16 20:31:09

I'm trying to make a directive that I can do a virtual scroll, so as the user scrolls the table, the table remove "old" views and add "new" views, kind like of collection repeat but I've been failing, I think I didn't understand the math behind it, can someone help me?

我正在尝试制作一个可以进行虚拟滚动的指令,因此当用户滚动表格时,表格会删除“旧”视图并添加“新”视图,类似于收集重复,但我一直在失败,我想我不明白它背后的数学,有人可以帮助我吗?

this is my directive code:

这是我的指令代码:

BaseModule.directive('myScroll', function() {
    return {
        restrict:"A",
        scope:{
            rows:"=",
            headers:"="
        },
        link: function(scope,el) {
            var scrollTop = 0;
            var scrollLeft = 0;
            angular.element(el).on('scroll',function(){
                scrollTop = $(el).scrollTop();
                scrollLeft = $(el).scrollLeft();
                $(el).parent().find(".header").scrollLeft(scrollLeft);
                var height = $(el).height();
                var numberOfRows = height/23;
                var initialRow = scrollTop/23;
                var html = "";
                for(i=0; i<numberOfRows;i++){
                    var row = scope.rows[i+initialRow];
                    html = html + addRow(row,i+initialRow);
                }
                $(el).find('.tbody-scroll').append(html);
            });
            scope.$watch('rows',function(rows){
                var height = $(el).height();
                var numberOfRows = height/23;
                var initialRow = scrollTop/23;
                var html = "";
                for(i=0; i<numberOfRows;i++){
                    var row = rows[i+initialRow];
                    html = html + addRow(row,i+initialRow);
                }
                $(el).find('.tbody-scroll').append(html);
            });
            var addRow = function(row,index){
                var html = "";
                var pos = 0;
                var totalWidth = 0;
                angular.forEach(row,function(col){
                    var width = scope.headers[pos].width;
                    totalWidth = totalWidth + width;
                    html = html + "<span style='width:"+width+"px'>"+col.value+"</span>";
                    pos++;
                });
                html = "<div class='row' style='top:"+index*23+"px;width:"+totalWidth+"px;'>"+html;
                html = html + "</div>";
                return html;
            };
        }
    };
});

<!-- my directive .html -->
<div class="mTable">
    <div class="header" ng-style="headerWidth(headers())">
        <span ng-repeat="header in headers()" ng-style="widthStyle(header)">
            {{::header.label}}
        </span>
    </div>
    <div class="tbody-container" my-scroll headers="headers()" rows="rows()">
        <div class="tbody-scroll" ng-style="scrollHeight(rows(),headers())"></div>
    </div>
</div>

2 个解决方案

#1


2  

Giving a full answer with code might require a bit too much of an effort
This library implements virtual scroll on ng-repeat https://github.com/stackfull/angular-virtual-scroll in the description there's also an article on how to implement this feature on your own.

使用代码给出完整答案可能需要花费太多精力这个库在ng-repeat上实现虚拟滚动https://github.com/stackfull/angular-virtual-scroll在描述中还有一篇关于如何实现的文章这个功能你自己。

The basic concept is to make two divs, one above and one below the list, which size is determined by the number of elements inside the list (this approach has a limitation since list elements must either have all the same height or their height must be fixed), then you delete elements as they disappear from the viewport and resize the divs according to the number of elements not currently rendered and your position on the list

基本概念是制作两个div,一个在列表上方,一个在列表下方,其大小由列表中的元素数量决定(这种方法有一个限制,因为列表元素必须具有相同的高度或它们的高度必须是固定),然后删除元素,因为它们从视口中消失,并根据当前未呈现的元素数量和列表中的位置调整div的大小

#2


2  

Not a direct answer to your question, but an alternative: You may want to look at the ui-scroll directive which is a replacement for ng-repeat and has a similar function.

不是你的问题的直接答案,而是另一种选择:你可能想看一下ui-scroll指令,它取代了ng-repeat并具有类似的功能。

Example in your controller

控制器中的示例

$scope.movieDataSource = {

    get : function (index, count, callback) {
        var i, items = [$scope.userMovies], item;

            var min = 1;
            var max = 1000;

        for (i=index ; i<index + count ; i++) {
                if(i < min || i > max) {
                    continue;
                }
            item = {
                title: $scope.userMovies.title,
                imageURL: $scope.userMovies.poster_path
            };
            items.push (item);
        }
        callback (items);
    }
}

And in your view:

在你看来:

<div ui-scroll="item in movieDataSource">
  {{item.title}}
</div>

#1


2  

Giving a full answer with code might require a bit too much of an effort
This library implements virtual scroll on ng-repeat https://github.com/stackfull/angular-virtual-scroll in the description there's also an article on how to implement this feature on your own.

使用代码给出完整答案可能需要花费太多精力这个库在ng-repeat上实现虚拟滚动https://github.com/stackfull/angular-virtual-scroll在描述中还有一篇关于如何实现的文章这个功能你自己。

The basic concept is to make two divs, one above and one below the list, which size is determined by the number of elements inside the list (this approach has a limitation since list elements must either have all the same height or their height must be fixed), then you delete elements as they disappear from the viewport and resize the divs according to the number of elements not currently rendered and your position on the list

基本概念是制作两个div,一个在列表上方,一个在列表下方,其大小由列表中的元素数量决定(这种方法有一个限制,因为列表元素必须具有相同的高度或它们的高度必须是固定),然后删除元素,因为它们从视口中消失,并根据当前未呈现的元素数量和列表中的位置调整div的大小

#2


2  

Not a direct answer to your question, but an alternative: You may want to look at the ui-scroll directive which is a replacement for ng-repeat and has a similar function.

不是你的问题的直接答案,而是另一种选择:你可能想看一下ui-scroll指令,它取代了ng-repeat并具有类似的功能。

Example in your controller

控制器中的示例

$scope.movieDataSource = {

    get : function (index, count, callback) {
        var i, items = [$scope.userMovies], item;

            var min = 1;
            var max = 1000;

        for (i=index ; i<index + count ; i++) {
                if(i < min || i > max) {
                    continue;
                }
            item = {
                title: $scope.userMovies.title,
                imageURL: $scope.userMovies.poster_path
            };
            items.push (item);
        }
        callback (items);
    }
}

And in your view:

在你看来:

<div ui-scroll="item in movieDataSource">
  {{item.title}}
</div>