前言
最近使用d3 + React制作一个可视化系统。d3与React的思想有些违背,d3直接操作DOM,React有一层Fiber Tree(Virtual DOM)再通过render函数渲染。
但是两者通过一些技巧也可以相结合,方式主要有两种:
- 在React合适的生命周期,d3直接操作DOM进行绘制,即d3渲染UI
- React利用d3的布局算法,在render中设置svg的标签位置,即React渲染UI。业界也有很多以这种形式渲染的可视化react库,如react-vis,但是个人感觉多数并不是特别活跃,同时也缺乏扩展性。
各种方式有各种方式的好处,在此不做赘述
此系统需要实现交互效果,所以使用d3渲染UI。
问题
系统开发过程中,树图需要实现交互效果:鼠标mousemove,悬浮弹窗显示具体信息。
这个悬浮窗设置了position:fixed用于定位,定位具体位置既可以给d3做,也可以给react做。
为了提升开发效率(赶工期)此次实现中我把他交给了react,为树图添加mousemove监听,获取对应位置,setState传值给div控制悬浮窗。因此后面的问题及思考也是针对react。
但也随之带来了两个问题:
- 未处理好组件层级关系,setStaet导致componentDidUpdate中的d3代码高频重绘,性能很差
- mousemove下,setState多次调用效果合并,导致悬浮窗位移是闪动的,用户体验差
以下针对两个问题记录下实践中的思考
解决
1.未处理好组件层级关系,setStaet导致d3高频重绘,性能很差
一开始为切分组件,结构如下:
<DetailTreeMap />
constructor(props) {
super(props);
this.staet = {
left:...
top:...
//...
}
}
componentDidUpdate(){
//...
//...
.on("mousemove",()=>{
//...
this.setSate({
left:X,
top:Y
})
})
}
render(){
//......
return(<div>
{/* d3绘图区域 */}
<svg></svg>
{/* 悬浮窗div */}
<div style={{left:state.left, top:state.top}}>
//......
</div>
</div>)
}
每次mousemove时都会调用setState,后续触发一系列的生命周期函数,而我们的绘图代码放在componentDidUpdate之中,导致了高频的d3布局计算、直接操作svg与dom节点等等,性能非常差。
这里我想到两种方法解决这个问题:
- 绘图代码添加更多的条件判断
- 将悬浮窗div外提为单独的组件不放置在<DetailTreeMap>中,设置<DetailTreeMap>为PureComponent,以此减少render函数及ComponentDidUpdate的触发
以上两种方法的最终目的都是减少ComponentDidUpdate中d3绘图代码的并不必要执行。
本次实现中,由于ComponentDidUpdate中已经存在较多的条件判断,所以我选择了第二种方法。如此,组件结构如下
<CityDetail>
<DetailTreeMap />
<TreeMapWindow left={state.left} top={state.top}/>
通过父级组件CityDetail设置state及props传递值。
2.mousemove下,setState多次调用效果合并,导致悬浮窗位移是闪动的,用户体验差
这个问题并不是性能问题,而是react setState自身的优化所导致的。
但我们的最终目的就是提升用户体验,同时不会损耗太多的性能。
解决方法非常简单,根本原因是间断性的div位移,我们直接给这些间断性的位移加上动画就好了
.detail-window {
position: fixed;
width: 200px;
height: 100px;
transition: top 200ms, left 200ms;
background: rgba(0, 0, 0, 0.6);
padding: 10px;
}
如此,尽管触发mousemove也不会有悬浮框跳动的问题。
思考及总结
本次案例中实现悬浮窗跟随的方式还有很多,比如使用d3实现,使用React渲染UI等等,但是由于时间关系还没来得及一一尝试。
此次案例中给自己带来的更多思考是React与d3一起使用时,d3渲染UI的注意事项,如何防止不必要的重绘,如何提升开发效率而又不失用户体验......
私以为对组件更加合适的层级划分及componentDidUpdate中的绘制代码添加条件可一定程度上解决问题。
见识可能还甚少,欢迎大佬们指教交流