原生JS實現你畫我猜的一點點功能

朱昆鵬發表於2019-08-06

前言

首先這問題是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動圖) ?

原生JS實現你畫我猜的一點點功能

思考二:如何記錄這些資訊?(不對,應該說我需要記錄哪些資訊)

  • 我思考我應該記錄哪些資訊呢 ?,別的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動圖) ?

原生JS實現你畫我猜的一點點功能

思考三:websocket 部分如何實現

寫作中...


思考四:如果使用者畫一個小時,會有什麼問題,怎麼解決?


思考五:還能想到其他可能的問題麼?或者可能進行的優化?


思考六:我是否可以通過 TensorFlow.js 來實現一個簡易的AI,識別出我畫圖的內容是什麼?(這就跟開始的題意不符了,但是純屬處於個人興趣,可以一試)


思考七:遇到的問題

  • 點的位置和視口的位置的問題(目測可以解決)
  • 移動時候如果滑鼠移出到canvas外部,再放開滑鼠,canvas 監聽不了,也就沒辦法登出事件 (✅滑鼠抬起事件繫結到document上解決此問題)

後記

都差不多實現一遍之後,我要抽離出裡面能複用的模組,省的下次再寫一遍了

此文章屬於個人筆記文章,如果有可能千萬不要推薦呀,要不就很尷尬了

我發現掘金有一篇 你畫我猜的實現文章,晚上回去看

相關文章