--- title: 刮刮樂實作練習 --- ## 實作開始 ### Step 1: 刮刮樂原理 及 分析 HTML 架構 刮刮樂結構分解 ![image](https://hackmd.io/_uploads/HyW4PveHT.png) ![image](https://hackmd.io/_uploads/Sk07gdlS6.png) ![image](https://hackmd.io/_uploads/HJ8wT4fHp.png) --- ### Step 2: 建立 index.html 及初步的 html 內容 1. 使用 `html:5 + TAB 鍵` 在編譯器上建立初步的 HTML 2. 引入 css `reset.min.css` ```htmlembedded= <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css"> ``` 3. 建立 `<style></style>` 標籤 ```css= <style> * { box-sizing: border-box; } </style> ``` 4. 建立主架構的 html 內容 可以參考 [Emmet](https://docs.emmet.io/cheat-sheet/) 語法簡化 HTML 撰寫 ``` a. 建立最一層 div(class="container") b. 建立第二層 div(class="content") c. .content 內建立 <img> 元素並放入圖片網址(設定寬高400px) ``` > 圖片網址: > `https://images.pexels.com/photos/2660348/pexels-photo-2660348.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1` > Emment 語法: > `.container>.content>img[src="https://images.pexels.com/photos/2660348/pexels-photo-2660348.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" width="400" height="400"]` >實際執行結果如下 ```htmlembedded= <div class="container"> <div class="content"> <img src="https://images.pexels.com/photos/2660348/pexels-photo-2660348.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" width="400" height="400"> </div> </div> ``` --- ### Step 3: 水平且垂直置中內容 a. 在 .content 的 div 中 水平置中圖片 b. 水平且垂直置中 class="container" 的 div #### a. 在 .content 的 div 中 水平置中圖片 幫 .content 加上背景更好 debug ```sass= /* 方法一 */ .content { text-align: center; } /* 方法二 */ /* 多個元素的話建議採用此方法 */ .content { display: flex; /* grid 也可以 */ justify-content: center; align-items: center; /* 若需要垂直置中的話 */ flex-wrap: wrap; /* 多個元素需要斷行 */ } ``` #### b. 水平且垂直置中 class="container" 的 div ```sass= /* 方法一 針對父容器設置高度 */ .content { display: flex; justify-content: center; align-items: center; flex-wrap: wrap; /* 配上原本的 flex,需要撐高元素滿版 */ height: 100vh; /* 有延伸問題,無法讓網頁隨著內容長寬出現滾軸 */ } /* 方法二 絕對位置 */ .container { position: relative; height: 100vh; } .content { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); /* 內容物的 width 和 height 的 50% */ } ``` --- ### Step 4: 如何解決 height: 100vh 無法出現滾軸問題 #### a. 在原本架構外層建立 class="full-page-container" 的 DIV 用 `position: fixed;` 撐滿頁面 ```htmlembedded= <div class="full-page-container"></div> ``` ```css= .full-page-container { position: fixed; top: 0; left: 0; right: 0; bottom: 0; overflow: auto; padding: .5rem; background-color: brown; } ``` #### b. 重新調整 .container 的樣式 ``` sass= .container { position: relative; width: 100%; height: 100%; /* 需要依內容物最小寬高撐開,影響手機版顯示 */ min-width: 400px; min-height: 400px; } ``` --- ### Step 5: 建立 Canvas 元素 放入 content div 內 ```htmlembedded= <canvas id="draw-container" width="400" height="400"></canvas> ``` ```css= canvas { position: absolute; top: 0; left: 0; } ``` --- ### Step 6: 開始實作 Cavnas 相關的函式 - Canvas 的 globalCompositeOperation 屬性:[W3C](https://www.w3schools.com/jsref/canvas_globalcompositeoperation.asp) #### a. 建立 window.onload 函式 等圖片載入後才進行刮刮樂遊戲 ```javascript= window.onload = function () { const canvas = document.getElementById('draw-container') const ctx = canvas.getContext('2d') /* css 上的 background-color 無法受到 canvas 影響 */ ctx.fillStyle = 'rgb(208,208,208)' // 灰色 ctx.fillRect(0, 0, canvas.width, canvas.height) /* 設定後續的圖像疊合模式 */ ctx.globalCompositeOperation = 'destination-out' } /* 如果使用 css 上的 background-color 無法用上述方式移除 */ ``` #### b. 建立 Cavnas 上的滑鼠事件 (onmousedown/onmousemove/onmouseup) ```javascript= window.onload = function () { ... let isDrawing = false const handleMouseDown = (e) => { // 開始畫圖 isDrawing = true } const handleMouseMove = (e) => { // 刮除灰色區塊中 if (!isDrawing) return } const handleMouseUp = (e) => { // 停止刮除 if (!isDrawing) return isDrawing = false } canvas.onmousedown = handleMouseDown canvas.onmousemove = handleMouseMove canvas.onmouseup = handleMouseUp } ``` #### c. 取得滑鼠畫圖時的 X/Y 座標位置 (起始點) - getBoundingClientRect 函式:[MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Element/getBoundingClientRect) ![image](https://hackmd.io/_uploads/rka2UOsBp.png) ```javascript= /* e = mouse event */ const getRectPosition = (e) => { const rect = canvas.getBoundingClientRect() return { x: e.clientX - rect.left, y: e.clientY - rect.top, } } ``` #### d. 移除滑鼠點擊位置的區塊 - Canvas arc 函式:[MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arc) - Canvas rect 函式:[MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/rect) ```javascript= const eraseImage = ({ x, y }) => { const circleRadius = 20 ctx.beginPath() ctx.arc(x, y, circleRadius, 0, Math.PI * 2) // 圓形 // ctx.rect(x, y, 40, 40) // 方形 ctx.fill() } ``` #### e. 針對滑鼠事件判斷需要移除的區塊 ```javascript= const handleMouseDown = (e) => { ... const { x, y } = getRectPosition(e) eraseImage({ x, y }) } const handleMouseMove = (e) => { ... const { x, y } = getRectPosition(e) eraseImage({ x, y }) } ``` ## 其他資源 - ES6 入門:https://es6.ruanyifeng.com/#README - 箭頭函式:[MDN](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Functions/Arrow_functions)