iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 19
0
Modern Web

Go into Web!系列 第 19

Day19 | 製作一個公開匿名聊天室 - 前端篇

昨天講完關於 WebSocket 的基本介紹,今天就讓我們直接利用這個協定做一個簡易聊天室吧!

本次的範例主要分成兩大塊,前端與後端

  • 前端使用 javascript 結合 html
  • 後端使用 melody 這個 package 進行實作

今天先講講前端的部分

前端

整體的檔案架構如下

/
   /template
      /html
         - index.html
      /css
         - style.css
      /js
         - main.js
   - main.go

以下分別說明各個組件需要寫些什麼

javascript

在一開始連入網頁時,會自動帶入 0 ~ 1000 間的亂數當作 Guest ID,之後建立 ws 物件與 server 進行 WebSocket 連線,ws.onmessage 為處理 server 傳入的訊息,這邊使用 Guest ID 來判斷是否為自己發出的訊息

變數

首先我們先定義好要用到的相關變數,以下為範例

  • LEFT : 訊息放左邊
  • RIGHT : 訊息放右邊
  • EVENT_MESSAGE : 收到 WebSocket 的訊息 event 為 message
  • EVENT_OTHER : 收到 WebSocket 的訊息 event 為 other
  • userPhotos : 使用者的大頭貼,預設有 10 張
  • PERSON_IMG : 當前使用者的大頭貼
  • PERSON_NAME : 當前使用者的名稱
  • url : 連線 websocket 的 url
  • ws : WebSocket 物件
  • chatroom : html 中聊天室的 div class 名稱
  • text : html 中訊息欄位的 id
  • send : html 中送出訊息的 id
const LEFT = "left";
const RIGHT = "right";

const EVENT_MESSAGE = "message"
const EVENT_OTHER = "other"

const userPhotos = [
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408584.svg",
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408537.svg",
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408540.svg",
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408545.svg",
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408551.svg",
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408556.svg",
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408564.svg",
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408571.svg",
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408578.svg",
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408720.svg"
]
var PERSON_IMG = userPhotos[getRandomNum(0, userPhotos.length)];
var PERSON_NAME = "Guest" + Math.floor(Math.random() * 1000);

var url = "ws://" + window.location.host + "/ws?id=" + PERSON_NAME;
var ws = new WebSocket(url);
var chatroom = document.getElementsByClassName("msger-chat")
var text = document.getElementById("msg");
var send = document.getElementById("send")

function getRandomNum(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

設定觸發方法

當我們輸入完訊息按下 Enter 鍵或是滑鼠按下 送出訊息 按鈕會觸發的方法,觸發後透過 handleMessageEvent 傳送訊息至 WebSocket Server,訊息格式 json


send.onclick = function (e) {
    handleMessageEvent()
}

text.onkeydown = function (e) {
    if (e.keyCode === 13 && text.value !== "") {
        handleMessageEvent()
    }
};
function handleMessageEvent() {
    ws.send(JSON.stringify({
        "event": "message",
        "photo": PERSON_IMG,
        "name": PERSON_NAME,
        "content": text.value,
    }));
    text.value = "";
}

設定接收訊息

上面設定完傳送訊息,當然也要有接收訊息的部分,這邊透過 onmessage 接收 server 傳送的訊息

ws.onmessage = function (e) {
    var m = JSON.parse(e.data)
    var msg = ""
    switch (m.event) {
        case EVENT_MESSAGE:
            if (m.name == PERSON_NAME) {
                msg = getMessage(m.name, m.photo, RIGHT, m.content);
            } else {
                msg = getMessage(m.name, m.photo, LEFT, m.content);
            }
            break;
        case EVENT_OTHER:
            if (m.name != PERSON_NAME) {
                msg = getEventMessage(m.name + " " + m.content)
            } else {
                msg = getEventMessage("您已" + m.content)
            }
            break;
    }
    insertMsg(msg, chatroom[0]);
};
function getEventMessage(msg) {
    var msg = `<div class="msg-left">${msg}</div>`
    return msg
}

function getMessage(name, img, side, text) {
    const d = new Date()
    //   Simple solution for small apps
    var msg = `
    <div class="msg ${side}-msg">
      <div class="msg-img" style="background-image: url(${img})"></div>

      <div class="msg-bubble">
        <div class="msg-info">
          <div class="msg-info-name">${name}</div>
          <div class="msg-info-time">${d.getFullYear()}/${d.getMonth()}/${d.getDay()} ${d.getHours()}:${d.getMinutes()}</div>
        </div>

        <div class="msg-text">${text}</div>
      </div>
    </div>
  `
    return msg;
}

function insertMsg(msg, domObj) {
    domObj.insertAdjacentHTML("beforeend", msg);
    domObj.scrollTop += 500;
}

整合

最後整體的 javascript 就會像這樣

const LEFT = "left";
const RIGHT = "right";

const EVENT_MESSAGE = "message"
const EVENT_OTHER = "other"

const userPhotos = [
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408584.svg",
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408537.svg",
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408540.svg",
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408545.svg",
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408551.svg",
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408556.svg",
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408564.svg",
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408571.svg",
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408578.svg",
    "https://www.flaticon.com/svg/static/icons/svg/3408/3408720.svg"
]
var PERSON_IMG = userPhotos[getRandomNum(0, userPhotos.length)];
var PERSON_NAME = "Guest" + Math.floor(Math.random() * 1000);

var url = "ws://" + window.location.host + "/ws?id=" + PERSON_NAME;
var ws = new WebSocket(url);
var chatroom = document.getElementsByClassName("msger-chat")
var text = document.getElementById("msg");
var send = document.getElementById("send")

send.onclick = function (e) {
    handleMessageEvent()
}

text.onkeydown = function (e) {
    if (e.keyCode === 13 && text.value !== "") {
        handleMessageEvent()
    }
};

ws.onmessage = function (e) {
    var m = JSON.parse(e.data)
    var msg = ""
    switch (m.event) {
        case EVENT_MESSAGE:
            if (m.name == PERSON_NAME) {
                msg = getMessage(m.name, m.photo, RIGHT, m.content);
            } else {
                msg = getMessage(m.name, m.photo, LEFT, m.content);
            }
            break;
        case EVENT_OTHER:
            if (m.name != PERSON_NAME) {
                msg = getEventMessage(m.name + " " + m.content)
            } else {
                msg = getEventMessage("您已" + m.content)
            }
            break;
    }
    insertMsg(msg, chatroom[0]);
};

function handleMessageEvent() {
    ws.send(JSON.stringify({
        "event": "message",
        "photo": PERSON_IMG,
        "name": PERSON_NAME,
        "content": text.value,
    }));
    text.value = "";
}

function getEventMessage(msg) {
    var msg = `<div class="msg-left">${msg}</div>`
    return msg
}

function getMessage(name, img, side, text) {
    const d = new Date()
    //   Simple solution for small apps
    var msg = `
    <div class="msg ${side}-msg">
      <div class="msg-img" style="background-image: url(${img})"></div>

      <div class="msg-bubble">
        <div class="msg-info">
          <div class="msg-info-name">${name}</div>
          <div class="msg-info-time">${d.getFullYear()}/${d.getMonth()}/${d.getDay()} ${d.getHours()}:${d.getMinutes()}</div>
        </div>

        <div class="msg-text">${text}</div>
      </div>
    </div>
  `
    return msg;
}

function insertMsg(msg, domObj) {
    domObj.insertAdjacentHTML("beforeend", msg);
    domObj.scrollTop += 500;
}

function getRandomNum(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

css & html

關於 css 與 html 可以直接使用我提供的範例

使用 gin serve 前端頁面

gin 的部分在 Day5 的時候有提過,這邊直接把 index.html 透過 / 存取,以下為範例

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	r.LoadHTMLGlob("template/html/*")
	r.Static("/assets", "./template/assets")
	r.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.html", nil)
	})
	r.Run(":5000")
}

設定完畢後透過 go run main.go 的方式啟動 Server,接著可以利用瀏覽器存取 http://127.0.0.1:5000,如果看到以下畫面就代表前端網頁架設成功拉!

小結

今天的範例主要是先將前端(client)端的部分先設定好,這樣明天我們在設定後端(Server)端的時候就可以直接看到效果拉!就讓我們明天繼續寫後端吧!

專案範例我放在這裡

參考


上一篇
Day18 | WebSocket - 神奇的雙向溝通協定
下一篇
Day20 | 製作一個公開匿名聊天室 - 後端篇
系列文
Go into Web!30

尚未有邦友留言

立即登入留言