极简弹幕方案
重大的活動現場一般離不開 PPT 演示,可是如何有效和現場互動呢?這時候彈幕必不可少,靜態的 PPT 就略顯乏力。有沒有一種好的方案可以二者兼得呢?
如何才能使 PPT 具有交互性,這是一個值得思考的問題!
可能很多童鞋想到了,如果使用「網頁 PPT」 ,豈不是完美解決了這個問題。本節我們就來提供一種思路,用「PPT + 發射器 + Socket」 實現「極簡彈幕方案」。
關于「網頁 PPT」,可以查看我之前的文章「酷炫的 HTML5 網頁 PPT」一探究竟。
一、效果演示
我們先通過一個簡單的視屏演示一下效果:
相關代碼:Demo 地址
二、方案概括
看完上面的演示,是不是迫不及待想知道答案,下面我們來逐步拆分。
先來看看代碼結構
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | . ├── README.md ├── mobile │ ├── README.md │ ├── node_modules │ ├── package.json │ ├── public │ ├── src │ └── yarn.lock ├── package.json ├── ppt │ ├── css │ ├── extras │ ├── images │ ├── index.html │ ├── js │ └── temp ├── server │ ├── app.js │ ├── data │ ├── node_modules │ ├── package-lock.json │ └── package.json └── yarn.lock |
我們主要關注以下三個目錄:
1.ppt
使用 impressjs 構建的項目,PPT 演講「主屏」,主要演示內容區域,同時接收「服務端」推送彈幕信息。
2.mobile
移動端,下文稱作「發射器」,主要用作現場用戶互動向主屏發送彈幕消息。通過 Create React App 生成,技術棧是:React + Antd。
3.server
服務端,主要接受用戶彈幕,同時廣報到主屏,使用 Socket 實現。
啟動方式:
1.進入 server 目錄,啟動服務:
| 1 | node app.js |
此時會啟動一個本機 IP 地址的服務。
2.進入 ppt 目錄,使用 http-server 啟動站點:
| 1 | http-server |
注意:接口地址需要替換成本機 IP 地址。
3.進入 mobile 目錄,啟動發射器:
| 1 | yarn start |
注意:請求接口需要使用本機 IP 地址。
Demo 比較簡單,主要展示主流程,如果細節過程有問題,歡迎一起探討。
三、主屏細節(核心代碼)
主屏是主要演示版面,我們需要像下面這樣作出 PPT,這里我們做了三個頁面:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <div id="impress" class="jartto" data-transition-duration="1000"> <div id="cover" class="step slide title" data-x="1000" data-y="1000"> <img src="temp/img/qrcode.png" /> </div> <div id="award" class="step slide" data-x="2000" data-y="3000"> <h1>請開始你的表演~</h1> </div> <div id="change" class="step slide" data-x="2000" data-y="3000" data-scale="5"> <h1>切換 PPT</h1> </div> <div id="thank" class="step slide" data-rel-x="0" data-rel-y="3000" data-rotate="90" data-scale="2"> <img src="images/thanks.png" /> </div> </div> |
每個 div 就是一頁 ppt,里面可以隨意排版,data-x 控制位置,data-scale 控制縮放,data-rotate 控制旋轉。
更多 API 文檔,請參考如下文檔:
1.酷炫的 HTML5 網頁 PPT
2.文檔地址
四、實現彈幕
為了更好的理解彈幕,我們來實現一個簡版:
1.定義彈幕結構
| 1 | <div class="jartto_demo">我是彈幕</div> |
2.定義移動動畫
| 1 2 3 4 5 6 7 8 9 10 11 | @keyframes barrager{ from{ left:100%; transform:translateX(0); transform:translate3d(0, 0, 0); } to{ left:0; transform:translate3d(-100%, 0, 0); } } |
注意,使用 translate3d 可以開啟 GPU 硬件加速,會比 translateX 更流暢一些。
關于硬件加速,可以關注我之前寫的一篇文章:詳談層合成(composite)
3.使用動畫
| 1 2 3 4 | .jartto_demo{ position:absolute; animation: barrager 5s linear 0s; } |
OK,我們通過三步實現了一個簡單的彈幕動畫。那么問題來了,彈幕都是隨機位置,隨機速度,隨機顏色出現在屏幕上的,這個該如何實現呢?
4.隨機彈幕出現位置
| 1 2 3 | let window_height = $(window).height() - 150; bottom = Math.floor(Math.random() * window_height + 40); code = code.replace(" bottom:{bottom}, //距離底部高度,單位px,默認隨機 \n", ''); |
5.隨機彈幕顏色
| 1 2 | let color = `#${Math.floor(Math.random() * (2 << 23)).toString(16)}`; console.log(color); // #6e8360 |
好了,大功告成,我們順手加上 Socket 事件監聽。
6.事件監聽
為了拿到用戶發送過來的彈幕,我們需要做一個事件監聽(接收服務端數據):
首先,引入 socket.io.js 文件:
| 1 | <script type="text/javascript" src="http://{jartto.ip}/socket.io/socket.io.js"></script> |
| 1 2 3 4 5 | const socket = io('http://{jartto.ip}'); socket.on('server-push', function (data) { console.log('news message >>>>>>>>', data.message); run(data.message); }); |
當我們監聽到 server-push 事件的時候,run 函數就會初始化彈幕方法,隨機生成一條彈幕,在屏幕滑過。
五、發射器細節(核心代碼)
發射器就非常簡單了,我們使用 Create React App 初始化項目,在 src/app.js 中寫入一個表單(這里以 React 為例,Vue 也是大同小異):
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <div className="app-box"> <div className="form-box"> <Form.Item {...formItemLayout} label=""> {getFieldDecorator('msg', { rules: [ { required: true, message: '請輸入內容', }, ], })(<Input size="large" placeholder="發送消息,嗨起來~" />)} </Form.Item> <Form.Item {...formTailLayout} > <Button className="btns" shape="round" icon="close" size="large" onClick={this.cancle}>取消</Button> <Button type="primary" shape="round" icon="check" size="large" onClick={this.check}>發送</Button> </Form.Item> </div> </div> |
用戶在輸入框輸入消息,向我們的服務器發送請求,很簡單,就不贅述了。效果圖可以參考下面:
請注意,此處為了演示效果,我將三端同框了。
六、服務端細節(核心代碼)
服務端比較簡單,使用 Express 初始化一個 Node 項目,向 app.js 寫入如下內容:
1.啟動 Socket 服務:
| 1 2 3 4 5 6 7 8 | const express = require('express'), bodyParser = require('body-parser'), socket = require('socket.io'), fs = require('fs'); const app = express(); const PORT = 4000; const io = socket(app.listen(PORT, () => console.log(`start on port ${PORT}`))); |
2.監聽 Socket 連接,接收用戶發送數據,將數據寫入本地 JSON 文件,并廣播到 server-push 事件:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | io.on('connection', sockets => { console.log('連接成功!'); app.post('/api/send', (req, res, next) => { // console.log(req.body); let info = JSON.stringify(req.body.msg); fs.writeFile('./data/jartto.json', `${info},\n`, {flag:'a',encoding:'utf-8',mode:'0666'},function(err){ if(err) { console.log('文件寫入失敗'); res.status(500).send('Error'); } else { sockets.broadcast.emit('server-push', { message: req.body.msg }); res.status(200).send('Done'); } }) }) sockets.on('disconnect', () => { console.log('User Disconnected'); }) }); |
3.當然,我們也可以存入數據庫做持久化,以下演示存入 MySQL 核心代碼:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | io.on('connection', sockets => { console.log('連接成功!'); app.post('/api/send', (req, res, next) => { let {ua, msg} = req.body.msg; req.getConnection(function(err, cnt) { let query = cnt.add('INSERT INTO (ua, msg)', {ua, msg}, function(err, rows) { if (err) { console.log("Error inserting : %s ",err ); return next(err); } sockets.broadcast.emit('server-push', { message: req.body.msg }); res.status(200).send('Done'); }) }) }) sockets.on('disconnect', () => { console.log('User Disconnected'); }) }); |
4.啟動服務
| 1 | node app.js |
我們的服務端就啟動起來了,訪問地址是你的主機 IP 和 4000 端口。
七、總結
本文我們從零到一搭建了一個完整的彈幕方案,涉及到三部分:主屏,發射器和服務端,旨在為小伙伴們提供一套極簡的設計思路。通過 Demo 我們可以簡單的串聯一個全棧項目,做更多有趣的事情。
文章最后,打個小廣告吧。如果你想搭上在線教育的快車,快速成長,不妨加入我們。一起成長,一起學習,一起挑戰更多有趣的事情,「跟誰學-高途課堂」歡迎你,請將簡歷私我~
總結
- 上一篇: STM32 STM8 GD32 脱机烧
- 下一篇: 图片上传通用后台模板