源码:https://gitee.com/epiphanywh/project/tree/master/CppGobangWebPro/source
注册登录
注册
这个注册页面是我在网上找的,源码地址。
效果展示:
HTML 整体结构
<body>
<div class="box">
<div class="content">
<div class="login-wrapper">
<!-- 注册表单 -->
<h1>注册</h1>
<div class="login-form">
<div class="username form-item">
<span>账号</span>
<input type="text" id="user_name" class="input-item">
</div>
<div class="password form-item">
<span>密码</span>
<input type="password" id="password" class="input-item">
</div>
<button class="login-btn" id="submit" onclick="reg()">注 册</button>
</div>
<!-- 分隔线 -->
<div class="divider">
<span class="line"></span>
<span class="divider-text">其他方式注册</span>
<span class="line"></span>
</div>
<!-- 第三方登录 -->
<div class="other-login-wrapper">
<div class="other-login-item">
<img src="/asset/QQ.png" alt="">
</div>
<div class="other-login-item">
<img src="/asset/WeChat.png" alt="">
</div>
</div>
<!-- 跳转登录页链接 -->
<div class="register-link">
<p>已有账号? <a href="login.html">点击登录</a></p>
</div>
</div>
</div>
</div>
</body>
关键组件
- 注册表单:包含用户名和密码输入框,提交按钮绑定
reg()
函数。 - 分隔线:使用
<span>
标签和 CSS 实现水平分割线。 - 第三方登录:展示 QQ 和微信图标(需确保
/asset/
路径正确)。 - 跳转链接:点击跳转到
login.html
页面。
CSS 样式
- 外部样式表:通过
<link>
引入/css/style.css
,控制页面布局和视觉效果。 - 关键样式类:
box
:容器布局,可能用于居中内容。login-form
:表单容器,包含输入框和按钮。divider
:分隔线样式,通过line
类控制横线。other-login-item
:第三方登录图标样式。
JavaScript 逻辑
下面主要介绍如何为按钮添加事件,并向服务器发送请求:
依赖库 jQuery: 通过 <script>
引入 /js/jquery.min.js
,简化 DOM 操作和 AJAX 请求。
核心函数 reg()
给注册按钮 添加事件处理函数:
<button class="login-btn" id="submit" onclick="reg()">注 册</button>
函数逻辑如下:
function reg() {
// 获取输入数据
var reg_info = {
username: document.getElementById("user_name").value,
password: document.getElementById("password").value
};
// 发送 AJAX 请求
$.ajax({
url: "/reg",
type: "POST",
data: JSON.stringify(reg_info),
success: function(res) {
alert(res.reason);
window.location.assign("/login.html");
},
error: function(xhr) {
document.getElementById("user_name").value = "";
document.getElementById("password").value = "";
alert(JSON.stringify(reg_info)); // 这里可能需要优化错误提示
}
});
}
功能说明
- 数据收集:从输入框获取用户名和密码,封装为 JSON 对象。
- AJAX 请求:
- URL:
/reg
(需后端提供注册接口)。 - 请求类型:POST。
- 数据格式:JSON 字符串(需后端支持解析)。
- URL:
- 成功处理:
- 弹出提示信息(
res.reason
)。 - 跳转到登录页面。
- 弹出提示信息(
- 错误处理:
- 清空输入框内容。
- 弹出错误信息(当前逻辑显示原始 JSON,建议改为
xhr.responseText
)。
登录
登录页面和注册页面的逻辑类似,这里就不讲解了:
添加的函数逻辑如下:
// 用户登录功能请求的处理
void login(wsserver_t::connection_ptr& conn)
{
websocketpp::http::parser::request req = conn->get_request();
// 1. 获取请求正文(获得用户提交的注册数据)
std::string request_body = conn->get_request_body();
// 2. 对正文进行Json反序列化,得到用户名和密码
Json::Value login_info; // 用户注册信息
bool ret = json_util::unserialize(&login_info, request_body);
if (ret == false)
{
LOG(DEBUG, "请求正文格式错误");
return http_request(false, "请求正文格式错误", websocketpp::http::status_code::bad_request, conn);
}
// 3. 进行数据的完整性验证,如果验证失败,返回400
if (login_info["username"].isNull() || login_info["password"].isNull())
{
LOG(DEBUG, "请输入用户名/密码");
return http_request(false, "请输入用户名/密码", websocketpp::http::status_code::bad_request, conn);
}
ret = _tb_user.login(login_info);
if(ret == false)
{
LOG(DEBUG, "用户名/密码错误");
return http_request(false, "用户名/密码错误", websocketpp::http::status_code::bad_request, conn);
}
// 4. 如果验证成功,创建session
uint64_t uid = login_info["id"].asUInt64();
session_ptr sp = _session_manager.create_session(uid, session_status::LOGIN);
if(sp.get() == nullptr)
{
LOG(DEBUG, "创建会话失败");
return http_request(false, "创建会话失败", websocketpp::http::status_code::internal_server_error, conn);
}
// 5. 设置响应头部,Set-Cookie,通过Cookie返回sessionid
std::string cookie = "SSID=" + std::to_string(sp->get_session_id());
conn->append_header("Set-Cookie", cookie);
return http_request(true, "登录成功", websocketpp::http::status_code::ok, conn);
}
游戏大厅
这个前端页面是一个网络五子棋游戏大厅的入口,结合了 HTML 结构、CSS 样式和 JavaScript 交互逻辑。
HTML 整体结构
<body>
<div class="nav">网络五子棋对战游戏</div>
<div class="container">
<div>
<div id="screen"></div>
<div id="match-button">开始匹配</div>
</div>
</div>
</body>
关键组件
- 导航栏:显示游戏名称,样式由外部 CSS 控制。
- 用户信息区:
id="screen"
的<div>
,用于展示用户积分、比赛场次等信息。 - 匹配按钮:
id="match-button"
的<div>
,点击触发匹配或取消匹配操作。
CSS 样式
- 外部样式表:
common.css
:可能包含全局样式(如布局、字体等)。game_hall.css
:针对游戏大厅的特定样式(如按钮状态、容器布局等)。
- 关键样式类:
.container
:可能用于内容居中,确保在不同屏幕尺寸下布局稳定。.nav
:导航栏样式,可能设置背景色、字体大小等。
JavaScript 逻辑
依赖库:
- jQuery:用于 AJAX 请求和 DOM 操作。
- WebSocket:与服务器进行实时通信,处理匹配状态更新。
核心功能
-
用户信息获取与页面初始化:
- 通过
get_user_info()
函数发送 AJAX 请求到/info
接口,获取用户数据。 - 成功后,动态生成 HTML 内容填充到
#screen
中。 - 创建 WebSocket 连接,连接到
ws://<host>/hall
。
- 通过
-
WebSocket 处理:
- 事件绑定:
onopen
:连接建立时触发。onclose
:连接关闭时触发。onerror
:连接出错时触发。onmessage
:处理服务器推送的消息。
- 消息类型处理:
hall_ready
:连接成功提示。match_start
:匹配开始,更新按钮状态为“匹配中…”。match_success
:匹配成功,跳转到游戏房间页面。match_stop
:取消匹配,恢复按钮状态为“开始匹配”。
- 事件绑定:
-
匹配按钮交互:
- 根据
button_flag
状态切换匹配/取消匹配操作。 - 点击时通过 WebSocket 发送
match_start
或match_stop
请求。
- 根据
与后端接口的交互
AJAX 请求
/info
接口:- 请求方法:GET。
- 响应数据:
{ "username": "user123", "ladder_score": 1500, "total_count": 100, "win_count": 60 }
WebSocket 消息
- 客户端发送消息:
match_start
:发起匹配请求。match_stop
:取消匹配请求。
- 服务器推送消息:
hall_ready
:WebSocket 连接成功。match_start
:匹配开始(更新按钮状态)。match_success
:匹配成功,跳转页面。match_stop
:匹配取消(恢复按钮状态)。
游戏房间
这段代码实现了一个网络五子棋对战游戏的房间页面,包含棋盘绘制、棋子落子、聊天功能等。
HTML 整体布局
<body>
<div class="nav">网络五子棋对战游戏</div>
<div class="container">
<div id="chess_area">
<canvas id="chess" width="450px" height="450px"></canvas>
<div id="screen"> 等待玩家连接中... </div>
</div>
<div id="chat_area" width="400px" height="300px">
<div id="chat_show">
<p id="self_msg">你好!</p></br>
<p id="peer_msg">你好!</p></br>
</div>
<div id="msg_show">
<input type="text" id="chat_input">
<button id="chat_button">发送</button>
</div>
</div>
</div>
</body>
- 导航栏:使用
<div class="nav">
显示游戏名称。 - 容器部分:
<div class="container">
包裹整个游戏内容,包含棋盘区域和聊天区域。- 棋盘区域:
id="chess_area"
的<div>
包含一个<canvas>
元素用于绘制棋盘和棋子,以及一个id="screen"
的<div>
用于显示游戏状态信息。 - 聊天区域:
id="chat_area"
的<div>
包含聊天消息显示区id="chat_show"
和消息输入与发送区id="msg_show"
。
- 棋盘区域:
样式
通过 <link>
标签引入外部样式表:
<link rel="stylesheet" href="css/common.css">
<link rel="stylesheet" href="css/game_room.css">
common.css
:可能包含通用的样式设置,如字体、颜色、全局布局等。game_room.css
:针对游戏房间页面的特定样式,如棋盘布局、聊天框样式等。
脚本逻辑
变量定义与初始化
let chessBoard = [];
let BOARD_ROW_AND_COL = 15;
let chess = document.getElementById('chess');
let context = chess.getContext('2d');
var ws_url = "ws://" + location.host + "/room";
var ws_hdl = new WebSocket(ws_url);
var room_info = null;
var is_me;
chessBoard
:二维数组,用于记录棋盘上棋子的分布情况。BOARD_ROW_AND_COL
:棋盘的行数和列数。chess
:获取id="chess"
的<canvas>
元素。context
:获取<canvas>
的 2D 绘图上下文。ws_url
:WebSocket 连接的地址。ws_hdl
:WebSocket 连接对象。room_info
:保存房间信息,如房间 ID、黑白棋用户的 ID 等。is_me
:记录当前是否轮到自己走棋。
游戏初始化
function initGame() {
initBoard();
let logo = new Image();
logo.src = "image/sky.jpg";
logo.onload = function () {
context.drawImage(logo, 0, 0, 450, 450);
drawChessBoard();
}
logo.onerror = function () {
console.error('图片加载失败');
}
}
function initBoard() {
for (let i = 0; i < BOARD_ROW_AND_COL; i++) {
chessBoard[i] = [];
for (let j = 0; j < BOARD_ROW_AND_COL; j++) {
chessBoard[i][j] = 0;
}
}
}
function drawChessBoard() {
context.strokeStyle = "#BFBFBF";
for (let i = 0; i < BOARD_ROW_AND_COL; i++) {
context.moveTo(15 + i * 30, 15);
context.lineTo(15 + i * 30, 430);
context.stroke();
context.moveTo(15, 15 + i * 30);
context.lineTo(435, 15 + i * 30);
context.stroke();
}
}
initGame
:初始化游戏,包括初始化棋盘数组、加载背景图片并绘制棋盘网格线。initBoard
:将棋盘数组初始化为全 0,表示没有棋子。drawChessBoard
:使用<canvas>
的绘图 API 绘制棋盘的网格线。
绘制棋子
function oneStep(i, j, isWhite) {
if (i < 0 || j < 0) return;
context.beginPath();
context.arc(15 + i * 30, 15 + j * 30, 13, 0, 2 * Math.PI);
context.closePath();
var gradient = context.createRadialGradient(15 + i * 30 + 2, 15 + j * 30 - 2, 13, 15 + i * 30 + 2, 15 + j * 30 - 2, 0);
if (!isWhite) {
gradient.addColorStop(0, "#0A0A0A");
gradient.addColorStop(1, "#636766");
} else {
gradient.addColorStop(0, "#D1D1D1");
gradient.addColorStop(1, "#F9F9F9");
}
context.fillStyle = gradient;
context.fill();
}
oneStep
:根据传入的坐标(i, j)
和棋子颜色isWhite
,在<canvas>
上绘制棋子,并使用径向渐变实现立体效果。
棋盘点击事件
chess.onclick = function (e) {
if (!is_me) {
alert("轮到对方走棋");
return;
}
let x = e.offsetX;
let y = e.offsetY;
let col = Math.floor(x / 30);
let row = Math.floor(y / 30);
if (chessBoard[row][col] != 0) {
alert("当前位置已有棋子!");
return;
}
send_chess(row, col);
}
function send_chess(r, c) {
var chess_info = {
optype: "put_chess",
room_id: room_info.room_id,
uid: room_info.uid,
row: r,
col: c
};
ws_hdl.send(JSON.stringify(chess_info));
console.log("click" + JSON.stringify(chess_info));
}
当用户点击棋盘时,首先检查是否轮到自己走棋,然后计算点击位置对应的棋盘坐标。如果该位置已有棋子,则给出提示;否则,调用 send_chess
函数向服务器发送落子请求。
WebSocket 事件处理
window.onbeforeunload = function () {
ws_hdl.close();
}
ws_hdl.onopen = function () {
console.log("房间长连接建立成功");
}
ws_hdl.onclose = function () {
console.log("房间长连接断开");
}
ws_hdl.onerror = function () {
console.log("房间长连接出错");
}
ws_hdl.onmessage = function (evt) {
info = JSON.parse(evt.data);
if (info.optype == "room_ready") {
room_info = info;
is_me = (room_info.uid == room_info.white_id ? true : false);
set_screen(is_me);
initGame();
} else if (info.optype == "put_chess") {
if (info.result == "false") {
alert(info.reason);
return;
}
is_me = (info.uid == room_info.uid ? false : true);
isWhite = (info.uid == room_info.white_id ? true : false);
if (info.row != -1 && info.col != -1) {
oneStep(info.col, info.row, isWhite);
chessBoard[info.row][info.col] = 1;
}
if (info.winner == 0) {
return;
} else {
var screen_div = document.getElementById("screen");
if (room_info.uid == info.winner)
screen_div.innerHTML = info.reason + " φ(* ̄0 ̄) ";
else
screen_div.innerHTML = "你输了 >︿< ";
var chess_div = document.getElementById("chess_area");
var button_div = document.createElement("div");
button_div.innerHTML = "返回大厅";
button_div.onclick = function () {
ws_hdl.close();
location.replace("/game_hall.html");
}
chess_div.append(button_div);
}
} else if (info.optype == "chat") {
if (info.result == false) {
alert(info.reason);
return;
}
var msg_div = document.createElement("p");
msg_div.innerHTML = info.message;
if (info.uid == room_info.uid) {
msg_div.setAttribute("id", "self_msg");
} else {
msg_div.setAttribute("id", "peer_msg");
}
var br_div = document.createElement("br");
var message_show_div = document.getElementById("chat_show");
message_show_div.append(msg_div);
message_show_div.append(br_div);
document.getElementById("chat_input").value = "";
}
}
window.onbeforeunload
:在页面关闭时关闭 WebSocket 连接。ws_hdl.onopen
:WebSocket 连接建立成功时,在控制台输出提示信息。ws_hdl.onclose
:WebSocket 连接关闭时,在控制台输出提示信息。ws_hdl.onerror
:WebSocket 连接出错时,在控制台输出提示信息。ws_hdl.onmessage
:处理服务器发送的消息,根据消息的optype
字段进行不同的处理:room_ready
:初始化房间信息,判断当前是否轮到自己走棋,并初始化游戏界面。put_chess
:处理落子响应,更新棋盘状态和轮到谁走棋的信息。如果有胜利者,显示获胜信息并添加返回大厅的按钮。chat
:处理聊天消息,将消息添加到聊天显示框中。
聊天功能
var chat_button_div = document.getElementById("chat_button");
chat_button_div.onclick = function () {
var send_msg = {
optype: "chat",
room_id: room_info.room_id,
uid: room_info.uid,
message: document.getElementById("chat_input").value
};
ws_hdl.send(JSON.stringify(send_msg));
}
当用户点击聊天发送按钮时,获取输入框中的消息,封装成 JSON 对象并通过 WebSocket 发送给服务器。
五子棋项目的前端页面就介绍到这里了……