HTML5 Canvas คืออะไร? + วิธีใช้งานเบื้องต้น

HTML5 Canvas คืออะไร? + วิธีใช้งานเบื้องต้น Cover Image

HTML5 Canvas เป็นหนึ่งใน feature ที่มีคุณสมบัติทางด้านกราฟฟิคที่มาพร้อมกับ HTML5 โดยส่วนตัวเพิ่งเริ่มสนใจ Canvas ก็ตอนที่ได้ลองหัดทำเกมส์บน Browser นี่แหละครับ เลยไปนั่งอ่านและลองทำความเข้าใจอยู่ซักพัก สุดท้ายก็เลยเขียนและเรียบเรียงตามความเข้าใจของตัวเองไว้

ตัวอย่างนี้เป็นการทดลองและลองเล่น HTML5 Canvas โดยจะนำเสนอและวิธีการใช้งานและวาดรูปทรงต่างๆเบื้องต้น ทั้ง วาดสี่เหลี่ยม วาดวงกลม วาดเส้น แบบง่ายๆ :)

Canvas คืออะไร ?

Canvas เป็น Element หนึ่งใน HTML5 เหมือนอย่าง <img> หรือ <a> โดยใน <img> หรือ <a> จะมี attributes เช่น src และ href แต่ใน <canvas> จะมีเพียงแค่ 2 attributes คือ width และ height (ความกว้างและความสูงของ Canvas) เท่านั้น

Note: <canvas> นั้น required closed tag ด้วยไม่เหมือนอย่าง <img> ฉะนั้นก็อย่าลืมปิดแท็กด้วย </canvas> นะครับ :)

ตัวอย่างเช่น

<canvas id="tutorial" width="150" height="150"></canvas>

เป็นการประกาศสร้าง Element <canvas> ขึ้นมาโดยมี id ชื่อ canvas และมีความกว้างและความสูง 150 pixels

Note: โดยปกติแล้ว canvas จะมีความกว้าง 300px และสูง 150px ถึงแม้ว่าเราจะไม่ได้กำหนดขนาดให้มันก็ตาม

ใช้งาน Canvas เบื้องต้น

เริ่มแรกทำการสร้างไฟล์ HTML ขึ้นมา ผมทำการตั้งชื่อว่า index.html และมี element <canvas> ดังตัวอย่าง

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>HTML5 Canvas Tutorials</title>
</head>
<body>
  <canvas id="canvas" width="500" height="500"></canvas>

  <script src="main.js"></script>
</body>
</html>

จากนั้นลิงค์สคริปไปยังไฟล์ JavaScript ของเรา โดยทำการตั้งชื่อว่า main.js

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

จากโค๊ดด้านบน เราทำการเลือก element ที่มีไอดีว่า canvas ซึ่งก็คือตัว <canvas> จากนั้นทำการ getContext('2d') เพื่อทำการใช้ canvas context แบบ 2d (เป็นออปเจ็คที่เอาไว้วาดและสร้างกราฟฟิคต่างๆ)

ระบบ Coordinate System

โดยปกติแล้ว หากได้เรียนคณิตศาสตร์มา จะพบว่าเราจะใช้การกำหนดแกน x, y ด้วยระบบ Cartesian coordinate system ซึ่งกำหนดจุด x, y จากการลากเส้นแกนทั้งสองตัดกันเป็นมุมฉาก

ส่วนระบบของ Canvas coordinate system จะต่างออกไป จะนับจุดบนซ้ายเป็นจุด 0,0 ดังตัวอย่างเปรียบเทียบในภาพด้านล่างครับ

Canvas System

วาดสี่เหลี่ยม

การวาดสี่เหลี่ยม สามารถใช้ฟังค์ชัน fillRect(x, y, width, height) โดย x คือแกน x, y คือแกน y และ width, height คือความกว้างและความสูง ตามลำดับ

โดยเมื่อเราเลือก Element canvas และทำการ getContext() แล้ว เราสามารถวาดสี่เหลี่ยมแบบง่ายๆ ด้วยโค๊ดแบบนี้

// กำหนดสี
context.fillStyle = 'green';
// วาดสี่เหลี่ยม Rectangle (x, y, width, height) 
// โดยแกน x,y จะนับจากมุมบนซ้ายเป็นจุด 0, 0
context.fillRect(10, 10, 100, 100);

See the Pen WbyJEV by Chai Phonbopit (@Phonbopit) on CodePen.

นอกจาก fillRect() แล้ว เรายังสามารถใช้

  • strokeRect(x, y, width, height) : เพื่อวาดสี่เหลี่ยมให้มีเฉพาะเส้นขอบ
  • clearRect(x, y, width, height) : วาดสี่เหลี่ยมให้เป็น transparent หรืออีกในหนึ่งก็คือการลบ content อื่นๆ (เหมือนเป็นยางลบ)

เช่น

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

context.fillStyle = 'green';
context.fillRect(10, 10, 100, 100);

context.strokeStyle = 'red';
context.strokeRect(110, 110, 200, 200);

context.clearRect(35, 35, 50, 50);

See the Pen jEKxGO by Chai Phonbopit (@Phonbopit) on CodePen.

Paths

ให้นึกถึงเวลาคุณจับดินสอวาดรูป Path หนึ่งก็เหมือนกับการจิ้มดินสอลงบนกระดาษ เวลาคุณวาดรูป คุณก็จะมีการยกดินสอขึ้น และเลื่อนไปยังอีกตำแหน่งเพื่อวาดภาพต่อๆมา (ลองนึกว่าให้คุณวาดรูปแมวโดยไม่ยกดินสอซิ เปรียบเสมือนใช้ Canvas แค่ Path เดียว)

ฉะนั้น Path จะมีด้วยกัน 2 เมธอดคือ (ให้นึกถึงการใช้ดินสอวาดรูปเหมือนเดิม)

  1. moveTo(x, y) : หมายถึงเลื่อนดินสอไปยังตำแหน่งที่ต้องการ (จุดเริ่มต้น)
  2. lineTo(x, y) : หมายถึงว่าลากเส้นไปถึงตำแหน่งไหน (จุดสุดท้ายที่ยกมือ)

เช่น

context.moveTo(0, 100);
context.lineTo(200, 100);

แต่ว่ายังไม่มีอะไรเกิดขึ้น เนื่องจากว่าดินสอเราไส้หมดครับ ต้องเดิมไส้ใหม่ พอดีใช้ดินสอกด :)

context.strokeStyle = 'black';
context.stroke();

วาดวงกลม

การวาดวงกลมจะใช้ context.arc() ดังนี้ context.arc(x, y, radius, startAngle, endAngle, anticlockwise) โดย - x,y คือ แกน x และ y - radius: รัศมีของวงกลม - startAngle, endAngle คือ เรเดียน - anticlockwise : ทวนเข็มหรือตามเข็มนาฬิกา (true/false)

เช่นตัวอย่าง จะทำการวาดวงกลม 2 วง วงแรกขนาดรัศมี 40 ตรงตำแหน่ง 50, 50 โดยวาดเฉพาะเส้นรอบวง และวงที่สองขนาดเท่ากัน ที่ตำแหน่ง 120, 120 และวาดทั้งวงกลม จะได้ดังนี้

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

context.beginPath();
context.strokeStyle = "black";
context.lineWidth = 5;
context.arc(50, 50, 40, 0, Math.PI * 2, false);
context.stroke();
context.closePath();

context.beginPath();
context.fillStyle = '#dd0343'
context.arc(120, 120, 40, 0, Math.PI * 2);
context.fill();
context.closePath();

See the Pen GgGdOw by Chai Phonbopit (@Phonbopit) on CodePen.

วาดเส้น

การวาดเส้นบน Canvas เราจะใช้ร่วมกับการใช้ Path โดยมีฟังค์ชันที่สำคัญดังนี้

  • context.beginPath() : สำหรับกำหนด path
  • context.moveTo(x, y) : ตำแหน่งเริ่มต้นในการวาด
  • context.lineTo(x, y) : ตำแหน่งสิ้นสุดการวาด
  • context.lineWidth : เอาไว้กำหนดขนาดของเส้น
  • context.strokeStyle : เอาไว้กำหนดสีของเส้น
  • context.lineCap : กำหนดรูปแบบของเส้น เช่น round, square หรือ butt

ตัวอย่าง เช่น

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

context.beginPath();
context.strokeStyle = "#3984dd";
context.lineWidth = 10;
context.moveTo(50, 50);
context.lineTo(50, 200);
context.stroke();

context.beginPath();
context.strokeStyle = "#dd0343";
context.lineWidth = 10;
context.lineCap = 'round'
context.moveTo(100, 50);
context.lineTo(100, 200);
context.stroke();

context.beginPath();
context.strokeStyle = "#06f3d2";
context.lineWidth = 10;
context.lineCap = 'butt';
context.moveTo(150, 50);
context.lineTo(150, 200);
context.stroke();

See the Pen ZYRorp by Chai Phonbopit (@Phonbopit) on CodePen.

วาด Text

การวาด Text จะมีด้วยกัน 2 เมธอดคือ

  • fillText(text, x, y) : คือการแสดง Text แบบปกติ
  • strokeText(text, x, y) : วาดแต่เส้นขอบของ Text

และเมธอดสำคัญๆ อื่นๆด้วย เช่น

  • font : ทำการกำหนดขนาดและฟ้อนที่ต้องการใช้
  • fillStyle : ไว้กำหนดสีของ Text แบบเดียวกันกับการวาดสี่เหลี่ยม วงกลม เส้น
  • textAlign : สามารถกำหนด text-align ได้

ดังเช่นตัวอย่าง

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

context.beginPath();
context.font = '50px Open Sans sans-serif';
context.fillStyle = '#f36';
context.fillText('Hello HTML5 Canvas', 50, 50);

See the Pen EaRLEX by Chai Phonbopit (@Phonbopit) on CodePen.

ประยุกต์ใช้ Canvas แบบง่ายๆ

ตัวอย่างผมจะลองสร้าง ลูกบอล จากนั้นก็ให้มันเด้ง อยู่ภายใน canvas เริ่มแรกผมสร้างลูกบอลขึ้นมา โดยกำหนด x, y, radius, speedX, speedY ต่างๆ เพื่อกำหนดตำแหน่งลูกบอล รัศมี ความเร็วบอลในแกน x,y ดังนี้

var ball = {
    x: 20,
    y: 200,
    radius: 20,
    speedX: 2,
    speedY: 4
};

จากนั้นก็ใช้ฟังค์ชั่น context.arc() เพื่อวาดวงกลมลงไป แต่มันจะไม่ขยับ ก็เลยต้องใช้ setInterval() เพื่อให้มัน refresh ทุกๆ 1000/60 millisec (60 FPS)


setInterval(functino() {
   update();
   render();
}, 1000 / 60);

จากนั้นผมสร้างฟังค์ชัน update() เพื่อเอาไว้วาดวงกลมทุกๆครั้งที่ถูกรีเฟรซ จุดประสงค์คือทุกครั้งที่รีเฟรซก็จะทำการขยับตำแหน่ง ball จะได้ดังนี้

ball.x += ball.speedX;
ball.y += ball.speedY;

เมื่อลองรันดูจะพบผลลัพธ์ดังนี้ (ลูกบอลทั้งหมดไม่ถูกลบ แต่จะซ้อนทับกันไปเรื่อยๆ)

See the Pen MYXGBj by Chai Phonbopit (@Phonbopit) on CodePen.

ฉะนั้นก็เลยต้องทำการเคลียร์ canvas ก่อนทุกครั้ง

canvas.width = canvas.width; // reset canvas
ball.x += ball.speedX;
ball.y += ball.speedY;

Note: Clear Canvas ง่ายๆด้วย canvas.width = canvas.width; ซึ่งจริงๆจะใช้ clearRect(0, 0, canvas.width, canvas.height) ก็ได้

ต่อมา render() ผมเอาไว้แสดงผลลูกบอล (update() เอาไว้เปลี่ยนแปลง x,y เฉยๆ ไม่ได้วาด)

function render() {
  context.fillStyle = '#2634dd';
  context.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
  context.fill();
}

สุดท้ายกำหนดเงื่อนไข ถ้าลูกบอลเด้งไปโดนขอบ แต่ละด้าน ให้มันเด้งกลับ (ใช้ speed *= -1)

ดังนี้

if (ball.x > canvas.width - ball.radius || ball.x < ball.radius) {
    ball.speedX *= -1;
}

if (ball.y > canvas.height - ball.radius || ball.y < ball.radius ) {
    ball.speedY *= -1;
}

ball.x += ball.speedX;
ball.y += ball.speedY;

สุดท้ายโค๊ดทั้งหมด และตัวอย่างเดโม่

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');

var ball = {
    x: 20,
    y: 200,
    radius: 20,
    speedX: 2,
    speedY: 4
};

start();

function start() {
    setInterval(function() {
            update();
        render();
    }, 1000 / 60);
}

function update() {
  canvas.width = canvas.width; // reset canvas

  if (ball.x > canvas.width - ball.radius || ball.x < ball.radius) {
    ball.speedX *= -1;
  }

  if (ball.y > canvas.height - ball.radius || ball.y < ball.radius ) {
    ball.speedY *= -1;
  }

  ball.x += ball.speedX;
  ball.y += ball.speedY;
}

function render() {
  context.fillStyle = '#2634dd';
  context.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
  context.fill();
}

See the Pen RNJyJq by Chai Phonbopit (@Phonbopit) on CodePen.

สรุป

บทความนี้ก็เป็นการนำเสนอ HTML5 Canvas แบบง่ายๆสำหรับผู้เริ่มต้น ในแบบฉบับของผมนะครับ ซึ่งผมก็เพิ่งจะนั่งศึกษา นั่งอ่าน ตามเว็บและบล็อคดังที่แนบไว้ใน References หากผิดพลาดไปบ้าง ก็ขออภัยด้วยครับ

สุดท้ายรายชื่อด้านล่างก็เป็นรายการที่ผมลิสท์ไว้ ดูแล้วน่าสนใจ ว่าจะเอาไว้อ่านต่อ และ Reference ทั้งๆหมดที่ใช้อ้างอิง หากใครสนใจอ่านเพิ่มเติมก็สามารถเข้าไปดูได้ครับ

References

Chai Chai Phonbopit : MEAN Stack @Nextzy • ผู้ชายธรรมดาๆ ที่ชื่นชอบ Android, JavaScript (Node.js) และ Open Source มีงานอดิเรกเป็น Acoustic Guitar และ Football

บทความล่าสุด