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