用react的ReactCSSTransitionGroup插件实现简单的弹幕动画
1,开始的思路
公司想做直播方面的项目,并想加入弹幕的功能,直播的页面已经作为一个组件放在了用react+redux写好的一个网站项目上。
所以技术老大让我研究下如何用react实现弹幕的功能。下面我就简单说下我的react弹幕折腾之路。
一开始其实是两手空空,作为一个php的初级开发人员,我对前端技术掌握的很少,远不到熟练的程度。所以,我得从头学习如何用js+css实现弹幕,然后再将弹幕移植到react项目上去,这是我一开始的思路。
2,中间的折腾
我百度了下“js 弹幕”,发现大部分都是用jquery的animate()函数和css配合来实现的,比如这个HTML+CSS+jQuery实现弹幕技术,有些则是jquery配合css的animation来实现。
我学习了下用jquery的animate()函数配合css来实现弹幕的方法,然后就尝试将这个方法引入到react项目中去。但我在这个地方费了好多时间都没有进展,最终我放弃了将jquery引入react的想法。技术老大提醒我,可以找找react动画的解决方法。
于是百度、google,在sgemenfault和知乎上有不少问答,给出了三个解决方向:
1,用react官方提供的动画插件(ReactCSSTransitionGroup)可以实现基本和简单的动画
2,引入专业的第三方的动画库
3,用第三方的react动画插件
第1种方法,简单、直接,需要对react的动画插件有所了解,第2种方法需要非常熟悉第三方库的用法,像我这种前端的半吊子还是算了:),第3种方法,我也没考虑。
总之,我选择了第1种。我大致看了下react官方的tutorial和docs,然后就开始动手了。
3,初现曙光
按照react官网上给的TodoList例子,我写出了我的第1个react动画(没有用到redux),
实现的基本功能就是在一个输入框中输入文字,然后enter发送文字,文字从一个div的右侧走到左侧,最后消失。先把代码贴出来:
1 import React from 'react'; 2 import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; 3 4 class App extends React.Component { 5 constructor(props) { 6 super(props); 7 this.state = { 8 word: '', 9 value: '', 10 index: 0, 11 top: 0, 12 }; 13 this.returnWord = this.returnWord.bind(this); 14 this.handleChange = this.handleChange.bind(this); 15 this.handleSubmit = this.handleSubmit.bind(this); 16 } 17 18 componentDidMount() { 19 // 监听回车事件 20 document.onkeydown = (event) => { 21 if (event.keyCode === 13) { 22 document.getElementById('sendBullet').click = null; 23 } 24 }; 25 } 26 27 handleSubmit(event) { 28 event.preventDefault(); 29 this.setState({ value: '' }); 30 } 31 32 handleChange(event) { 33 this.setState({ value: event.target.value }); 34 } 35 36 returnWord() { 37 const word = this.myTextInput; 38 const index = this.state.index; 39 const top = this.state.top; 40 if (word === this.state.value) { 41 this.setState({ word, index: index + 1 }); 42 } 43 if (top <= 435) { 44 this.setState({ top: top + 75 }); 45 } else if (top > 435) { 46 this.setState({ top: 0 }); 47 } 48 } 49 50 render() { 51 const item = ( 52 <div 53 className="bullet" 54 key={this.state.index} 55 style={{ top: `${this.state.top}px`, color: `#${((1 << 24) * Math.random() | 0).toString(16)}` }} 56 > 57 {this.state.word} 58 </div> 59 ); 60 61 return ( 62 <div> 63 <form onSubmit={this.handleSubmit}> 64 <input 65 type="text" 66 ref={ 67 (ref) => { 68 if (ref !== null && ref.value.trim() !== '') { 69 this.myTextInput = ref.value; 70 } 71 } 72 } 73 value={this.state.value} 74 onChange={this.handleChange} 75 /> 76 <button id="sendBullet" onClick={this.returnWord}>发送弹幕</button> 77 <div 78 style={{ 79 position: 'relative', 80 width: '1200px', 81 height: '500px', 82 margin: 'auto', 83 background: 'rgba(255, 0, 0, 0.1)', 84 overflow: 'hidden', 85 }} 86 > 87 <ReactCSSTransitionGroup 88 transitionName={{ 89 enter: 'bullet-enter', 90 }} 91 transitionEnterTimeout={5000} 92 transitionLeave={false} 93 > 94 {item} 95 </ReactCSSTransitionGroup> 96 </div> 97 </form> 98 </div> 99 ); 100 } 101 } 102 103 export default App;
可以看到我这里引入了ReactCSSTransitionGroup,它是react提供的一个动画插件,可以实现基本、简单的css动画和渐变功能,它需要单独安装,具体的安装方法可以百度或google。
下面来说下比较关键的地方:
1,代码第51~59行,定义要动的弹幕文字组件。 render方法中定义了一个item,这个item就是弹幕中要动起来的弹幕文字组件。要注意:item中的key属性是必须要给定的,哪怕你只有单独的1个item也要指定key,
这样React才能决定哪个item该如何动作,这也是react官方文档中所强调的。我定义了自增的index作为state来管理item的key,这样每个item都有一个唯一的key。
2,代码第87~95行,ReactCSSTransitionGroup配置。需要注意的是,一定要将要操纵的动画元素完全包含到ReactCSSTransitionGroup中去,然后就是根据动画需要设置要做的动画过程。我只要入场动画(有字幕出现时就开始动画)就可以了。
所以设置入场动画时间transitionEnterTimeout={5000}。另外,出场动画不需要可以设置为false: transitionLeave={false},不能忽略这个属性,不然会报错。transitionName中可以设置动画过程的css样式名称,设置规则可参考官网。
3,下面是我的入场动画css样式,也是要注意的第3点
1 .bullet { 2 opacity: 0.01; 3 } 4 5 .bullet-enter { 6 opacity: 1; 7 position: absolute; 8 left: 1100px; 9 } 10 11 .bullet-enter.bullet-enter-active { 12 opacity: .5; 13 position: absolute; 14 left: -100px; 15 transition: all 5000ms ease-in; 16 }
.bullet-enter对应入场动画开始时的item样式,.bullet-enter.bullet-enter-active对应入场动画结束时的item样式,可以看到用到了css的transition,这也是实现动画的关键。
以上3点比较关键,其余的问题就是如何产生item的问题,这个暂时先不写了。
4,将弹幕作为一个组件整合到react+redux项目中
其实只要实现了弹幕动画,整合也就容易了,下面先把弹幕组件的代码贴出来。
1 import React, { Component, PropTypes } from 'react'; 2 import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; 3 4 class BulletScreen extends Component { 5 6 render() { 7 const item = ( 8 <div 9 className="bullet" 10 key={this.props.index} 11 style={{ 12 top: `${this.props.top}vh`, 13 color: 'rgb(255, 255, 255)', 14 whiteSpace: 'nowrap', 15 fontSize: '3vh', 16 }} 17 > 18 {this.props.word} 19 </div> 20 ); 21 22 return ( 23 <div 24 id="bullt-screen" 25 style={{ 26 position: 'relative', 27 width: 'auto', 28 height: '46vh', 29 overflow: 'hidden', 30 margin: '-50vh auto auto auto', 31 background: 'rgba(0, 255, 0, 0.01)', 32 }} 33 > 34 <ReactCSSTransitionGroup 35 transitionName={{ 36 enter: 'bullet-enter', 37 }} 38 transitionEnterTimeout={5000} 39 transitionLeave={false} 40 > 41 {item} 42 </ReactCSSTransitionGroup> 43 </div> 44 ); 45 } 46 } 47 48 BulletScreen.propTypes = { 49 word: PropTypes.string, 50 index: PropTypes.number, 51 top: PropTypes.number, 52 }; 53 54 export default BulletScreen;
到时在视频播放的页面中引入这个组件就可以了。
这个项目目前是我司的开源项目,感兴趣的朋友可以去github上看下: wecan-tv-frontend