我最近做的React项目中需要做这样的一个组件来展示数据。
根据不同的数值,这个蓝色的条显示的宽度不同。这个其实很简单,用正常的react的编程思维,我只要根据数据动态的计算它的宽度,循环生成相应的节点就行了。 其中的部分核心react代码如下
{data && data.length > 0
? data.map((item, index) => (
<div className={styles.item} key={index}>
<div className={styles.itemTop}>
<span>{item.label}</span>
<span className={styles.num}>{item.value}</span>
</div>
<p className={styles.progressBar}>
<span
className={styles.inner}
style={{ background, width: getWidth(item.value)}
/>
</p>
</div>
))
: null}
复制代码
这样就实现如上图所示的功能显示,但是现在又有个需求,就是需要那个蓝色的条刚加载出来的时候动起来。
transition
首当其冲的当然是css的transition属性,然后把它加入代码中
.inner {
width: 0;
transition: width 0.6s ease;
}
复制代码
想象之中,一切都是这么简单, 可是这个动画并没有生效。
然后我就回过头来思考它 为什么没有生效?
transition这个属性只有在width属性发生改变的时候才会产生作用。现在只能说明span的width并没有发生改变。 这时候就要说到我的这段代码了,它是一边计算宽度,一边渲染节点的,也就是说它节点生成的时候,宽度就已经定好了。所以transition当然不会生效了
现在我要怎么改进这段代码使动画生效呢?
我只能先让节点生成好,然后再改变它的宽度了。
这就想到了react中的ref属性了,这个属性接受字符串或者一个函数,而这个函数在节点加载完成后或者节点销毁时都会触发。等span节点渲染完成后,我就可以通过这个函数返回的真实的dom节点,操作它改变宽度了,这正是我所需要的。
有了思路,开始改进代码。
{data && data.length > 0
? data.map((item, index) => (
<div className={styles.item} key={index}>
<div className={styles.itemTop}>
<span>{item.label}</span>
<span className={styles.num}>{item.value}</span>
</div>
<p className={styles.progressBar}>
<span
className={styles.inner}
style={{ background }}
ref={n => {
if (n && n.style) {
n.style.width = `${getWidth(item.value)}px`;
}
}}
/>
</p>
</div>
))
: null}
复制代码
然后打开浏览器看结果,果然成功了。 效果如下。