背景
最近做项目遇到一个需求,要做选择优惠券后金额试算的一个金额切换的动画效果,如图所示:
然后我把做了几个小demo用于演示:
技术
所用技术为:React.js、css3
本效果用的是CSS3 transition 属性
transition 属性设置元素当过渡效果,四个简写属性为:
| 值 | 描述 |
|---|---|
| transition-property | 指定CSS属性的name,transition效果 |
| transition-duration | transition效果需要指定多少秒或毫秒才能完成 |
| transition-timing-function | 指定transition效果的转速曲线 |
| transition-delay | 定义transition效果开始的时候 |
实现
我把0~9的数字全部列出来,排成一列,然后再根据需要展示的数字去对这个数字列进行定位,再利用transition过渡效果实现数字切换动画。 如图所示:
布局
首先是布局代码,将0~9的数字排列出来
<div className="number-animation-wrap">
<div className="number-animation-wrap-hidden">0</div>
<div className="number-animation" style={style}>
<div className="number">0</div>
<div className="number">1</div>
<div className="number">2</div>
<div className="number">3</div>
<div className="number">4</div>
<div className="number">5</div>
<div className="number">6</div>
<div className="number">7</div>
<div className="number">8</div>
<div className="number">9</div>
</div>
</div>
样式
设置样式:
overflow: hidden; 超出隐藏
transition: top 0.2s实现过渡动画
@defaultSize: 16;
.number-animation-wrap {
position: relative;
display: inline-block;
overflow: hidden;
.number-animation {
position: absolute;
left: 0;
top: 0;
height: auto;
transform-origin: 0 0;
transition: top 0.2s;//过渡属性的名称 过渡动画所需的时间
}
.number,.number-animation-wrap-hidden {
line-height: unit((36/@defaultSize),rem);
font-size: unit((36/@defaultSize),rem);
font-weight: bold;
text-align: center;
}
.number-animation-wrap-hidden{
visibility: hidden;
}
}
控制定位
根据要显示的数字value计算出要偏移的距离, 利用top对布局中的数字列表进行定位,展示对应的数字:
let style = {
top: (-1 * value * this.lineHeight / this.defaultSize) + 'rem'
}
随着value的变化,动态地生成样式并设置到数字列表div上
<div className="number-animation" style={style}>
调用
我将其封装成了组件,可以很方便的调用,可以根据需要进行组装:
import NumberAnimation from "../../src/numberAnimation";
<div style={{textAlign:"center",fontSize: "36px",fontWeight: "bold"}}>
<div style={{margin:"2rem 0"}}>
倒计时 <NumberAnimation value={this.state.number} />
</div>
<div style={{margin:"2rem 0"}}>
计时
{
this.state.number2.toFixed(2).toString().replace(".",":").split("").map((numberItem, index) => {
return (
<NumberAnimation key={index} value={numberItem} />
)
})
}
</div>
<div style={{margin:"2rem 0"}}>
访问人数
{
this.state.number4.toString().split("").map((numberItem, index) => {
return (
<NumberAnimation key={index} value={numberItem} />
)
})
}
</div>
<div style={{margin:"2rem 0"}}>
{
this.state.number.toString().split("").map((numberItem, index) => {
return (
<NumberAnimation key={index} value={numberItem} />
)
})
}
</div>
<div style={{margin:"2rem 0"}}>
{
this.state.number2.toFixed(2).toString().split("").map((numberItem, index) => {
return (
<NumberAnimation key={index} value={numberItem} />
)
})
}
</div>
<div style={{margin:"2rem 0"}}>
{
this.state.number3.toFixed(2).toString().split("").map((numberItem, index) => {
return (
<NumberAnimation key={index} value={numberItem} />
)
})
}
<button onClick={()=>{this.setState({
number3: Math.random()*Math.random()*100
})}}> 点击切换数字</button>
</div>
</div>
全部代码
js文件
//numberAnimationPage.js
import React, { Component } from 'react';
import './style.less'
class NumberAnimation extends Component {
constructor(props) {
super(props);
this.lineHeight = 36;
this.defaultSize = 16;
}
render() {
const { value } = this.props;
if (isNaN(value)) {//非数字
return (
<div className="number-animation-wrap">
<div className="number-animation-wrap-hidden">.</div>
<div className="number-animation">
<div className="number">{value}</div>
</div>
</div>
)
}
let style = {
top: (-1 * value * this.lineHeight / this.defaultSize) + 'rem'
}
return (
<div className="number-animation-wrap">
<div className="number-animation-wrap-hidden">0</div>
<div className="number-animation" style={style}>
<div className="number">0</div>
<div className="number">1</div>
<div className="number">2</div>
<div className="number">3</div>
<div className="number">4</div>
<div className="number">5</div>
<div className="number">6</div>
<div className="number">7</div>
<div className="number">8</div>
<div className="number">9</div>
</div>
</div>
)
}
}
export default NumberAnimation;
样式文件
//style.less
@defaultSize: 16;
.number-animation-wrap {
position: relative;
display: inline-block;
overflow: hidden;
.number-animation {
position: absolute;
left: 0;
top: 0;
height: auto;
transform-origin: 0 0;
transition: top 0.2s;//过渡属性的名称 过渡动画所需的时间
}
.number,.number-animation-wrap-hidden {
line-height: unit((36/@defaultSize),rem);
font-size: unit((36/@defaultSize),rem);
font-weight: bold;
text-align: center;
}
.number-animation-wrap-hidden{
visibility: hidden;
}
}
调用示例文件
//numberAnimationPage.js
import React, { Component } from "react";
import NumberAnimation from "../../src/numberAnimation";
class NumberAnimationPage extends Component {
constructor(props) {
super(props);
this.state = {
number: 0,
number2: 0,
number3: 0
};
this.number = 0;
this.number2 = 0;
}
componentDidMount() {
setInterval(() => {
console.log(1)
this.setState({
number: this.number++,
number2: this.number2 += 0.01
})
}, 1000)
}
render() {
return (
<div style={{textAlign:"center"}}>
<div style={{margin:"2rem 0"}}>
<NumberAnimation value={this.state.number} />
</div>
<div style={{margin:"2rem 0"}}>
{
this.state.number.toString().split("").map((numberItem, index) => {
return (
<NumberAnimation key={index} value={numberItem} />
)
})
}
</div>
<div style={{margin:"2rem 0"}}>
{
this.state.number2.toFixed(2).toString().split("").map((numberItem, index) => {
return (
<NumberAnimation key={index} value={numberItem} />
)
})
}
</div>
<div style={{margin:"2rem 0"}}>
{
this.state.number3.toFixed(2).toString().split("").map((numberItem, index) => {
return (
<NumberAnimation key={index} value={numberItem} />
)
})
}
<button onClick={()=>{this.setState({
number3: Math.random()*Math.random()*100
})}}> 点击切换数字</button>
</div>
</div>
);
}
}
module.exports = NumberAnimationPage;
总结
- 本组件是以单个数字形式进行展示,可以供使用者灵活组装
- 组件中过滤了传入值为非数字的情况,支持用于展示整数、小数
大家有什么建议,或者其他的实现思路,欢迎在评论区分享。