js使用canvas實現(xiàn)繪制月餅
皓月當(dāng)空,月圓中秋,在這個傳統(tǒng)節(jié)日里,除了賞月、猜燈謎、賞花燈等習(xí)俗外,還有就是品嘗美味的月餅。關(guān)于月餅,大家一定都知道月餅上會印著一個精美的圖案與紋路。
效果圖

HTML
這個 HTML 片段的主要組成部分包括一個canvas畫布、一個背景圖片、文字以及一組按鈕功能。 關(guān)于css部分過于冗長大家可以直接去碼上掘金閱讀。
<div class="banner">
<canvas id="canvas" width="500" height="500"> </canvas>
<div class="basebg"> </div>
<img src="./bg.png" alt="" class="bg">
<img src="./sc1.png" alt="" class="sc1">
<div class="text">———— 但愿人長久,千里共嬋娟 ————</div>
<div class="btn">
<div class="model" onclick="model(1)">單筆</div>
<div class="model" onclick="model(8)">對稱</div>
<div class="reset" onclick="reset()">清空畫布</div>
</div>
</div>初步繪制
我們來看一下繪制的邏輯,首先通過了事件監(jiān)聽器綁定了pointerdown、pointermove、pointerup。
pointerdown:當(dāng)用戶按下鼠標(biāo)左鍵或觸摸屏幕時,isDrawing 的變量設(shè)置為 true,表示用戶開啟繪制。
pointermove:當(dāng)用戶在Canvas上移動鼠標(biāo)或手指時,首先判斷isDrawing的值,確定用戶是否開啟繪制,如果為開啟繪制,則獲取鼠標(biāo)或觸摸事件的坐標(biāo)信息(e.clientX 和 e.clientY)并根據(jù)Canvas的相對位置(使用 getBoundingClientRect() 方法計算)將坐標(biāo)信息轉(zhuǎn)換為Canvas內(nèi)部的坐標(biāo)(以左上角為原點)。將其賦值給 point對象(point對象中的表示一條線段的起點與終點),調(diào)用draw函數(shù)繪制線條。
pointerup:用戶釋放鼠標(biāo)左鍵或手指時觸發(fā)將 isDrawing 變量設(shè)置為 false,表示用戶停止繪制。
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
let point = {};
let lineNum = 8;
let isDrawing = false;
canvas.addEventListener('pointerdown', (e) => {
isDrawing = true;
});
canvas.addEventListener('pointermove', (e) => {
if (!isDrawing) return;
const x = e.clientX - canvas.getBoundingClientRect().left
const y = e.clientY - canvas.getBoundingClientRect().top
point.x1 = x;
point.y1 = y;
draw(ctx, "#fdbb07", 5);
point.x2 = x;
point.y2 = y;
});
canvas.addEventListener('pointerup', (e) => {
isDrawing = false;
});pointerevnet與touchevent
一般來講在電腦上我們都會使用MouseEvent,但是如果想要在手機上也能進行繪制,就需要使用PointerEvent和TouchEvent。
PointerEvent是一個通用的事件類型,用于處理多種輸入設(shè)備(包括鼠標(biāo)、觸摸屏、觸控筆等)的輸入事件。TouchEvent用于處理觸摸屏輸入事件,如觸摸、滑動等。所有在這里我們使用了PointerEvent,因為它更加具備通用性。
不過在實際開發(fā)中,pointermove事件出現(xiàn)繪制中斷的情況,于是只能再添加一個touchmove監(jiān)聽器。 需要注意的是TouchEvent獲取坐標(biāo)的方式與PointerEvent略有不同。
e.touches[0].clientX e.touches[0].clientY
getBoundingClientRect()
在獲取坐標(biāo)時,我們還做了一個操作,減去getBoundingClientRect()的top和left。
getBoundingClientRect返回值是一個 DOMRect 對象,這個對象是由該元素的 getClientRects() 方法返回的一組矩形的集合,就是該元素的 CSS 邊框大小。返回的結(jié)果是包含完整元素的最小矩形,并且擁有 left, top, right, bottom, x, y, width, 和 height 這幾個以像素為單位的只讀屬性用于描述整個邊框。除了 width 和 height 以外的屬性是相對于視圖窗口的左上角來計算的。

這步其實可有可無,是因為在一開始還沒寫css時發(fā)現(xiàn)的一個沒寫* {margin: 0;padding: 0;}導(dǎo)致的問題。直接來看一下區(qū)別,一開始寫了css還可能發(fā)現(xiàn)不了。


繪制函數(shù)
接下來是繪制的函數(shù),傳入了canvas對象、畫筆顏色和線條寬度,再通過point中線段的起點與終點進行繪制。
function draw(canvas, color, lineWidth) {
canvas.strokeStyle = color;
canvas.lineWidth = lineWidth;
canvas.lineCap = "round";
canvas.moveTo(point.x1, point.y1);
canvas.lineTo(point.x2, point.y2);
canvas.stroke();
}對稱繪制
上一步簡單了實現(xiàn)了繪制功能,已經(jīng)可以繪制月餅的圖案,不過有的月餅上都是一些對稱的的圖案,簡單的靠一支畫筆完全不可能畫出對稱的效果。所以還需要一個能繪制對稱圖案的功能。 大致的方案是通過rotate進行旋轉(zhuǎn),為了保證畫筆的點位正常,需要使用translate將坐標(biāo)原點移動到 Canvas 的中心位置,對point坐標(biāo)都要進行偏移操作。
function draw(canvas, color, lineWidth) {
canvas.strokeStyle = color;
canvas.lineWidth = lineWidth;
canvas.lineCap = "round";
var r = 360 / lineNum * Math.PI / 180;
for (let i = 0; i < lineNum; i++) {
canvas.save();
canvas.translate(width / 2, height / 2);
canvas.rotate(r * i);
canvas.beginPath();
canvas.moveTo(point.x1 - width / 2, point.y1 - height / 2);
canvas.lineTo(point.x2 - width / 2, point.y2 - height / 2);
canvas.stroke();
canvas.restore();
}
}結(jié)語
至此,繪制的相關(guān)功能寫完了,還有一個額外的操作是,對于圖案線條添加了邊框,本意是想增加線條的立體感,但是寫完發(fā)現(xiàn)一點也感覺不到。由于在繪制過程中線條交叉會導(dǎo)致邊框覆蓋之前線條的情況,于是使用了一個簡單粗暴的方案,在這個canvas中下面在添加一個canvas進行同步繪制單獨的邊框效果。
到此這篇關(guān)于js使用canvas實現(xiàn)繪制月餅的文章就介紹到這了,更多相關(guān)js canvas繪制月餅內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript中removeChild 方法開發(fā)示例代碼
這篇文章主要介紹了JavaScript中removeChild 方法開發(fā)示例代碼,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-08-08
30分鐘快速入門掌握ES6/ES2015的核心內(nèi)容(下)
這篇文章主要給大家介紹了如何通過30分鐘快速入門掌握ES6/ES2015的核心內(nèi)容的相關(guān)資料,之前給大家介紹過基礎(chǔ)的一些內(nèi)容,下面繼續(xù)來介紹一些其他的新特性,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-04-04

