1:需求
原需求是要做个聊天框效果,但是聊天框是从下到上的..原本一般的从上到下聊天框就很简单,我只要把动画效果封装到组件里面,然后延迟给数组添加数据就可以了,类似如下这样
但是需求咋可能这么简单呢….我最终需要的效果长这样….不仅如此,这个聊天框组件距离页面的header的margin是固定的,那么聊天框整个组件一开始的初始位置就应该是聊天框组件的高度加上某个固定的margin-top。
要做聊天框上移效果,第一次上移的距离是loading动画的距离,第二次上移的距离是下个聊天框高度减去loading高度的距离,loading动画高度固定可写死,但是下个聊天框的高度就免不了需要用api获得高度。
反正就是乱七八糟的一大堆属性需要用js动态的获得。组件的初始位置的margin-top还好,在didMount方法里面直接设置就完事儿了。但是css的动画定义是hard code,所以无法直接动态设置css动画的移动高度。
解决方案:
1、动态添加css。在上移高度计算好之后在document上面动态添加css样式
2、使用原生的js动画,可用的API有requestAnimaionFrame,和setInterval,区别是requestAnimationFrame是固定60帧移动,setInterval是可设置移动帧数。自己写动画太费时间,所以最后是使用了anime.js来做动画效果。
使用的react,根据react生命周期,需要在render之后才能dom的确切高度,使用ref获得节点,在componentDidMount里面获得节点高度进行计算。计算出div的margin-top和以及需要的anime。
2:问题
3.1 html高度问题
问题来了,在componentDidMount获得的高度是未应用css时,原生html的高度,即无法获得css渲染后的dom高度。
问题所在:这是因为webpack在style-loader, less-loader, css-loader 加入sourceMap打包的时候先运行了生成dom的js,再通过js引入css 。去除即可(其实找了个plugin,mini-css-extract-plugin这个也可以解决先运行js再运行css的问题的)
3.2:还是html高度问题
即使是去除了source-map获得的dom高度依然是有问题的。原因在于,使用的字体是谷歌的第三方字体,而第三方字体渲染的机制如下:
第三方字体加载未完成之前,所有的字体样式的css并不应用,同时使用浏览器本地字体库代替第三方字体,当第三方字体加载成功后,第三方字体会替代本地字体。
很不幸,debug发现js执行到获取dom高度的时候第三方字体并没有加载成功,所以获取到dom的高度是本地字体渲染时的高度,与最终呈现的高度有所误差。最终找到一个fontfaceobserver的插件使用,插件会观察制定的字体加载情况,提供了一个加载成功的promise,将所有计算以及获取代码放到这个promise中即可(目前不知道直接下载font是不是有效的解决方案,主要不知道怎么才能把font文件放在服务器上= =)。
3.3:组件生命周期
本以为到此为止,运行之后看见控制台有抛出一个警告。大意是,不要在componentDidMount使用延时的setState()。因为可能会出现promise未返回,或者setTimeout时间未到,component就已经Unmount了,这个时候执行setState去设置div的margin显然是不行的。
解决方案:动态计算出来的margin值不放在state里面,而是存储在redux中,然后作为props传入组件,在promise里面调用某个action,改变margin值这样的话,即使组件已经unmount了,更改redux值也不会有什么问题。
至此,动画效果问题基本解决。遗留的唯一一个问题是可不可以通过下载font把fontfaceobserver干掉….但是我这里又没办法去弄服务器,就很烦。