mirror of
https://github.com/Athou/commafeed.git
synced 2026-03-21 21:37:29 +00:00
improve scrolling performance by registering events only once instead of once per entry
This commit is contained in:
@@ -665,15 +665,17 @@ module.controller('FeedListCtrl', [
|
|||||||
'$route',
|
'$route',
|
||||||
'$state',
|
'$state',
|
||||||
'$window',
|
'$window',
|
||||||
|
'$timeout',
|
||||||
'$location',
|
'$location',
|
||||||
'EntryService',
|
'EntryService',
|
||||||
'SettingsService',
|
'SettingsService',
|
||||||
'FeedService',
|
'FeedService',
|
||||||
'CategoryService',
|
'CategoryService',
|
||||||
'AnalyticsService',
|
'AnalyticsService',
|
||||||
function($scope, $stateParams, $http, $route, $state, $window, $location, EntryService, SettingsService, FeedService,
|
function($scope, $stateParams, $http, $route, $state, $window, $timeout, $location, EntryService, SettingsService, FeedService,
|
||||||
CategoryService, AnalyticsService) {
|
CategoryService, AnalyticsService) {
|
||||||
|
|
||||||
|
$window = angular.element($window);
|
||||||
AnalyticsService.track();
|
AnalyticsService.track();
|
||||||
|
|
||||||
$scope.keywords = $location.search().q;
|
$scope.keywords = $location.search().q;
|
||||||
@@ -758,6 +760,79 @@ module.controller('FeedListCtrl', [
|
|||||||
}, callback);
|
}, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var watch_scrolling = true;
|
||||||
|
var watch_current = true;
|
||||||
|
|
||||||
|
$scope.$watch('current', function(newValue, oldValue) {
|
||||||
|
if (!watch_current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newValue && newValue !== oldValue) {
|
||||||
|
var force = $scope.navigationMode == 'keyboard';
|
||||||
|
|
||||||
|
// timeout here to execute after dom update
|
||||||
|
$timeout(function() {
|
||||||
|
var docTop = $(window).scrollTop();
|
||||||
|
var docBottom = docTop + $(window).height();
|
||||||
|
|
||||||
|
var elem = $('#entry_' + newValue.id);
|
||||||
|
var elemTop = elem.offset().top;
|
||||||
|
var elemBottom = elemTop + elem.height();
|
||||||
|
|
||||||
|
if (!force && (elemTop > docTop) && (elemBottom < docBottom)) {
|
||||||
|
// element is entirely visible
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
var scrollTop = elemTop - $('#toolbar').outerHeight();
|
||||||
|
watch_scrolling = false;
|
||||||
|
$('html, body').animate({
|
||||||
|
scrollTop : scrollTop
|
||||||
|
}, 400, 'swing', function() {
|
||||||
|
watch_scrolling = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var scrollHandler = function() {
|
||||||
|
if (!watch_scrolling || _.size($scope.entries) === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.navigationMode = 'scroll';
|
||||||
|
if (SettingsService.settings.viewMode == 'expanded') {
|
||||||
|
var w = $(window);
|
||||||
|
var docTop = w.scrollTop();
|
||||||
|
|
||||||
|
var current = null;
|
||||||
|
for ( var i = 0; i < $scope.entries.length; i++) {
|
||||||
|
var entry = $scope.entries[i];
|
||||||
|
var e = $('#entry_' + entry.id);
|
||||||
|
if (e.offset().top + e.height() > docTop + $('#toolbar').outerHeight()) {
|
||||||
|
current = entry;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var previous = $scope.current;
|
||||||
|
$scope.current = current;
|
||||||
|
if (previous != current) {
|
||||||
|
if (SettingsService.settings.scrollMarks) {
|
||||||
|
$scope.mark($scope.current, true);
|
||||||
|
}
|
||||||
|
watch_current = false;
|
||||||
|
$scope.$apply();
|
||||||
|
watch_current = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var scrollListener = _.throttle(scrollHandler, 200);
|
||||||
|
$window.on('scroll', scrollListener);
|
||||||
|
$scope.$on('$destroy', function() {
|
||||||
|
return $window.off('scroll', scrollListener);
|
||||||
|
});
|
||||||
|
|
||||||
$scope.goToFeed = function(id) {
|
$scope.goToFeed = function(id) {
|
||||||
$state.transitionTo('feeds.view', {
|
$state.transitionTo('feeds.view', {
|
||||||
_type : 'feed',
|
_type : 'feed',
|
||||||
@@ -955,16 +1030,6 @@ module.controller('FeedListCtrl', [
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.onScroll = function(entry) {
|
|
||||||
$scope.navigationMode = 'scroll';
|
|
||||||
if (SettingsService.settings.viewMode == 'expanded') {
|
|
||||||
$scope.current = entry;
|
|
||||||
if (SettingsService.settings.scrollMarks) {
|
|
||||||
$scope.mark(entry, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Mousetrap.bind('j', function(e) {
|
Mousetrap.bind('j', function(e) {
|
||||||
$scope.$apply(function() {
|
$scope.$apply(function() {
|
||||||
$scope.navigationMode = 'keyboard';
|
$scope.navigationMode = 'keyboard';
|
||||||
|
|||||||
@@ -59,96 +59,6 @@ module.directive('ngBlur', function() {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Fired when the top of the element is not visible anymore
|
|
||||||
*/
|
|
||||||
module.directive('onScrollMiddle', function() {
|
|
||||||
return {
|
|
||||||
restrict : 'A',
|
|
||||||
link : function(scope, element, attrs) {
|
|
||||||
|
|
||||||
var w = $(window);
|
|
||||||
var e = $(element);
|
|
||||||
var d = $(document);
|
|
||||||
|
|
||||||
var offset = parseInt(attrs.onScrollMiddleOffset, 10);
|
|
||||||
|
|
||||||
var down = function() {
|
|
||||||
var docTop = w.scrollTop();
|
|
||||||
var elemTop = e.offset().top;
|
|
||||||
var threshold = docTop === 0 ? elemTop - 1 : docTop + offset;
|
|
||||||
return (elemTop > threshold) ? 'below' : 'above';
|
|
||||||
};
|
|
||||||
var up = function() {
|
|
||||||
var docTop = w.scrollTop();
|
|
||||||
var elemTop = e.offset().top;
|
|
||||||
var elemBottom = elemTop + e.height();
|
|
||||||
var threshold = docTop === 0 ? elemBottom - 1 : docTop + offset;
|
|
||||||
return (elemBottom > threshold) ? 'below' : 'above';
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!w.data.scrollInit) {
|
|
||||||
w.data.scrollPosition = d.scrollTop();
|
|
||||||
w.data.scrollDirection = 'down';
|
|
||||||
|
|
||||||
var onScroll = function(e) {
|
|
||||||
var scroll = d.scrollTop();
|
|
||||||
w.data.scrollDirection = (scroll - w.data.scrollPosition > 0) ? 'down' : 'up';
|
|
||||||
w.data.scrollPosition = scroll;
|
|
||||||
scope.$apply();
|
|
||||||
};
|
|
||||||
w.bind('scroll', _.throttle(onScroll, 500));
|
|
||||||
w.data.scrollInit = true;
|
|
||||||
}
|
|
||||||
scope.$watch(down, function downCallback(value, oldValue) {
|
|
||||||
if (value != oldValue && value == 'above')
|
|
||||||
scope.$eval(attrs.onScrollMiddle);
|
|
||||||
});
|
|
||||||
scope.$watch(up, function upCallback(value, oldValue) {
|
|
||||||
if (value != oldValue && value == 'below')
|
|
||||||
scope.$eval(attrs.onScrollMiddle);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scrolls to the element if the value is true and the attribute is not fully
|
|
||||||
* visible, unless the attribute scroll-to-force is true
|
|
||||||
*/
|
|
||||||
module.directive('scrollTo', ['$timeout', function($timeout) {
|
|
||||||
return {
|
|
||||||
restrict : 'A',
|
|
||||||
link : function(scope, element, attrs) {
|
|
||||||
scope.$watch(attrs.scrollTo, function(value) {
|
|
||||||
if (!value)
|
|
||||||
return;
|
|
||||||
var force = scope.$eval(attrs.scrollToForce);
|
|
||||||
|
|
||||||
// timeout here to execute after dom update
|
|
||||||
$timeout(function() {
|
|
||||||
var docTop = $(window).scrollTop();
|
|
||||||
var docBottom = docTop + $(window).height();
|
|
||||||
|
|
||||||
var elemTop = $(element).offset().top;
|
|
||||||
var elemBottom = elemTop + $(element).height();
|
|
||||||
|
|
||||||
if (!force && (elemTop > docTop) && (elemBottom < docBottom)) {
|
|
||||||
// element is entirely visible
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
var offset = parseInt(attrs.scrollToOffset, 10);
|
|
||||||
var scrollTop = $(element).offset().top + offset;
|
|
||||||
$('html, body').animate({
|
|
||||||
scrollTop : scrollTop
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevent mousewheel scrolling from propagating to the parent when scrollbar
|
* Prevent mousewheel scrolling from propagating to the parent when scrollbar
|
||||||
* reaches top or bottom
|
* reaches top or bottom
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="span10 main-content">
|
<div class="span10 main-content">
|
||||||
<div class="toolbar" ng-include="'templates/_toolbar.html'"></div>
|
<div id="toolbar" class="toolbar" ng-include="'templates/_toolbar.html'"></div>
|
||||||
<div class="entryList">
|
<div class="entryList">
|
||||||
<div ui-view></div>
|
<div ui-view></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -18,8 +18,6 @@
|
|||||||
ng-class="{'expanded' : settingsService.settings.viewMode == 'expanded' }">
|
ng-class="{'expanded' : settingsService.settings.viewMode == 'expanded' }">
|
||||||
<div ng-show="message && errorCount > 10">${view.error_while_loading_feed} : {{message}}</div>
|
<div ng-show="message && errorCount > 10">${view.error_while_loading_feed} : {{message}}</div>
|
||||||
<div ng-repeat="entry in entries" class="entry entry-font-size-{{font_size}}" id="entry_{{entry.id}}"
|
<div ng-repeat="entry in entries" class="entry entry-font-size-{{font_size}}" id="entry_{{entry.id}}"
|
||||||
scroll-to="navigationMode != 'scroll' && current == entry" scroll-to-force="navigationMode == 'keyboard'" scroll-to-offset="-50"
|
|
||||||
on-scroll-middle="onScroll(entry)" on-scroll-middle-offset="50"
|
|
||||||
ng-class="{unread: entry.read == false, current: current==entry, open: isOpen, closed: !isOpen }">
|
ng-class="{unread: entry.read == false, current: current==entry, open: isOpen, closed: !isOpen }">
|
||||||
<div class="entry-heading">
|
<div class="entry-heading">
|
||||||
<a href="{{entry.url}}" target="_blank" class="entry-heading-link" ng-click="noop($event)" ng-mouseup="entryClicked(entry, $event)">
|
<a href="{{entry.url}}" target="_blank" class="entry-heading-link" ng-click="noop($event)" ng-mouseup="entryClicked(entry, $event)">
|
||||||
|
|||||||
Reference in New Issue
Block a user