ionic入门教程第十五课-ionic性能优化之图片延时加载
周五的时候有个朋友让我写一个关于图片延时加载的教程,
直到今天才有空编辑,这阶段真的是很忙,公众号都变成僵尸号了。
实在是对不起大家。
有人喜欢我的教程,可能我总习惯了用比较简单容易理解的方式去描述这些东西。
别的就不多说了,大家遇到什么问题。
可以直接联系我,不是一句话两句话能讲清楚的问题,我都会抽空写一个教程的。
现在还欠着一个问题没回复:按需加载所有的文件。之前教程里面只写了按需加载controller文件。
这个现在有一个方案,我们公司自己的产品正在使用,正在开发中。效果和性能要等个一两周的测试之后才能知道。
要是方案测试通过了,我就把方案分享给大家。
接着进入今天的主题。
图片延时加载,为什么要延时加载图片呢?
大家都都知道在较长的列表页绘制时会使得应用程序变得卡顿。不仅在界面之内的图片需要绘制,在页面之外的图片也要绘制,如果一次请求的图片数量较少,看不出有什么性能问题,但是当图片较多且较大时,达到一定的量级,那么就会影响程序的性能了。使用延时加载图片,当你滚动到相应的子项时,该子项的图片才开始加载。这样子实现,当前可视区域的图片显示正常,而可视区域之外的图片却不加载,在一定程度上能够加快页面的绘制,对于ionic的应用长列表页应该算是一个比较常见的部件了。所以在ionic项目中使用图片延时加载技术是一个不错的选择。
根据需求分析,我们能够理解,首先延时加载图片就是一开始不加载,而在滚动容器之后,按条件开始加载图片,条件就是该图片在可视区域内。
所以首先我们要给图片所在容器绑定一个滚动事件,当视图滚动时抛出加载图片的事件。
这个demo是我上次不知道写那个教程之后的Demo,懒得新建一个项目,大家关注我截图的这些地方就好了。
这里就是把这个外层承载的容器定义为lazy-scroll,用于在指令里面绑上事件。
lazyScrollEvent事件。
接着我们实现按条件加载图片,当图片在现实区域之内。
给对象绑上监听事件
这里有几个地方可以关注的。一个是isInView()条件用来判断对象是否在屏幕可视区域内。一个就是这个监听事件的返回对象被存储为deregistration,然后在处理加载图片之后被重新执行了绑定。其实做过web端通信的应该了解,web是需要保活机制确定事件的活性。然而这里并没有什么作用,当图片加载完成之后,并不需要再次执行加载。但是会有一个问题,可能加载是失败的时候,再次进入视图可以再次加载。
判断是否在屏幕的可视区域之内的方法,有点类似编写游戏时常使用到的碰撞检测。
------------------------------------------略------------------------
额。有工作了。没时间写了,将就看吧。
---------------------------------------略--------------------------------
image-lazy-src就可以了,记得给他们的承载容器加上lazy-scroll。
我这里是直接把文件放在app.js里面了。大家可以根据自己的文件加载规范把文件分离出来。
1. app.directive('lazyScroll', ['$rootScope', '$timeout',
2. function($rootScope, $timeout) {
3. return {
4. 'A',
5. function ($scope, $element) {
6. var scrollTimeoutId = 0;
7. function () {
8. 'lazyScrollEvent');
9. };
10. 'scroll', function () {
11. $timeout.cancel(scrollTimeoutId);
12. scrollTimeoutId = $timeout($scope.invoke, 0);
13. });
14. }
15. };
16. }])
然后是
1. app.directive('imageLazySrc', ['$document', '$timeout', '$ionicScrollDelegate', '$compile',
2. function ($document, $timeout, $ionicScrollDelegate, $compile) {
3. return {
4. 'A',
5. scope: {
6. "@lazyScrollResize",
7. "@imageLazyBackgroundImage"
8. },
9. function ($scope, $element, $attributes) {
10. if (!$attributes.imageLazyDistanceFromBottomToLoad) {
11. $attributes.imageLazyDistanceFromBottomToLoad = 0;
12. }
13. if (!$attributes.imageLazyDistanceFromRightToLoad) {
14. $attributes.imageLazyDistanceFromRightToLoad = 0;
15. }
16. if ($attributes.imageLazyLoader) {
17. var loader = $compile('<div class="image-loader-container"><ion-spinner class="image-loader" icon="' + $attributes.imageLazyLoader + '"></ion-spinner></div>')($scope);
18. $element.after(loader);
19. }
20. var deregistration = $scope.$on('lazyScrollEvent', function () {
21. //console.log('scroll');
22. if (isInView()) {
23. loadImage();
24. deregistration();
25. }
26. }
27. );
28. function loadImage() {
29. "load", function (e) {
30. if ($attributes.imageLazyLoader) {
31. loader.remove();
32. }
33. if ($scope.lazyScrollResize == "true") {
34. //Call the resize to recalculate the size of the screen
35. $ionicScrollDelegate.resize();
36. }
37. });
38. if ($scope.imageLazyBackgroundImage == "true") {
39. var bgImg = new Image();
40. function () {
41. if ($attributes.imageLazyLoader) {
42. loader.remove();
43. }
44. 'url(' + $attributes.imageLazySrc + ')'; // set style attribute on element (it will load image)
45. if ($scope.lazyScrollResize == "true") {
46. //Call the resize to recalculate the size of the screen
47. $ionicScrollDelegate.resize();
48. }
49. };
50. bgImg.src = $attributes.imageLazySrc;
51. else {
52. // set src attribute on element (it will load image)
53. }
54. }
55. function isInView() {
56. var clientHeight = $document[0].documentElement.clientHeight;
57. var clientWidth = $document[0].documentElement.clientWidth;
58. var imageRect = $element[0].getBoundingClientRect();
59. return (imageRect.top >= 0 && imageRect.top <= clientHeight + parseInt($attributes.imageLazyDistanceFromBottomToLoad))
60. && (imageRect.left >= 0 && imageRect.left <= clientWidth + parseInt($attributes.imageLazyDistanceFromRightToLoad));
61. }
62. '$destroy', function () {
63. deregistration();
64. });
65. function () {
66. if (isInView()) {
67. loadImage();
68. deregistration();
69. }
70. }, 500);
71. }
72. };
73. }]);
还有其他问题的直接小窗问我吧!本来想说的明白点的,这边工作催的紧。。。
项目Demo地址:http://pan.baidu.com/s/1eSM4te6
周五的时候有个朋友让我写一个关于图片延时加载的教程,
直到今天才有空编辑,这阶段真的是很忙,公众号都变成僵尸号了。
实在是对不起大家。
有人喜欢我的教程,可能我总习惯了用比较简单容易理解的方式去描述这些东西。
别的就不多说了,大家遇到什么问题。
可以直接联系我,不是一句话两句话能讲清楚的问题,我都会抽空写一个教程的。
现在还欠着一个问题没回复:按需加载所有的文件。之前教程里面只写了按需加载controller文件。
这个现在有一个方案,我们公司自己的产品正在使用,正在开发中。效果和性能要等个一两周的测试之后才能知道。
要是方案测试通过了,我就把方案分享给大家。
接着进入今天的主题。
图片延时加载,为什么要延时加载图片呢?
大家都都知道在较长的列表页绘制时会使得应用程序变得卡顿。不仅在界面之内的图片需要绘制,在页面之外的图片也要绘制,如果一次请求的图片数量较少,看不出有什么性能问题,但是当图片较多且较大时,达到一定的量级,那么就会影响程序的性能了。使用延时加载图片,当你滚动到相应的子项时,该子项的图片才开始加载。这样子实现,当前可视区域的图片显示正常,而可视区域之外的图片却不加载,在一定程度上能够加快页面的绘制,对于ionic的应用长列表页应该算是一个比较常见的部件了。所以在ionic项目中使用图片延时加载技术是一个不错的选择。
根据需求分析,我们能够理解,首先延时加载图片就是一开始不加载,而在滚动容器之后,按条件开始加载图片,条件就是该图片在可视区域内。
所以首先我们要给图片所在容器绑定一个滚动事件,当视图滚动时抛出加载图片的事件。
这个demo是我上次不知道写那个教程之后的Demo,懒得新建一个项目,大家关注我截图的这些地方就好了。
这里就是把这个外层承载的容器定义为lazy-scroll,用于在指令里面绑上事件。
lazyScrollEvent事件。
接着我们实现按条件加载图片,当图片在现实区域之内。
给对象绑上监听事件
这里有几个地方可以关注的。一个是isInView()条件用来判断对象是否在屏幕可视区域内。一个就是这个监听事件的返回对象被存储为deregistration,然后在处理加载图片之后被重新执行了绑定。其实做过web端通信的应该了解,web是需要保活机制确定事件的活性。然而这里并没有什么作用,当图片加载完成之后,并不需要再次执行加载。但是会有一个问题,可能加载是失败的时候,再次进入视图可以再次加载。
判断是否在屏幕的可视区域之内的方法,有点类似编写游戏时常使用到的碰撞检测。
------------------------------------------略------------------------
额。有工作了。没时间写了,将就看吧。
---------------------------------------略--------------------------------
image-lazy-src就可以了,记得给他们的承载容器加上lazy-scroll。
我这里是直接把文件放在app.js里面了。大家可以根据自己的文件加载规范把文件分离出来。
1. app.directive('lazyScroll', ['$rootScope', '$timeout',
2. function($rootScope, $timeout) {
3. return {
4. 'A',
5. function ($scope, $element) {
6. var scrollTimeoutId = 0;
7. function () {
8. 'lazyScrollEvent');
9. };
10. 'scroll', function () {
11. $timeout.cancel(scrollTimeoutId);
12. scrollTimeoutId = $timeout($scope.invoke, 0);
13. });
14. }
15. };
16. }])
然后是
1. app.directive('imageLazySrc', ['$document', '$timeout', '$ionicScrollDelegate', '$compile',
2. function ($document, $timeout, $ionicScrollDelegate, $compile) {
3. return {
4. 'A',
5. scope: {
6. "@lazyScrollResize",
7. "@imageLazyBackgroundImage"
8. },
9. function ($scope, $element, $attributes) {
10. if (!$attributes.imageLazyDistanceFromBottomToLoad) {
11. $attributes.imageLazyDistanceFromBottomToLoad = 0;
12. }
13. if (!$attributes.imageLazyDistanceFromRightToLoad) {
14. $attributes.imageLazyDistanceFromRightToLoad = 0;
15. }
16. if ($attributes.imageLazyLoader) {
17. var loader = $compile('<div class="image-loader-container"><ion-spinner class="image-loader" icon="' + $attributes.imageLazyLoader + '"></ion-spinner></div>')($scope);
18. $element.after(loader);
19. }
20. var deregistration = $scope.$on('lazyScrollEvent', function () {
21. //console.log('scroll');
22. if (isInView()) {
23. loadImage();
24. deregistration();
25. }
26. }
27. );
28. function loadImage() {
29. "load", function (e) {
30. if ($attributes.imageLazyLoader) {
31. loader.remove();
32. }
33. if ($scope.lazyScrollResize == "true") {
34. //Call the resize to recalculate the size of the screen
35. $ionicScrollDelegate.resize();
36. }
37. });
38. if ($scope.imageLazyBackgroundImage == "true") {
39. var bgImg = new Image();
40. function () {
41. if ($attributes.imageLazyLoader) {
42. loader.remove();
43. }
44. 'url(' + $attributes.imageLazySrc + ')'; // set style attribute on element (it will load image)
45. if ($scope.lazyScrollResize == "true") {
46. //Call the resize to recalculate the size of the screen
47. $ionicScrollDelegate.resize();
48. }
49. };
50. bgImg.src = $attributes.imageLazySrc;
51. else {
52. // set src attribute on element (it will load image)
53. }
54. }
55. function isInView() {
56. var clientHeight = $document[0].documentElement.clientHeight;
57. var clientWidth = $document[0].documentElement.clientWidth;
58. var imageRect = $element[0].getBoundingClientRect();
59. return (imageRect.top >= 0 && imageRect.top <= clientHeight + parseInt($attributes.imageLazyDistanceFromBottomToLoad))
60. && (imageRect.left >= 0 && imageRect.left <= clientWidth + parseInt($attributes.imageLazyDistanceFromRightToLoad));
61. }
62. '$destroy', function () {
63. deregistration();
64. });
65. function () {
66. if (isInView()) {
67. loadImage();
68. deregistration();
69. }
70. }, 500);
71. }
72. };
73. }]);
还有其他问题的直接小窗问我吧!本来想说的明白点的,这边工作催的紧。。。
项目Demo地址:http://pan.baidu.com/s/1eSM4te6