本文中,我們添加了在 HTML5 畫布上選擇顏色和繪制線條、圓形和正方形的功能!
繪圖過程如何工作
為了在畫布上繪圖,我們需要知道
將這些屬性添加到 Canvas 類的構造函數中:
this.activeColor = '#000000';
this.startPoint = null;
this.endPoint = null;
this.pointMode = 'start';
this.mode = 'Line';
所以我們默認畫一條黑線。 現在我們添加一個函數,讓我們改變我們繪制的顏色,作為 Canvas 類的一個方法:
setColor(color) {
this.activeColor = color;
this.ctx.strokeStyle = color;
this.ctx.fillStyle = color;
}
使調色板工作
現在我們有了 setColor 方法,我們可以完成 Palette.draw() 方法。 我們將畫布對象添加為參數,并將 onclick 事件處理程序附加到每個調色板方塊:
draw(canvas) {
const row1 = document.querySelectorAll('#row-1 .palette');
const row2 = document.querySelectorAll('#row-2 .palette');
row1.forEach((div, idx) => {
div.style.backgroundColor = this.colors[0][idx];
div.onclick = e => canvas.setColor(this.colors[0][idx]);
});
row2.forEach((div, idx) => {
div.style.backgroundColor = this.colors[1][idx];
div.onclick = e => canvas.setColor(this.colors[1][idx]);
});
}
由于我們在這里添加了它作為參數,所以我們需要在 index.html 文件中進行設置:
palette.draw(canvas);
選擇要繪制的形狀
該項目允許繪制 5 種形狀:線條、空心圓、實心圓、空心矩形和實心矩形。 我們默認為一行。 為了改變形狀(代碼中稱為模式),首先將此方法添加到 Canvas :
setMode(mode) {
this.mode = mode;
}
然后在 HTML 中,在調色板下方,添加一行按鈕,讓我們選擇要繪制的形狀:
<div id="draw-methods">
<button onclick="canvas.setMode('Line')">Line</button>
<button onclick="canvas.setMode('Hollow Rectangle')">Hollow Rectangle</button>
<button onclick="canvas.setMode('Filled Rectangle')">Filled Rectangle</button>
<button onclick="canvas.setMode('Hollow Circle')">Hollow Circle</button>
<button onclick="canvas.setMode('Filled Circle')">Filled Circle</button>
</div>
處理要繪制的畫布上的點擊
這是 Canvas 的 handleDraw 方法。 我會給你代碼然后解釋它在做什么,因為它比我們目前看到的更復雜。
handleDraw(e) {
const rect = this.canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top; if (this.pointMode == 'start') {
this.startPoint = [x, y];
this.pointMode = 'end';
} else if (this.pointMode == 'end') {
this.pointMode = 'start';
this.endPoint = [x, y];
// do the drawing
if (this.mode == 'Line') {
this.drawLine(this.startPoint, this.endPoint);
} else if (this.mode == 'Hollow Rectangle') {
this.drawHollowRectangle(this.startPoint, this.endPoint);
} else if (this.mode == 'Filled Rectangle') {
this.drawFilledRectangle(this.startPoint, this.endPoint);
} else if (this.mode == 'Hollow Circle') {
this.drawHollowCircle(this.startPoint, this.endPoint);
} else if (this.mode == 'Filled Circle') {
this.drawFilledCircle(this.startPoint, this.endPoint);
}
this.startPoint = null;
this.endPoint = null;
}
}
前三行是關于確定用戶點擊的位置。您可能會認為,當您單擊畫布時,鼠標單擊事件只會包含您在畫布中單擊的位置的坐標。但這并不那么容易。你必須自己計算。
getBoundingRectClient 返回值,例如畫布從左上角到左下角的距離。頁面的左上角是 (0, 0),越往右越往下,數值越大。 e是傳遞給函數的參數,代表鼠標點擊事件。 clientX 和 clientY 代表您在頁面上單擊的位置。減去畫布元素的偏移量可以得到鼠標在畫布元素中的位置。
一旦我們有了點擊的位置,我們就需要知道這是第一次(“開始”)還是第二次(“結束”)點擊。每個形狀都是通過兩次點擊繪制的。對于一條線,只需選擇起點和終點。對于圓,選擇中心和邊緣。對于矩形,選擇兩個對角。如果是第一次單擊,則存儲單擊的位置,但不執行任何操作。如果是第二次單擊,則存儲其位置,繪制形狀,然后忘記位置。
繪制形狀的方法可能看起來比實際復雜。對于 drawLine ,它很簡單:
drawLine(startPoint, endPoint) {
this.ctx.beginPath();
this.ctx.moveTo(startPoint[0], startPoint[1]);
this.ctx.lineTo(endPoint[0], endPoint[1]);
this.ctx.stroke();
}
繪制空心和填充的矩形只需要計算兩個未點擊的點:
drawHollowRectangle(startPoint, endPoint) {
this.ctx.beginPath();
this.ctx.strokeRect(
startPoint[0],
startPoint[1],
endPoint[0] - startPoint[0],
endPoint[1] - startPoint[1]
);
}drawFilledRectangle(startPoint, endPoint) {
this.ctx.beginPath();
this.ctx.fillRect(
startPoint[0],
startPoint[1],
endPoint[0] - startPoint[0],
endPoint[1] - startPoint[1]
);
}
繪制圓的方法需要使用距離公式計算半徑,然后繪制 360 度圓弧。
drawHollowCircle(startPoint, endPoint) {
const x = startPoint[0] - endPoint[0];
const y = startPoint[1] - endPoint[1];
const radius = Math.sqrt(x * x + y * y);
this.ctx.beginPath();
this.ctx.arc(startPoint[0], startPoint[1], radius, 0, 2 * Math.PI, false);
this.ctx.stroke();
}drawFilledCircle(startPoint, endPoint) {
const x = startPoint[0] - endPoint[0];
const y = startPoint[1] - endPoint[1];
const radius = Math.sqrt(x * x + y * y);
this.ctx.beginPath();
this.ctx.arc(startPoint[0], startPoint[1], radius, 0, 2 * Math.PI, false);
this.ctx.fill();
}
附加 Canvas 事件偵聽器
讓畫布真正響應被點擊需要將這兩行添加到構造函數中:
this.handleDraw = this.handleDraw.bind(this);
this.canvas.addEventListener('click', this.handleDraw);
那些使用過 React 的人可能會認識到這種模式。 但為什么需要它? 它與事件偵聽器的工作方式有關。 如果沒有綁定,handleDraw 中的 this 上下文變量將指向被點擊的 HTML 元素。 在這種情況下,畫布元素。 但這意味著我們無法訪問具有我們需要的方法的 Canvas 對象。 通過使用 bind,我們強制 this 引用對象。
把它們放在一起
在此步驟中更改的文件此時應如下所示:
<html>
<head>
<title>Collaborative Drawing App</title>
<style type="text/css">
canvas {
border: 1px solid black;
}
.palette {
border: 1px solid black;
display: inline-block;
margin: 2px;
height: 25px;
width: 25px;
}
</style>
</head>
<body>
<h1>Collaborative Drawing App</h1>
<div>
<canvas id="canvas" height="500px" width="500px"></canvas><br />
</div>
<div id="palette">
<div id="row-1">
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
</div>
<div id="row-2">
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
<div class="palette"></div>
</div>
</div>
<div id="draw-methods">
<button onclick="canvas.setMode('Line')">Line</button>
<button onclick="canvas.setMode('Hollow Rectangle')">Hollow Rectangle</button>
<button onclick="canvas.setMode('Filled Rectangle')">Filled Rectangle</button>
<button onclick="canvas.setMode('Hollow Circle')">Hollow Circle</button>
<button onclick="canvas.setMode('Filled Circle')">Filled Circle</button>
</div>
<script type="text/javascript" src="./script.js"></script>
<script type="text/javascript">
const canvas = new Canvas();
const palette = new Palette();
palette.draw(canvas);
</script>
</body>
</html>
class Canvas {
constructor() {
this.canvas = document.querySelector('#canvas');
this.ctx = this.canvas.getContext('2d');
this.activeColor = '#000000';
this.startPoint = null;
this.endPoint = null;
this.pointMode = 'start';
this.mode = 'Line';
this.handleDraw = this.handleDraw.bind(this);
this.canvas.addEventListener('click', this.handleDraw);
}
setColor(color) {
this.activeColor = color;
this.ctx.strokeStyle = color;
this.ctx.fillStyle = color;
}
setMode(mode) {
this.mode = mode;
}
handleDraw(e) {
const rect = this.canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
if (this.pointMode == 'start') {
this.startPoint = [x, y];
this.pointMode = 'end';
} else if (this.pointMode == 'end') {
this.pointMode = 'start';
this.endPoint = [x, y];
// do the drawing
if (this.mode == 'Line') {
this.drawLine(this.startPoint, this.endPoint);
} else if (this.mode == 'Hollow Rectangle') {
this.drawHollowRectangle(this.startPoint, this.endPoint);
} else if (this.mode == 'Filled Rectangle') {
this.drawFilledRectangle(this.startPoint, this.endPoint);
} else if (this.mode == 'Hollow Circle') {
this.drawHollowCircle(this.startPoint, this.endPoint);
} else if (this.mode == 'Filled Circle') {
this.drawFilledCircle(this.startPoint, this.endPoint);
}
this.startPoint = null;
this.endPoint = null;
}
}
drawLine(startPoint, endPoint) {
this.ctx.beginPath();
this.ctx.moveTo(startPoint[0], startPoint[1]);
this.ctx.lineTo(endPoint[0], endPoint[1]);
this.ctx.stroke();
}
drawHollowRectangle(startPoint, endPoint) {
this.ctx.beginPath();
this.ctx.strokeRect(
startPoint[0],
startPoint[1],
endPoint[0] - startPoint[0],
endPoint[1] - startPoint[1]
);
}
drawFilledRectangle(startPoint, endPoint) {
this.ctx.beginPath();
this.ctx.fillRect(
startPoint[0],
startPoint[1],
endPoint[0] - startPoint[0],
endPoint[1] - startPoint[1]
);
}
drawHollowCircle(startPoint, endPoint) {
const x = startPoint[0] - endPoint[0];
const y = startPoint[1] - endPoint[1];
const radius = Math.sqrt(x * x + y * y);
this.ctx.beginPath();
this.ctx.arc(startPoint[0], startPoint[1], radius, 0, 2 * Math.PI, false);
this.ctx.stroke();
}
drawFilledCircle(startPoint, endPoint) {
const x = startPoint[0] - endPoint[0];
const y = startPoint[1] - endPoint[1];
const radius = Math.sqrt(x * x + y * y);
this.ctx.beginPath();
this.ctx.arc(startPoint[0], startPoint[1], radius, 0, 2 * Math.PI, false);
this.ctx.fill();
}
}
class Palette {
constructor() {
this.colors = [
['#000000', '#FFFFFF', '#7F7F7F', '#C3C3C3', '#880015', '#B97A57', '#ED1C24', '#FFAEC9', '#FF7F27', '#FFC90E'],
['#FFF200', '#EFE4B0', '#22B14C', '#B5E61D', '#00A2E8', '#99D9EA', '#3F48CC', '#7092BE', '#A349A4', '#C8BFE7']
];
}
draw(canvas) {
const row1 = document.querySelectorAll('#row-1 .palette');
const row2 = document.querySelectorAll('#row-2 .palette');
row1.forEach((div, idx) => {
div.style.backgroundColor = this.colors[0][idx];
div.onclick = e => canvas.setColor(this.colors[0][idx]);
});
row2.forEach((div, idx) => {
div.style.backgroundColor = this.colors[1][idx];
div.onclick = e => canvas.setColor(this.colors[1][idx]);
});
}
}
重新啟動節點服務器后,嘗試更改顏色,設置要繪制的形狀,然后開始單擊畫布。 這是我為確保它有效而進行的一些測試:
在下一篇文章中,我們將添加一些方便的函數來顯示選擇的形狀,以及應用程序期望用戶下一步做什么的簡短說明。
tml5經歷了前期html快速的更新換代,以其獨有特性的優勢迅速占據了網頁開發市場鰲頭。介于目前學習和想要從事html5網頁開發的人越來越多。千鋒老師給大家整理了一下基本的html5學習計劃路線圖,適應于一些零基礎學習html5的同學借鑒。
HTML5學習路線規劃:
一、HTML5基礎
HTML 快速入門、文本、圖像、鏈接、表格、列表、表單、框架;
二、CSS3基礎
CSS基礎語法、各種選擇器(通用選擇器、元素選擇器、id和class選擇器、后代選擇器、偽類選擇器等)、框模型與背景、文本格式化、表格、顯示與定位、瀏覽器調試
三、HTML5高級
HTML5 增強表單元素、HTML5驗證、HTML5新事件和新屬性、Canvas繪圖、HTML5 SVG、音頻和視頻處理、離線Web存儲與應用、HTML5 拖放、Web Socket API、Geolocation API、Web Worker API
四、實戰技能目標
掌握JQuery核心API,HTML5 中的繪圖、音頻視頻處理、表單新特性,輕量級WEBAPP開發。
專業的html5開發工程師需要掌握的專業技術有:
第一階段:前端頁面重構:PC端網站布局、HTML5+CSS3基礎項目、WebAPP頁面布局;
第二階段:JavaScript高級程序設計:原生JavaScript交互功能開發、面向對象開發與ES5/ES6、JavaScript工具庫自主研發;
第三階段:PC端全棧項目開發:jQuery經典特效交互開發、HTTP協議,Ajxa進階與后端開發、前端工程化與模塊化應用、PC端網站開發、PC端管理信息系統前端開發;
第四階段:移動端webAPP開發:Touch端項目、微信場景項目、應用Vue.js開發WebApp項目、應用Ionic開發WebApp項目、應用React.js開發WebApp;
第五階段:混合(Hybrid)開發:各類混合應用開發;
第六階段:NodeJS全棧開發:WebApp后端系統開發;
第七階段:大數據可視化:數據可視化入門、D3.jS詳解及項目實戰。
HTML5開發從根本上改變了開發者開發web和應用的方式,從桌面瀏覽器到移動應用,HTML5都已經成為前端開發必不可少的語言。特別是移動互聯網的爆發和微信這個超級應用對HTML5的支持,掌握HTML5語言的程序員已然成為各個互聯網公司的標配,薪資也是一路走高。
適的動畫不僅更能吸引人們的眼球,也能讓你的應用體驗更為流暢,而將動畫的效果做到極致,才能讓用戶感到使用你的應用是一種享受,而不是覺得生硬和枯燥。那么Web前端人員是否了解各種前端動畫效果實現方式的異同,具體應用中又是如何實現的呢?下面就讓我們一起來看一看吧~
一、JavaScript 動畫
因為沒有其它可用的實現方式,最初的前端動畫都是JS來實現,實現上就是通過一個定時器setInterval 每隔一定時間來改變元素的樣式,動畫結束時clearInterval即可。早期的類庫包括 jquery、prototype、mootools 等等都是這種方式。
盡管這種方式動畫的可控性很強,但是問題也很明顯:
· 性能不佳,因為需要不斷獲取和修改Dom的布局,所以導致了大量頁面重排(repaint)
· 缺乏標準,不同的庫使用了不同的API,導致即使是簡單的動畫也有各不相同的實現方式,調整起來比較耗時
· 帶寬消耗,相對豐富的動畫庫代碼量都很大,結果就是增加了http請求的大小,降低了頁面的載入時間
二、CSS3 動畫
css3 加了兩種動畫的實現方式,一種是 transition, 一種是 animation。
transition 包含4種屬性:transition-delay transition-duration transition-property transition-timing-function,對應動畫的4種屬性: 延遲、持續時間、對應css屬性和緩動函數,
transform 包含7種屬性:animation-name animation-duration animation-timing-function animation-delay animation-direction animation-iteration-count animation-fill-mode animation-play-state,它們可以定義動畫名稱,持續時間,緩動函數,動畫延遲,動畫方向,重復次數,填充模式。
總的來書,css 動畫相比與JS更輕量,性能更好,更易于實現,同時也不必擔心缺乏標準和增加帶寬消耗的問題。animation 相比 transtion 使用起來更為復雜,但也提供了更多的控制,其中最重要的就是 frame 的支持,不過通過一些簡單的JS庫,例如 TJ 的 move.js, 我們也能在JS中通過 transition 來實現更復雜的控制。
三、Html5 動畫
Html5 定義了三種繪圖的方式,canvas svg Webgl,其中svg做為一種可縮放矢量圖形的實現是基于xml標簽定義的,它有專門的 animate 標簽來定義動畫。而為 canvas 或者 Webgl 實現動畫則需要通過 requestAnimationFrame (簡稱 raf) 來定期刷新畫布。盡管說 raf 的方式會讓代碼變得復雜,但是因為不需要那么多的文檔對象(通常瀏覽器只需要管理一個畫布),它的性能也好很多,尤其是在內存吃緊的移動端上面。
通過新的 raf 接口以及一些改進手段我們也可以用JS來實現高性能的動畫。主要手段如下:
1. 減少 Dom 樣式屬性查詢,Dom 樣式屬性的查詢會導致頁面重排,從而消耗性能,通過將屬性保存在JS變量中就可以避免在動畫時去查詢,從而減少卡頓。
2. 使用性能更好的 css transform 替代改變絕對定位元素的定位屬性
3. 在移動設備上使用 3d 硬件加速,最簡單辦法就是添加 -Webkit-transform: translateZ(0),原因是移動端的顯卡有很強的圖形渲染能力,而每個應用的 WebvieW 內存卻是極其有限的。
使用JS的動畫可控性更好,比如說通過事件捕捉可以很容易的設定不同參數。這方面做的最全面的有 Velocity.js,它可做為jquery 插件使用,對于初學者很友好。加入465042726,關于前端方面的更多問題我們可以一起交流!
*請認真填寫需求信息,我們會在24小時內與您取得聯系。