项目--五子棋(前端模块)

源码: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)); // 这里可能需要优化错误提示
        }
    });
}

功能说明

  1. 数据收集:从输入框获取用户名和密码,封装为 JSON 对象。
  2. AJAX 请求
    • URL/reg(需后端提供注册接口)。
    • 请求类型:POST。
    • 数据格式:JSON 字符串(需后端支持解析)。
  3. 成功处理
    • 弹出提示信息(res.reason)。
    • 跳转到登录页面。
  4. 错误处理
    • 清空输入框内容。
    • 弹出错误信息(当前逻辑显示原始 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:与服务器进行实时通信,处理匹配状态更新。

核心功能

  1. 用户信息获取与页面初始化

    • 通过 get_user_info() 函数发送 AJAX 请求到 /info 接口,获取用户数据。
    • 成功后,动态生成 HTML 内容填充到 #screen 中。
    • 创建 WebSocket 连接,连接到 ws://<host>/hall
  2. WebSocket 处理

    • 事件绑定
      • onopen:连接建立时触发。
      • onclose:连接关闭时触发。
      • onerror:连接出错时触发。
      • onmessage:处理服务器推送的消息。
    • 消息类型处理
      • hall_ready:连接成功提示。
      • match_start:匹配开始,更新按钮状态为“匹配中…”。
      • match_success:匹配成功,跳转到游戏房间页面。
      • match_stop:取消匹配,恢复按钮状态为“开始匹配”。
  3. 匹配按钮交互

    • 根据 button_flag 状态切换匹配/取消匹配操作。
    • 点击时通过 WebSocket 发送 match_startmatch_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 发送给服务器。


五子棋项目的前端页面就介绍到这里了……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值