小蝌蚪聊天室增加在线列表、历史聊天记录、加速、跟随、瞬移等功能。
介绍一下如何修改源码,升级这些功能
一、修改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
修改版代码获取:





还没有评论,来说两句吧...