背景
最近做项目遇到一个需求,要做选择优惠券后金额试算的一个金额切换的动画效果,如图所示:
然后我把做了几个小demo用于演示:
技术
所用技术为:React.js、css3
本效果用的是CSS3transition属性
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文件
//NumberAnimation.jsimport React, { Component } from 'react';import '../../../less/numberAnimation.less'class NumberAnimation extends Component {constructor(props) {super(props);this.state = {height : 36}}render() {const { value } = this.props;if (isNaN(value)) {//非数字if (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>)}else {return ''}}let height = document.getElementsByClassName("number").length && document.getElementsByClassName("number")[0].offsetHeight;if(height != this.state.height){this.setState({height : height})}let style = {top: (-1 * value * this.state.height) + 'px'}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.jsimport 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;
总结
本组件是以单个数字形式进行展示,可以供使用者灵活组装组件中过滤了传入值为非数字的情况,支持用于展示整数、小数大家有什么建议,或者其他的实现思路,欢迎在评论区分享。
本文链接