前言
首先這問題是2019.8.6 下午一個漂亮的小姐姐問我的,當時回答的不是很好,晚上回去之後,休息了一會,然後就開始著手實現一下
然後就開始網上查檢視有什麼實現的思路不,結果百度上翻了幾篇是沒找到我想要的,我想要一個原生的實現方式,因為這樣我能從中收穫點東西,儘早完善前端體系,入門前端。
既然沒有借鑑的,可能是暫時沒找到,那就先按照我現在的思路寫一個。以後真遇到了,再看看是如何實現的,對比,然後學習。
思路梳理
- 首先通訊方面不用說,你畫我猜這種多人的遊戲肯定是 websocket 通訊 (可能還有更好的)
- 畫圖技術實現也肯定確定是 canvas
- 思考一:兩個核心技術確定了,那麼現在開始先做畫圖這一步,思考怎麼通過滑鼠移動(移動端是螢幕觸動,我先搞下PC端的試試思路)就能在畫板上展示出效果?
- 思考二:如何記錄這些資訊?(因為這些資訊才是需要websocket進行傳送的內容)
思考一:如何在canvas上畫出內容
- 肯定要和監聽事件緊密相關,想法就是當在canvas內部點下滑鼠移動的時候,不斷觸發事件,然後根據事件中的座標位置,渲染路徑
- 但是發現 canvas 沒有監聽滑鼠按下之後 移動的響應事件(也可能有,但是我沒找到)
- 有一個簡單的曲線救國的套路:在滑鼠按下事件中繫結 滑鼠移動事件,然後在滑鼠抬起事件中 登出滑鼠移動事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<canvas id="canvas" width="500" height="500" style="border: 1px solid #999;"></canvas>
<script>
let canvas = document.getElementById('canvas')
let cxt = canvas.getContext('2d')
// 在 canvas 區域滑鼠按下的時候觸發
canvas.onmousedown = (e) => {
cxt.strokeStyle = "#3d7e9a"
cxt.beginPath() // 開始設定路徑
cxt.moveTo(e.clientX, e.clientY) // 路徑原點
// 滑鼠移動事件
canvas.onmousemove = (e) => {
cxt.lineTo(e.clientX, e.clientY) // 路徑終點
cxt.stroke() // 輸出路徑輪廓(空心)
}
}
// 在 canvas 區域滑鼠抬起的時候觸發
canvas.onmouseup = () => {
canvas.onmousemove = null
}
</script>
</body>
</html>
複製程式碼
實現效果(Gif動圖) ?
思考二:如何記錄這些資訊?(不對,應該說我需要記錄哪些資訊)
- 我思考我應該記錄哪些資訊呢 ?,別的API可能也不會,能看到的就是我能得到移動中的每個點的資訊
- 如果把每個點的座標都記錄下來,然後傳送這個,應該就可以了
- 實踐了一下,發現,如果記錄每個點的資訊傳送幾乎是沒辦法的,一個是數量越來越多,一個是難道每次都要清空一次畫布重新繪製麼?我也是傻!
- ? 然後就思考到另一種方式,就是 當滑鼠按下時候,到抬起這段時間內移動的點,都記錄上,然後當滑鼠抬起後再傳送資料,這樣每次傳送的資料就小一些了,然後其他使用者那邊也不用清空畫布了
- 具體實現如下,下面有 ➕ 符號的,表示是新加的內容
<!-- ? 實踐 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<canvas id="canvas" width="400" height="400" style="border: 1px solid #999;"></canvas>
<!-- ➕ 模擬成另一個使用者 -->
<canvas id="canvas2" width="400" height="400" style="border: 1px solid blue;"></canvas>
<script>
let canvas = document.getElementById('canvas')
let cxt = canvas.getContext('2d')
let dotArr = [] // ➕ 儲存每次按下和抬起 中間時間移動點的位置資訊
// 在 canvas 區域滑鼠按下的時候觸發
canvas.onmousedown = (e) => {
cxt.strokeStyle = "#3d7e9a"
cxt.beginPath() // 開始設定路徑
cxt.moveTo(e.clientX, e.clientY) // 路徑原點
// ➕ 先清空,然後再把陣列第一個位置資料,記錄為路徑原點座標
dotArr.length = 0;
dotArr.push({x: e.clientX,y: e.clientY})
// 滑鼠移動事件
canvas.onmousemove = (e) => {
cxt.lineTo(e.clientX, e.clientY) // 路徑終點
cxt.stroke() // 輸出路徑輪廓(空心)
// ➕ 記錄移動中的每個點的資訊
dotArr.push({x: e.clientX,y: e.clientY})
}
}
// 在 canvas 區域滑鼠抬起的時候觸發
document.onmouseup = () => {
canvas.onmousemove = null
// ➕ 假裝傳送了資料,讓第二個圖開始繪製
renderCanvas()
}
// ➕ 一對多釋出訂閱,適合用釋出訂閱者模式,這裡就一個先這麼寫著
function renderCanvas() {
let canvas2 = document.getElementById('canvas2')
let cxt2 = canvas2.getContext('2d')
cxt2.beginPath() // 開始設定路徑
cxt2.moveTo(dotArr[0].x, dotArr[0].y) // 路徑原點
for (let i = 1, len = dotArr.length; i < len; i++) {
cxt2.lineTo(dotArr[i].x, dotArr[i].y) // 路徑重點
cxt2.stroke() // 輸出路徑輪廓(空心)
}
}
</script>
</body>
</html>
複製程式碼
實現效果(Gif動圖) ?
思考三:websocket 部分如何實現
寫作中...
思考四:如果使用者畫一個小時,會有什麼問題,怎麼解決?
思考五:還能想到其他可能的問題麼?或者可能進行的優化?
思考六:我是否可以通過 TensorFlow.js 來實現一個簡易的AI,識別出我畫圖的內容是什麼?(這就跟開始的題意不符了,但是純屬處於個人興趣,可以一試)
思考七:遇到的問題
- 點的位置和視口的位置的問題(目測可以解決)
- 移動時候如果滑鼠移出到canvas外部,再放開滑鼠,canvas 監聽不了,也就沒辦法登出事件 (✅滑鼠抬起事件繫結到document上解決此問題)
後記
都差不多實現一遍之後,我要抽離出裡面能複用的模組,省的下次再寫一遍了
此文章屬於個人筆記文章,如果有可能千萬不要推薦呀,要不就很尷尬了
我發現掘金有一篇 你畫我猜的實現文章,晚上回去看