在不少App的H5首頁,常常會看到頂部的輪播的消息流,相似於彈幕,展現給用戶,增長營銷感,例如某電商首頁:javascript
要實現相似的功能,該如何設計一個通用的組件?css
若是單個彈幕展現時間等於彈幕間隔時間,利用
aniamtion
keyframes
50% 以後隱藏彈幕便可; 若是單個彈幕展現時間不等於彈幕間隔時間,經過 js 控制 css 的 keyframes 在W3C規範中沒法實現,可是能夠經過animation-delay + 組件銷燬重建實現;html
彈幕的無限循環輪播經過 CSS 動畫
animation-iteration-count: infinite
,動畫總時間爲一個彈幕顯示 + 隱藏的總時間前端
彈幕展現規則以下圖所示 java
// 彈幕容器組件 <Barrage barrageList={barrageList} duration={4} /> // 彈幕渲染組件 duration 表示單個彈幕總時間 <BarrageItem barrageContent={barrageList[barrageIndex]} duration={duration} /> 複製代碼
每隔 duration
時間展現下一個彈幕web
const { duration } = this.props; this.timer = setInterval(() => { const { barrageList } = this.props; const { barrageIndex } = this.state; this.setState({ barrageIndex: (barrageIndex + 1) % barrageList.length // 這裏取模是爲了循環展現彈幕數據 }); }, duration * 1000); 複製代碼
function BarrageItem ({ barrageContent, duration }) { return ( <div className='barrage' style={{ animation: `showBarrage ${duration}s ease-in-out infinite` }}> <div className='thumb' style={{ backgroundImage: `url(${barrageContent.avatar})` }} /> <div className='text'> {barrageContent.text} </div> </div> ); } 複製代碼
CSS 動畫飛入飛出,且 動畫一直無限循環 來實現彈幕輪播npm
@keyframes showBarrage { 0% { opacity: 0; left: -100%; } 5% { opacity: 1; left: .06rem; } 45% { opacity: 1; left: .06rem; } 50% { opacity: 0; left: -100%; } 100% { opacity: 0; left: -100%; } } 複製代碼
這樣就實現一個最簡單的飛入飛出的彈幕了,效果以下: canvas
彈幕的無限循環輪播不經過 CSS 動畫,而是經過組件的銷燬與重建;後端
彈幕顯示時間爲動畫持續時間,彈幕間隔時間爲動畫延遲時間(由於
animation-iteration-count: infinite
狀況下,延遲時間只在首次動畫生效,後續每個動畫循環不會執行延遲效果)服務器
彈幕展現規則以下圖所示
// 彈幕容器組件 <Barrage barrageList={barrageList} showTime={3} gapTime={1} /> // 彈幕渲染組件 <BarrageItem barrageContent={barrageList[barrageIndex]} showTime={3} gapTime={1} /> 複製代碼
每隔 showTime + gapTime
時間展現下一個彈幕
const { duration } = this.props; this.timer = setInterval(() => { const { barrageList } = this.props; const { barrageIndex, showBarrage } = this.state; this.setState({ barrageIndex: (barrageIndex + 1) % barrageList.length, // 這裏取模是爲了循環展現彈幕數據 showBarrage: !showBarrage }); }, (showTime + gapTime) * 1000); // 經過組件的key不一樣來銷燬並重建組件 render () { const { barrageList, showTime, gapTime } = this.props; const { barrageIndex, showBarrage } = this.state; return showBarrage ? <BarrageItem key={'before'} barrageContent={barrageList[barrageIndex]} showTime={showTime} gapTime={gapTime} /> : <BarrageItem key={'after'} barrageContent={barrageList[barrageIndex]} showTime={showTime} gapTime={gapTime} />; } 複製代碼
function BarrageItem ({ barrageContent, duration }) { return ( <div className='common-barrage' style={{ animation: `showBarrage ${showTime}s ease-in-out ${gapTime}s` }}> <div className='thumb' style={{ backgroundImage: `url(${barrageContent.avatar})` }} /> <div className='text'> {barrageContent.txt} </div> </div> ); } 複製代碼
CSS 動畫飛入飛出,且 動畫一直無限循環 來實現彈幕輪播
@keyframes showBarrage { 0% { opacity: 0; left: -100%; } 10% { opacity: 1; left: 6px; } 90% { opacity: 1; left: 6px; } 100% { opacity: 0; left: -100%; } } 複製代碼
效果以下:
服務端推送,數據發送方爲服務端,接收方爲客戶端,服務端每隔一段時間就推送必定數量的彈幕數據給客戶端,客戶端拿到數據後更新本地彈幕數據隊列,展現順序依推入彈幕的順序執行。
首先須要瞭解下 Websocket
, 參考大神的 阮一峯博客 與 Websocket MDN
做爲一名前端,天然而然想到結合基於 Nodejs 的 WebSocket 框架 Socket.io 來實現彈幕數據的推送
首先基於 Nodejs 的 http 模塊和 WebSocket 框架 Socket.io 搭建一個簡單的推送服務器, 每隔一段時間往客戶端推送必定量的彈幕數據
搭建過程參考官方文檔 [搭建基於Node HTTP服務器的Socket](https://socket.io/docs/#Using-with-Node-http-server)
> npm init
> npm install --save socket.io
複製代碼
const socket = require('socket.io'); const http = require('http'); const server = http.createServer(/** 定義一個路由處理函數 */); server.listen(8080); const io = socket(server); let timer = null; // 模擬彈幕數據 io.on('connection', socket => { console.log('連上了'); // 連上以後隔一段時間往客戶端推送彈幕數據,每次推送三條隨機彈幕 timer = setInterval(() => { const obj = [ { avatar: 'xxx1.png', txt: '彈幕' + Math.floor(Math.random() * 100) }, { avatar: 'xxx2.png', txt: '彈幕' + Math.floor(Math.random() * 100) }, { avatar: 'xxx3.png', txt: '彈幕' + Math.floor(Math.random() * 100) } ] socket.send(JSON.stringify(obj)) // 傳輸序列化後的字符串數據 }, 6000) socket.on('disconnect', () => { console.log('斷開了'); clearInterval(timer); }) }) 複製代碼
前端這邊經過安裝 Socket.io
的客戶端,便可監聽並接收從服務端推送過來的數據
npm install --save socket.io-client
建立並鏈接到客戶端 socket.io-client
// // Barrage.js import io from 'socket.io-client'; const socket = io('ws://localhost:8080'); // 監聽message事件並更新本地彈幕數據 const [barrageList, getBarrageList] = useState([]); useEffect(() => { socket.on('message', (data) => { getBarrageList(oldBarrageList => { console.log([...oldBarrageList, ...JSON.parse(data)]) return [...oldBarrageList, ...JSON.parse(data)]; }); }) }, []); 複製代碼
效果以下,右邊控制檯打印的是每次接收到服務端的推送彈幕後的本地數據
王司徒鎮樓
視頻彈幕主要須要考慮以下幾個問題
在這裏咱們考慮一種比較簡單的狀況
固定軌道數
彈幕移動速度固定
統一軌道上彈幕不重疊
彈幕顏色隨機
軌道上下不重疊
彈幕數據來自於服務端推送(模擬用戶輸入彈幕)
最後,這裏推薦一個比較好用的基於 canvas 的視頻彈幕組件 Barrage UI