Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Angular文字折叠展开组件的原理分析 #28

Closed
Wscats opened this issue Jun 8, 2016 · 4 comments
Closed

Angular文字折叠展开组件的原理分析 #28

Wscats opened this issue Jun 8, 2016 · 4 comments
Labels

Comments

@Wscats
Copy link
Owner

Wscats commented Jun 8, 2016

自己写了个Angular的文字折叠组件,这种组件其实很多地方都能用到效果如下
展开后的效果
image
折叠后的效果
image
先放全部代码,使用的时候只需要把自己需要展现的文字{{designer.des}}替换成自己所在路由器所需要绑定的数据即可

.directive('textfold', function() {
        return {
            restrict: 'EA',
            template: '<p style="font-size: 14px; border-left:5px solid #dddddd; padding: 15px; padding-bottom: 10px; margin-bottom: 15px; margin-top: 15px;">' + '<span id="textfold" style="display:block; overflow:hidden;">{{designer.des}}</span>' + '<br />' + '<span style="color: #1bb7ac; position: relative; bottom: 10px;" ng-click="more(this)">{{isMore}}</span>' + '</p>',
            link: function(scope, element, attrs) {
                var height;
                var maxheight;
                function textfold() {
                    height = angular.element('#textfold').height();
                    maxheight = angular.element('#textfold').height();
                }
                scope.$watch('designer.des', function(data) {
                    if (data) {
                        textfold();
                    }
                })
                var isExtend = true;
                scope.isMore = "折叠";
                scope.more = function() {
                    var minheight = 23;
                    if (isExtend) {
                        if (height >= minheight) {
                            document.getElementById('textfold').style.height = height + "px";
                            setTimeout(function() {
                                scope.more();
                            }, 1);
                            height -= 10;
                        } else {
                            scope.isMore = "查看更多...";
                            scope.$apply();
                            isExtend = !isExtend;
                            height += 10;
                        }
                    } else {
                        if (height <= maxheight) {
                            document.getElementById('textfold').style.height = height + "px";
                            setTimeout(function() {
                                scope.more();
                            }, 1);
                            height += 10;
                        } else {
                            scope.isMore = "折叠";
                            scope.$apply();
                            isExtend = !isExtend;
                            height -= 10;
                        }
                    }
                }
            }
        }
    })

下面我一句句的分析这个组件的思路
首先肯定是定义好Angular该组件化的模板和使用模式

restrict: 'EA',
            template: '<p style="font-size: 14px; border-left:5px solid #dddddd; padding: 15px; padding-bottom: 10px; margin-bottom: 15px; margin-top: 15px;">' + '<span id="textfold" style="display:block; overflow:hidden;">{{designer.des}}</span>' + '<br />' + '<span style="color: #1bb7ac; position: relative; bottom: 10px;" ng-click="more(this)">{{isMore}}</span>' + '</p>',

EA为,使用元素和属性的方法都可以在DOM里面展现这个插件,既我可以这样
<textfold></textfold>也可以这样<div textfold="Wsscat"></div>的形式来复用该组件
后面我们使用link定义一个函数

var height;
var maxheight;

这两个变量一个是此时折叠完后的高度(这个是在展开变成折叠的过程中不断发生变化的,最后折叠后就是等于minheight),一个文字完全展开时候获取的高度

function textfold() {
                    height = angular.element('#textfold').height();
                    maxheight = angular.element('#textfold').height();
                }
                scope.$watch('designer.des', function(data) {
                    if (data) {
                        textfold();
                        scope.more();
                    }
                })

这两句其实很重要的,当我们获取文字的高度时候,是必须要捕获文字的变化(文字完全渲染后的高度),所以我们用scope.$watch来捕获designer.des的变化,当data不为空,即文字已渲染后

if (data) {
                        textfold();
                    }

再去执行回调函数textfold来获取当前文字的高度,暂时试过这种方法可以获取到文字渲染后的高度
如果顺序执行而不是回调执行
angular.element('#textfold').height()
只会拿到span标签的默认高度而已
这里还可以添加个小技巧,增加一句scope.more();

if (data) {
                        textfold();
                        scope.more();
                    }

这样就可以让它页面渲染完后先展开,然后再折叠,那么我们就在进来页面的时候默认是折叠的状态了,在程序里面,写这种效果,一般是先让它文字展开获取到高度再返回成折叠状态,来达到进来页面就是折叠的文字状态效果
var isExtend = true;这句是让下面的scope.more进入第一个分支折叠状态

setTimeout(function() {
                                scope.more();
                            }, 1);

这句是一句递归,其实就相当于实现jQuery的animate的文字框伸缩动画,只是这里用了一个递归来实现不断进来判断状态从而改变height值
然后通过改变scope.isMore改变它是查看更多...还是折叠
由于这里是用DOM操作
document.getElementById('textfold').style.height = height + "px";
下面这里最好加多一句
scope.$apply();
来获取DOM的变化
不过这一句要留意会有可能出现以下错误**$digest already in progress**
image
从字面的意思可以知道,就是$digest 或者 $apply 已经在一个digest的进程里了
可以有以下解决方法
在scope.$apply();外加一个定时器

setTimeout(function() {
                                scope.$apply();
                            }, 1);

或者加个判断

if (scope.$root.$$phase != '$apply' && scope.$root.$$phase != '$digest') {
                                scope.$apply();
                            }

其实大概思路就是很简单的,其他一些地方也是很容易理解有什么好的方法欢迎推荐,或者文中有什么错漏或者不足还请多多留言告知,谢谢

@Wscats
Copy link
Owner Author

Wscats commented Jun 12, 2016

directive('textfold', function() {
        return {
            restrict: 'EA',
            template: '<p style="font-size: 14px; border-left:5px solid #dddddd; padding: 10px; padding-bottom: 10px; margin-bottom: 15px; margin-top: 15px;">' + '<span id="textfold" style="display:block; overflow:hidden;">{{designer.des}}</span>' + '<br id="br" />' + '<span id="isMore" style="color: #1bb7ac; position: relative; bottom: 10px;" ng-click="more(this)">{{isMore}}</span>' + '</p>',
            link: function(scope, element, attrs) {
                var height;
                var maxheight;
                var minheight = 23;
                function textfold() {
                    height = angular.element('#textfold').height();
                    maxheight = angular.element('#textfold').height();
                }
                scope.$watch('designer.des', function(data) {
                    if (data) {
                        document.getElementById('textP').style.display = 'block';
                        textfold();
                        scope.more();
                        scope.status();
                    }else{
                        document.getElementById('textP').style.display = 'none';
                    }
                })
                var isExtend = true;
                scope.isMore = "查看更多";
                scope.status =function(){
                    if(maxheight<=minheight){
                        //scope.isMore = "";
                        document.getElementById('isMore').style.display = 'none';
                        document.getElementById('br').style.display = 'none';
                    }
                }
                scope.more = function() {
                    if (isExtend) {
                        if (height >= minheight) {
                            document.getElementById('textfold').style.height = height + "px";
                            setTimeout(function() {
                                scope.more();
                            }, 1);
                            height -= 10;
                        } else {
                            scope.isMore = "查看更多...";
                            scope.$apply();
                            isExtend = !isExtend;
                            height += 10;
                            document.getElementById('textfold').style.height = "20px";
                        }
                    } else {
                        if (height <= maxheight) {
                            document.getElementById('textfold').style.height = height + "px";
                            setTimeout(function() {
                                scope.more();
                            }, 1);
                            height += 10;
                        } else {
                            scope.isMore = "折叠";
                            scope.$apply();
                            isExtend = !isExtend;
                            height -= 10;
                        }
                    }
                }
            }
        }
    });

这里我后来再改进一下,就是增加设置默认展示的高度
var minheight = 23;
这个是可以自己改的,我这里23px,刚好是font-size为14px,height为20px时候显示一行

@Wscats Wscats added the notes label Jun 15, 2016
@Wscats
Copy link
Owner Author

Wscats commented Jun 28, 2016

修复移动端的折叠文字出现的问题
主要增加{{isMore2}}来展示折叠文字,如果绑定在{{isMore}}上面在移动端会出现部分显示问题

directive('textfold', function() {
        return {
            restrict: 'EA',
            template: '<p style="font-size: 14px; border-left:5px solid #dddddd; padding: 10px; padding-bottom: 10px; margin-bottom: 15px; margin-top: 15px;">' + '<span id="textfold" style="display:block; overflow:hidden;">{{designer.des}}</span>' + '<br id="br" />' + '<span id="isMore" style="color: #1bb7ac; position: relative; bottom: 10px;" ng-click="more(this)">{{isMore}}</span>' +  '<span id="isMore2" style="color: #1bb7ac; position: relative; bottom: 10px;" ng-click="more(this)">{{isMore2}}</span>' +'</p>',
            link: function(scope, element, attrs) {
                var height;
                var maxheight;
                var minheight = 23;

                function textfold() {
                    height = angular.element('#textfold').height();
                    maxheight = angular.element('#textfold').height();
                }
                scope.$watch('designer.des', function(data) {
                    if (data) {
                        document.getElementById('textP').style.display = 'block';
                        textfold();
                        scope.more();
                        scope.status();
                    } else {
                        document.getElementById('textP').style.display = 'none';
                    }
                })
                var isExtend = false;
                scope.isMore = "查看更多...";
                scope.status = function() {
                    if (maxheight <= minheight) {
                        document.getElementById('isMore').style.display = 'none';
                        document.getElementById('br').style.display = 'none';
                        document.getElementById('isMore2').style.display = 'none';
                        document.getElementById('br').style.display = 'none';
                    }
                }
                scope.more = function() {
                    if (isExtend) {
                        if (height >= minheight) {
                            document.getElementById('textfold').style.height = height + "px";
                            setTimeout(function() {
                                scope.more();
                            }, 1);
                            height -= 10;
                        } else {
                            scope.isMore = "查看更多";
                            scope.isMore2 = "";
                            if (scope.$root.$$phase != '$apply' && scope.$root.$$phase != '$digest') {
                                scope.$apply();
                            }
                            isExtend = !isExtend;
                            height += 10;
                            document.getElementById('textfold').style.height = "20px";
                        }
                    } else {
                        if (height <= maxheight) {
                            document.getElementById('textfold').style.height = height + "px";
                            setTimeout(function() {
                                scope.more();
                            }, 1);
                            height += 10;
                        } else {
                            scope.isMore = "";
                            scope.isMore2 = "折叠";
                            if (scope.$root.$$phase != '$apply' && scope.$root.$$phase != '$digest') {
                                scope.$apply();
                            }
                            isExtend = !isExtend;
                            height -= 10;
                        }
                    }
                }
            }
        }
    });

@qq452261789
Copy link

qq452261789 commented Nov 22, 2016

这逼高大上

@pengril
Copy link

pengril commented Jan 17, 2018

厉害了

@Wscats Wscats closed this as completed Aug 22, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants