一、需求
1、一条或者多条滚动文字,有序切换
2、循环滚动
二、原理
setInterval:控制循环
requestAnimationFrame:动画效果
三、实现
constructor() { super(); this.state = { sloganList: [ "横向滚动文字1横向滚动文字1横向滚动文字1横向滚动文字1", "横向滚动文字2", "横向滚动文字3", ], sloganIndex: 0,//展示的文本下标 } this.sign = true;//定时器判断是否要进行文字滚动动画 this.textWidth = 0;//文字的宽度 this.textLeft = 0; this.offsetStep = 3;//每次移动的步长 }
constructor内部主要记录了滚动文字的基本信息,重要的两个参数是sloganIndex,sign。
sloganIndex控制目前需要展示的数据下标
sign则是判断是否调用文字滚动控制方法,值为true则调用,进入新一轮的滚动动画
componentDidMount() { let sloganContainer = this.container.clientWidth; this.text.style.left = sloganContainer + "px"; this.timer = setInterval(() => { if (this.sign) { if(Array.isArray(this.state.sloganList)&&this.state.sloganList.length) { this.showSlogan(); } else { clearInterval(this.timer); } } else { this.timer = null; } }, 1000); } componentWillUnmount() { clearInterval(this.timer); }
在组建渲染完成后定时器启动,通过sign信号控制是否执行滚动方法;组件销毁、数据格式错误或是没有数据时清除定时器
showSlogan = () => { this.textWidth = this.text.clientWidth;//文字宽度 this.textLeft = parseInt(this.text.style.left);//相对父元素偏移距离 if(this.textLeft >= -this.textWidth) this.sign = false; if(this.textLeft > -this.textWidth) { this.text.style.left = this.textLeft - this.offsetStep + "px"; requestAnimationFrame(this.showSlogan) } else { let nextIndex = this.state.sloganIndex != this.state.sloganList.length - 1 ? ++this.state.sloganIndex : 0; this.setState({ sloganIndex: nextIndex }, () => { this.text.style.left = this.container.clientWidth + "px"; this.textWidth = this.text.clientWidth; this.sign = true; }) } }
1、开始滚动前,将sign设置为false,滚动期间不会重复调用
2、文字直到最后一个字消失本次任务才算结束,所以文字元素最后的left值为元素宽度的负值
3、本轮动画结束后判断下一个动画文字所在下标,改变sloganIndex
4、待新内容渲染结束后设置新文字元素在内容区的最右边,并获取新的滚动文字宽度
5、设置sign为true,定时器运行时检查到该值为true则调用showSlogan方法
四、完整代码
class Slogan extends Component { constructor() { super(); this.state = { sloganList: [ "横向滚动文字1横向滚动文字1横向滚动文字1横向滚动文字1", "横向滚动文字2", "横向滚动文字3", ], sloganIndex: 0, } this.sign = true;//判断是否进行文字滚动动画 this.textWidth = 0;//文字的宽度 this.textLeft = 0; this.offsetStep = 3;//每次移动的步长 } componentDidMount() { let sloganContainer = this.container.clientWidth; this.text.style.left = sloganContainer + "px"; this.timer = setInterval(() => { if (this.sign) { if(this.state.sloganList&&this.state.sloganList.length) { this.showSlogan(); } else { clearInterval(this.timer); } } else { this.timer = null; } }, 1000); } componentWillUnmount() { clearInterval(this.timer); } render() { const { sloganList, sloganIndex } = this.state; return ( <div ref={(ref) => this.container = ref} className="container"> <span ref={(ref) => this.text = ref} className="slogan"> {sloganList[sloganIndex]} </span> </div> ) } showSlogan = () => { this.textWidth = this.text.clientWidth;//文字宽度 this.textLeft = parseInt(this.text.style.left);//相对父元素偏移距离 if(this.textLeft >= -this.textWidth) this.sign = false; if(this.textLeft > -this.textWidth) { this.text.style.left = this.textLeft - this.offsetStep + "px"; requestAnimationFrame(this.showSlogan) } else { let nextIndex = this.state.sloganIndex != this.state.sloganList.length - 1 ? ++this.state.sloganIndex : 0; this.setState({ sloganIndex: nextIndex }, () => { this.text.style.left = this.container.clientWidth + "px"; this.textWidth = this.text.clientWidth; this.sign = true; }) } } }
.container { width: 70%; height: 35px; line-height: 35px; margin: 0 auto; border: 1px solid #B0BEC5; padding: 0 10px; position: relative; overflow: hidden; } .slogan { position: relative; display: inline-block; }
五、总结
主要做个记录,也没用jQuery,原生js的getter/setter操作太麻烦了
希望互相学习,非常感谢。