树莓派云课堂开发总结

完成功能

  1. 网站界面:Bootstrap4+jQuery+Vue.js
  2. 局域网搭建:hostapd+dhcpch+dnsmasq
  3. 局域网登录功能:Express+jQuery+MongoDB
  4. 局域网登录状态保存:express-session+cookie-parser
  5. 文件上传、下载和推送:bootstrap-fileinput+formidable+fs+socket.io
  6. 习题创建、完成和查看统计:MongoDB+Echart
  7. 云端电子白板:canvas+websocket
  8. 课堂直播:obs/ffmpeg+node-media-server+flv.js
  9. 直播弹幕:canvas+websocket

页面展示

更多计划

  1. 完善已有的功能,增加灵活性,增强用户体验
  2. 添加更多功能,使课堂更加方便
  3. 完善权限系统,界定超级管理员,管理员,可创建班级、可管理班级和无特殊权限的教师,学生等角色的权限功能

心得

js已经成为了一门应用场景非常广泛的语言,从服务器、到web客户端、以及窗口程序,都能使用js完成。nodejs的广泛使用,AngularJS、vue.js等新框架的诞生,mvvm、mvw等模式的融入,正在不断使js符合最新的设计理念。electron等打包方案使js在不同领域展现其独特性和适用性。Webpack、gulp等前端工具使js最初作为一项脚本语言而能开发大型项目成为可能。掌握好js及基于js的各种工具,非常有益。

课堂直播弹幕功能

首先利用vue的循环语句来绑定html中的列表元素

<div class="card-body" id="danmaku">
    <li v-for="chat in chats">
        {{ chat.name }}: {{ chat.content }}
    </li>
</div>
new Vue({
    el: '#danmaku',
    data: {
        chats: [
            { name: "学生1", content: "233"},
            { name: "学生2", content: "233"},
            { name: "学生3", content: "233"},
            { name: "学生4", content: "233"}
        ]
    }
})

然后增加弹幕发送框,利用v-model绑定输入框

var msg = new Vue({
        el: '#send',
        data: {
            message: ''
        }
    })

    $('#btn-send').on('click', () => {
        addDanmaku('‘你’', msg.message);
    })

function addDanmaku(name, content) {
    while (chats.length >= 15) {
        chats.shift();
    }
    chats.push({
        name: name,
        content: content
    })
    msg.message = '';
}

接着使用websocket进行所有客户端弹幕的同步

// 客户端js
var socket = io('ws://' + window.location.host);
socket.on('down', function(data) {
        addDanmaku(data.name, data.content);
    })

socket.emit('up', {
    name: name, 
    content: msg.message
});
// 服务端js
// 接收弹幕
socket.on('up', (chat) => {
    socket.broadcast.emit('down', chat);
})

最后添加利用canvas绘制弹幕的功能,学习了一下他人的思路,先对canvas创建一个弹幕对象,设置canvas的属性并创建容器,然后利用setInterval不断更新更新弹幕文本的位置,并重绘canvas。控制绘图的draw函数:

// 绘制弹幕
        this.draw = function () {
            if (this.interval != "") return; // 如果已经有重绘,则返回
            var _this = this; // 传入this(弹幕对象)
            this.interval = setInterval(function () { // 每20毫秒进行一次绘制
                _this.ctx.clearRect(0, 0, _this.width, _this.height); // 先擦除画布
                _this.ctx.save();   // 然后将当前画布保存
                for (var i = 0; i < _this.msgs.length; i++) {
                    if (!(_this.msgs[i] == null)) {
                        if (_this.msgs[i].left==null) { // 新创建的弹幕,不存在作为左坐标left属性
                            _this.msgs[i].left = _this.width; // 新弹幕的位置在最右边
                            _this.msgs[i].top = parseInt(Math.random() * 200) + 30; // 设置弹幕的高度
                            _this.msgs[i].speed = 4; // 设置弹幕的速度
                            _this.msgs[i].color = _this.colorArr[Math.floor(Math.random() * _this.colorArr.length)]; // 设置弹幕的颜色 
                        }else{
                            if(_this.msgs[i].left < -200){
                                _this.msgs[i]=null;  // 弹幕离开屏幕后删除
                            }else {
                                _this.msgs[i].left = parseInt(_this.msgs[i].left - _this.msgs[i].speed); // 弹幕移动
                                _this.ctx.fillStyle = _this.msgs[i].color; // 在画板上设置颜色
                                _this.ctx.fillText(_this.msgs[i].msg, _this.msgs[i].left, _this.msgs[i].top); // 在画板上重绘弹幕
                                _this.ctx.restore(); // 写入画板
                            }
                        }
                    }
                }
            }, 20);
        };

并且同时将创建弹幕的函数调用放在addDanmuku函数中,这样不论是自己发弹幕还是接收弹幕,都可以看见飘过的弹幕