小蝌蚪聊天室增加在线列表、历史聊天记录、加速、跟随、瞬移等功能。
介绍一下如何修改源码,升级这些功能
一、修改index.html源码
在图中位置插入如下代码
<div id="online-users"> <div class="header"> <h3>在线列表</h3> <div @click.stop="toggleOnlineUsers">{{ showOnlineUsersText }}</div> </div> <span>在线:{{ onlineCount }}只</span> <div class="users" v-if="showOnlineUsers"> <template v-for="(user,index) in onlineUsers"> <div class="user" :key="index"> <div @click.stop="toUserPos(user.user)" class="name"><span title="点击瞬移到玩家身边" class="name">{{ user.name }}</span><span class="pos">[{{ user.user.x | parseInt }},{{ user.user.y | parseInt }}]</span> </div> <div class="follow" @click.stop="onClickFollowUser(user)" v-if="followUser==null || user.id !== followUser.id">跟随 </div> <div class="cancel-follow" @click.stop="onClickCancelFollow" v-else>取消跟随</div> </div> </template> </div> </div> <div id="world"> <div class="header"> <h3>世界动态</h3> <div @click.stop="toggleMessages">{{ showText }}</div> </div> <div class="logs" ref="messages" v-if="showMessages"> <template v-for="(message,index) in messages"> <div class="message" v-if="message.type === 'message'" :key="index"> <div><span @click.stop="toUserPos(message.user)" title="点击瞬移到玩家身边" class="name">{{ message.user.name }}</span><span title="点击前往" @click.stop="deliveryTo(message.message.x,message.message.y)" class="pos">[{{ message.message.x }},{{ message.message.y }}]</span>: <span class="content">{{ message.message.content }}</span></div> </div> </template> </div> </div>
在如下图位置,增加代码
<script src="js/lib/vue-2.6.11.min.js"></script> <script src="js/New.js"></script>
二、修改main.css文件,在结尾处添加如下内容
#world { border-radius: 10px; padding: 10px; width: 300px; position: absolute; bottom: 30px; right: 60px; color: white; z-index: 100; background: rgba(0, 0, 0, .3); } #world > .header { display: flex; font-size: 1.5em; margin-bottom: 5px; } #world > .header > h3 { flex: 1; } #world > .logs { width: 300px; max-height: 200px; overflow: scroll; } #world > .logs::-webkit-scrollbar { width: 4px; height: 4px; scrollbar-arrow-color: #fff; } #world > .logs::-webkit-scrollbar-thumb { border-radius: 5px; -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); background: rgba(255, 255, 255, 0.2); scrollbar-arrow-color: red; } #world > .logs::-webkit-scrollbar-track { -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); border-radius: 0; background: rgba(255, 255, 255, 0.1); } #world > .logs > .message { display: flex; } #world > .logs > .message .content { color: #999; font-size: 1.3em; } #world > .logs > .message .name { color: white; font-weight: bold; font-size: 1.3em; } #world > .logs > .message .name:hover { cursor: pointer; text-decoration: underline; } #world > .logs > .message .pos { color: #999; font-size: 1.3em; } #world > .logs > .message .pos:hover { text-decoration: underline; cursor: pointer; } #online-users { border-radius: 10px; padding: 10px; width: 20vw; position: absolute; left: 60px; bottom: 30px; color: white; z-index: 99; background: rgba(0, 0, 0, .3); } #online-users > .header { display: flex; font-size: 1.5em; margin-bottom: 5px; } #online-users > .header > h3 { flex: 1; } #online-users > span { font-size: 1.3em; color: #999999; display: inline-block; } #online-users > .users { max-height: 200px; font-size: 1.4em; overflow: scroll; } #online-users > .users::-webkit-scrollbar { width: 4px; height: 4px; scrollbar-arrow-color: #fff; } #online-users > .users::-webkit-scrollbar-thumb { border-radius: 5px; -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); background: rgba(255, 255, 255, 0.2); scrollbar-arrow-color: red; } #online-users > .users::-webkit-scrollbar-track { -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); border-radius: 0; background: rgba(255, 255, 255, 0.1); } #online-users .users .name:hover { cursor: pointer; text-decoration: underline; } #online-users .users .user .pos { color: #999; } .users > .user { display: flex; padding: 5px 0; } .users > .user .name { flex: 1; } .users > .user .follow { display: none; cursor: pointer; background: rgba(255, 255, 255, .5); padding: 2px; border-radius: 2px; } .users > .user .cancel-follow { cursor: pointer; } .users > .user:hover .follow { display: block; }
三、修改js目录下的Tadpole.js文件内容,将其替换为如下内容
var Tadpole = function () { var tadpole = this; this.x = Math.random() * 300 - 150; this.y = Math.random() * 300 - 150; this.size = 4; this.name = ''; this.age = 0; this.sex = -1; this.hover = false; this.momentum = 0; this.maxMomentum = 5; this.angle = Math.PI * 2; this.targetX = 0; this.targetY = 0; this.targetMomentum = 0; this.messages = []; this.timeSinceLastActivity = 0; this.changed = 0; this.timeSinceLastServerUpdate = 0; this.update = function (mouse) { tadpole.timeSinceLastServerUpdate++; tadpole.x += Math.cos(tadpole.angle) * tadpole.momentum; tadpole.y += Math.sin(tadpole.angle) * tadpole.momentum; if (tadpole.targetX != 0 || tadpole.targetY != 0) { tadpole.x += (tadpole.targetX - tadpole.x) / 20; tadpole.y += (tadpole.targetY - tadpole.y) / 20; } // Update messages for (var i = tadpole.messages.length - 1; i >= 0; i--) { var msg = tadpole.messages[i]; msg.update(); if (msg.age == msg.maxAge) { tadpole.messages.splice(i, 1); } } // Update tadpole hover/mouse state if (Math.sqrt(Math.pow(tadpole.x - mouse.worldx, 2) + Math.pow(tadpole.y - mouse.worldy, 2)) < tadpole.size + 2) { tadpole.hover = true; mouse.tadpole = tadpole; } else { if (mouse.tadpole && mouse.tadpole.id == tadpole.id) { //mouse.tadpole = null; } tadpole.hover = false; } tadpole.tail.update(); }; this.onclick = function (e) { if (e.ctrlKey && e.which == 1) { if (isAuthorized() && tadpole.hover) { window.open("http://twitter.com/" + tadpole.name.substring(1)); return true; } } else if (e.which == 2) { //todo:open menu e.preventDefault(); return true; } return false; }; this.userUpdate = function (tadpoles, angleTargetX, angleTargetY) { this.age++; var prevState = { angle: tadpole.angle, momentum: tadpole.momentum, } // Angle to targetx and targety (mouse position) var anglediff = ((Math.atan2(angleTargetY - tadpole.y, angleTargetX - tadpole.x)) - tadpole.angle); while (anglediff < -Math.PI) { anglediff += Math.PI * 2; } while (anglediff > Math.PI) { anglediff -= Math.PI * 2; } tadpole.angle += anglediff / 5; // Momentum to targetmomentum if (tadpole.targetMomentum != tadpole.momentum) { tadpole.momentum += (tadpole.targetMomentum - tadpole.momentum) / 20; } if (tadpole.momentum < 0) { tadpole.momentum = 0; } tadpole.changed += Math.abs((prevState.angle - tadpole.angle) * 3) + tadpole.momentum; if (tadpole.changed > 1) { this.timeSinceLastServerUpdate = 0; } }; this.draw = function (context) { var opacity = Math.max(Math.min(20 / Math.max(tadpole.timeSinceLastServerUpdate - 300, 1), 1), .2).toFixed(3); // console.log(tadpole); if (tadpole.hover && isAuthorized()) { context.fillStyle = 'rgba(192, 253, 247,' + opacity + ')'; // context.shadowColor = 'rgba(249, 136, 119, '+opacity*0.7+')'; } else { context.fillStyle = 'rgba(226,219,226,' + opacity + ')'; } if (tadpole.sex == 0) { context.fillStyle = 'rgba(255, 181, 197,' + opacity + ')'; } else if (tadpole.sex == 1) { context.fillStyle = 'rgba(192, 253, 247,' + opacity + ')'; } context.shadowOffsetX = 0; context.shadowOffsetY = 0; context.shadowBlur = 6; context.shadowColor = 'rgba(255, 255, 255, ' + opacity * 0.7 + ')'; // Draw circle context.beginPath(); context.arc(tadpole.x, tadpole.y, tadpole.size, tadpole.angle + Math.PI * 2.7, tadpole.angle + Math.PI * 1.3, true); tadpole.tail.draw(context); context.closePath(); context.fill(); context.shadowBlur = 0; context.shadowColor = ''; drawName(context); drawMessages(context); }; var isAuthorized = function () { return tadpole.name.charAt('0') == "@"; }; var drawName = function (context) { var opacity = Math.max(Math.min(20 / Math.max(tadpole.timeSinceLastServerUpdate - 300, 1), 1), .2).toFixed(3); context.fillStyle = 'rgba(226,219,226,' + opacity + ')'; context.font = 7 + "px 'proxima-nova-1','proxima-nova-2', arial, sans-serif"; context.textBaseline = 'hanging'; var width = context.measureText(tadpole.name).width; context.fillText(tadpole.name, tadpole.x - width / 2, tadpole.y + 8); } var drawMessages = function (context) { tadpole.messages.reverse(); for (var i = 0, len = tadpole.messages.length; i < len; i++) { tadpole.messages[i].draw(context, tadpole.x + 10, tadpole.y + 5, i); } tadpole.messages.reverse(); }; // Constructor (function () { tadpole.tail = new TadpoleTail(tadpole); })(); }
四、修改js目录下的WebSocketService.js文件,替换如下内容
var WebSocketService = function (model, webSocket) { var webSocketService = this; var webSocket = webSocket; var model = model; var gouserList = []; this.hasConnection = false; this.welcomeHandler = function (data) { webSocketService.hasConnection = true; model.userTadpole.id = data.id; model.tadpoles[data.id] = model.tadpoles[-1]; delete model.tadpoles[-1]; $('#chat').initChat(); if ($.cookie('todpole_name')) { webSocketService.sendMessage('name:' + $.cookie('todpole_name')); } if ($.cookie('todpole_sex')) { webSocketService.sendMessage('我是' + $.cookie('todpole_sex')); } }; this.updateHandler = function (data) { var newtp = false; if (!model.tadpoles[data.id]) { newtp = true; model.tadpoles[data.id] = new Tadpole(); model.arrows[data.id] = new Arrow(model.tadpoles[data.id], model.camera); } var tadpole = model.tadpoles[data.id]; if (tadpole.id == model.userTadpole.id) { tadpole.name = data.name; return; } else { tadpole.name = data.name; } if (newtp) { tadpole.x = data.x; tadpole.y = data.y; vmLog.updateUsers(model.tadpoles); } else { tadpole.targetX = data.x; tadpole.targetY = data.y; } tadpole.angle = data.angle; tadpole.sex = data.sex; tadpole.momentum = data.momentum; tadpole.timeSinceLastServerUpdate = 0; } this.messageHandler = function (data) { var tadpole = model.tadpoles[data.id]; if (!tadpole) { return; } tadpole.timeSinceLastServerUpdate = 0; tadpole.messages.push(new Message(data.message)); vmLog.addLog(tadpole, { content: data.message, time: new Date(), x: parseInt(tadpole.x), y: parseInt(tadpole.y) }); } this.closedHandler = function (data) { if (model.tadpoles[data.id]) { delete model.tadpoles[data.id]; delete model.arrows[data.id]; vmLog.updateUsers(model.tadpoles); } } this.redirectHandler = function (data) { if (data.url) { if (authWindow) { authWindow.document.location = data.url; } else { document.location = data.url; } } } this.processMessage = function (data) { var fn = webSocketService[data.type + 'Handler']; if (fn) { fn(data); } } this.connectionClosed = function () { webSocketService.hasConnection = false; $('#cant-connect').fadeIn(300); }; this.sendUpdate = function (tadpole) { var sendObj = { type: 'update', x: tadpole.x.toFixed(1), y: tadpole.y.toFixed(1), angle: tadpole.angle.toFixed(3), momentum: tadpole.momentum.toFixed(3), sex: tadpole.sex }; if (tadpole.name) { sendObj['name'] = tadpole.name; } webSocket.send(JSON.stringify(sendObj)); } this.sendMessage = function (msg) { let regexp = /^(name[::;;]|我叫)(.+)/i; if (regexp.test(msg)) { model.userTadpole.name = msg.match(regexp)[2]; $.cookie('todpole_name', model.userTadpole.name, { expires: 14 }); return; } regexp = /^(我是|sex)(男生|女生|0|1|男|女)/; if (regexp.test(msg)) { let sex = msg.match(regexp)[2]; if (sex === "女生" || sex === '0') { model.userTadpole.sex = 0; } else if (sex === "男生" || sex === '1') { model.userTadpole.sex = 1; } else { model.userTadpole.sex = -1; } $.cookie('todpole_sex', model.userTadpole.sex, { expires: 14 }); return; } regexp = /^跟随(.+)/i; if (regexp.test(msg)) { let _this = this; let gouserByname = msg.match(regexp)[1]; console.log(gouserByname); var gousergo = setInterval(function () { var gouser = queryByName(gouserByname); let xx = /x: ?(.+)y/i; let yy = /y: ?(.+)/i; // console.log(gouser+"-"+xx+"-"+yy); if (gouser != null) { model.userTadpole.x = parseFloat(gouser.match(xx)[1] - 12); model.userTadpole.y = parseFloat(gouser.match(yy)[1] - 12); _this.sendUpdate(model.userTadpole); } else { clearInterval(gousergo); app.sendMessage("他跑了!!"); } gouserList.push(gousergo); // console.log(gouserList); }, 10); return; } regexp = /^(停止|stop)(挂机|跟随)/; if (regexp.test(msg)) { let gouserByname = msg.match(regexp)[2]; // console.log(gouserByname); if (gouserByname == "挂机" || gouserByname == "跟随") { // console.log(gouserList); gouserList.forEach((item, index) => { clearInterval(item); }) gouserList = []; gousergo = 0; return; } } regexp = /^-?(\d+)[,,]-?(\d+)$/i; if (regexp.test(msg)) { let pos = msg.match(regexp); // console.log(pos) app.deliveryTo(pos[1], pos[2]); return; } regexp = /^速度(\d+)$/i; if (regexp.test(msg)) { let num = msg.match(regexp); let speed = parseInt(num[1]) > 0 ? parseInt(num[1]) : 1; app.speed(speed); } regexp = /^flicker$/; if (regexp.test(msg)) { let _this = this; let interval = setInterval(function () { model.userTadpole.sex = model.userTadpole.sex ^ 1; $.cookie('todpole_sex', model.userTadpole.sex, { expires: 14 }); _this.sendUpdate(model.userTadpole); }, 500); setTimeout(function () { clearInterval(interval); }, 60000); return; } var sendObj = { type: 'message', message: msg }; webSocket.send(JSON.stringify(sendObj)); } this.authorize = function (token, verifier) { var sendObj = { type: 'authorize', token: token, verifier: verifier }; webSocket.send(JSON.stringify(sendObj)); } var queryByName = function (name) { var x; var y; var userid = JSON.stringify(model.tadpoles); userid = JSON.parse(userid); for (var j in userid) { if (userid[j].name == name || j == name) { x = userid[j].x; y = userid[j].y; return "x:" + x + "y:" + y; } } return null; } }
五、在js目录下添加文件New.js,内容如下
let vmLog = new Vue({ el: '#ui', data: { messages: [], showMessages: true, users: null, onlineCount: 0, onlineUsers: [], model: null, showOnlineUsers: true, followUser: null, followInterval: null, }, filters: { parseInt: function (value) { return parseInt(value); } }, watch: { messages() { this.$nextTick(() => { let container = this.$refs.messages; let scrollTop = container.scrollTop; let scrollHeight = container.scrollHeight; let clientHeight = container.clientHeight; if (clientHeight - scrollTop <= 220) { container.scrollTop = scrollHeight } container.scrollTop = scrollHeight }) }, followUser(newUser, oldUser) { if (this.followInterval !== null) clearInterval(this.followInterval); if (newUser !== null) { this.followUser = newUser; this.followInterval = setInterval(() => { // console.log(this.followUser) this.deliveryTo(this.followUser.user.x, this.followUser.user.y); }, 100); // console.log(this.followInterval) } }, onlineUsers(newOnlineUsers, oldOnlineUsers) { if (this.followUser !== null) { let index = newOnlineUsers.findIndex((user) => { return this.followUser.id === user.id; }); // console.log('update onlineUsers') if (index === -1) { console.log("用户离开,跟随结束") clearInterval(this.followInterval); this.followUser = null; this.followInterval = null; } } } }, computed: { showText() { return this.showMessages ? '隐藏' : '显示'; }, showOnlineUsersText() { return this.showOnlineUsers ? '隐藏' : '显示'; } }, methods: { addLog(user, message) { this.messages.push({ user: user, message: message, type: 'message' }); }, toggleMessages() { this.showMessages = !this.showMessages; }, toggleOnlineUsers() { this.showOnlineUsers = !this.showOnlineUsers; }, deliveryTo(x, y) { // console.log(x, y, app) app.deliveryTo(x, y); }, toUserPos(user) { this.deliveryTo(user.x, user.y); }, updateUsers(users) { this.users = users; this.onlineCount = this.users ? Object.keys(this.users).length : 0; //在线用户列表 let userList = [] for (let id in this.users) { userList.push({ id: id, name: this.users[id].name, user: this.users[id] }) } this.onlineUsers = userList }, updateModel(model) { this.model = model; }, onClickFollowUser(user) { this.followUser = user; // console.log(user) }, onClickCancelFollow() { this.followUser = null; clearInterval(this.followInterval); } } });
六、在js/lib目录下添加文件
vue-2.6.11.min.js
修改版代码获取:
还没有评论,来说两句吧...