"เป็นคุณเสมอสำหรับ Canvas2D"

Aaron Krajeski
Aaron Krajeski

Canvas2D อาจไม่ทำให้คุณตื่นเต้นในโลกของเชดเดอร์ ตาข่าย และเอฟเฟกต์ แต่จริงๆ ก็ดีนะ หน้าเว็บ 30–40% มีองค์ประกอบ <canvas> และ 98% ของ Canvas ทั้งหมดใช้บริบทการแสดงผล Canvas2D Canvas2D อยู่ในรถยนต์ ตู้เย็นและอวกาศ (จริง ๆ นะ)

เราต้องยอมรับว่า API นี้ล้าสมัยไปหน่อยเมื่อเทียบกับการวาดภาพ 2 มิติที่ทันสมัย แต่โชคดีที่เราได้ทํางานอย่างหนักเพื่อติดตั้งใช้งานฟีเจอร์ใหม่ใน Canvas2D เพื่อตามทัน CSS, ปรับปรุงการทํางานให้มีประสิทธิภาพยิ่งขึ้น และลดความซับซ้อน

ส่วนที่ 1: ทำความรู้จักกับ CSS

CSS มีคำสั่งวาดภาพ 2-3 รายการที่ Canvas2D ขาดไป เราได้เพิ่มฟีเจอร์ที่ผู้ใช้ต้องการมากที่สุด 2-3 รายการใน API ใหม่นี้

สี่เหลี่ยมผืนผ้ามุมมน

สี่เหลี่ยมผืนผ้ามุมมน: รากฐานของอินเทอร์เน็ต คอมพิวเตอร์ และอารยธรรม

สี่เหลี่ยมผืนผ้าที่โค้งมนมีประโยชน์อย่างมาก ไม่ว่าจะเป็นปุ่ม ฟองแชท ภาพปก ฟองข้อความ ฯลฯ คุณสร้างสี่เหลี่ยมผืนผ้าที่โค้งมนใน Canvas2D ได้ทุกเมื่อ แต่วิธีการอาจดูยุ่งยากไปหน่อย

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'magenta';

const top = 10;
const left = 10;
const width = 200;
const height = 100;
const radius = 20;

ctx.beginPath();
ctx.moveTo(left + radius, top);
ctx.lineTo(left + width - radius, top);
ctx.arcTo(left + width, top, left + width, top + radius, radius);
ctx.lineTo(left + width, top + height - radius);
ctx.arcTo(left + width, top + height, left + width - radius, top + height, radius);
ctx.lineTo(left + radius, top + height);
ctx.arcTo(left, top + height, left, top + height - radius, radius);
ctx.lineTo(left, top + radius);
ctx.arcTo(left, top, left + radius, top, radius);
ctx.stroke();

ทั้งหมดนี้จำเป็นสำหรับการทำสี่เหลี่ยมผืนผ้ามุมมนและเรียบง่าย:

สี่เหลี่ยมผืนผ้ามุมมน

API ใหม่จะมีเมธอด roundRect()

ctx.roundRect(upper, left, width, height, borderRadius);

ดังนั้น บรรทัดด้านบนจึงแทนที่ได้ด้วยบรรทัดต่อไปนี้

ctx.roundRect(10, 10, 200, 100, 20);

นอกจากนี้ เมธอด ctx.roundRect() ยังรับอาร์เรย์สำหรับอาร์กิวเมนต์ borderRadius ที่เป็นตัวเลขได้สูงสุด 4 รายการ รัศมีเหล่านี้จะควบคุมมุมทั้ง 4 มุมของสี่เหลี่ยมผืนผ้าที่โค้งมนในลักษณะเดียวกับCSS เช่น

ctx.roundRect(10, 10, 200, 100, [15, 50, 30]);

ลองใช้เดโมเพื่อดูตัวอย่าง

การไล่ระดับสี

คุณเคยเห็นการไล่ระดับสีแบบเส้นตรงแล้ว

const gradient = ctx.createLinearGradient(0, 0, 200, 100);
gradient.addColorStop(0, 'blue');
gradient.addColorStop(0.5, 'magenta');
gradient.addColorStop(1, 'white');
ctx.fillStyle = gradient;
ctx.fillRect(10, 10, 200, 100);

การไล่ระดับสีแบบเส้นตรง

การไล่ระดับสีแบบรัศมี

const radialGradient = ctx.createRadialGradient(150, 75, 10, 150, 75, 70);
radialGradient.addColorStop(0, 'white');
radialGradient.addColorStop(0.5, 'magenta');
radialGradient.addColorStop(1, 'lightblue');

ctx.fillStyle = radialGradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);

การไล่ระดับสีแบบรัศมี

แต่ลองใช้การไล่ระดับสีรูปกรวยดูสิ

const grad = ctx.createConicGradient(0, 100, 100);

grad.addColorStop(0, 'red');
grad.addColorStop(0.25, 'orange');
grad.addColorStop(0.5, 'yellow');
grad.addColorStop(0.75, 'green');
grad.addColorStop(1, 'blue');

ctx.fillStyle = grad;
ctx.fillRect(0, 0, 200, 200);

การไล่ระดับสีรูปกรวย

ตัวปรับแต่งข้อความ

ความสามารถในการแสดงผลข้อความของ Canvas2D นั้นล้าหลังมาก Chrome ได้เพิ่มแอตทริบิวต์ใหม่หลายรายการในการเรนเดอร์ข้อความ Canvas2D ดังนี้

แอตทริบิวต์เหล่านี้ทั้งหมดตรงกับแอตทริบิวต์ CSS ที่มีชื่อเดียวกัน

ส่วนที่ 2: การปรับให้เหมาะกับสรีระ

ก่อนหน้านี้ การใช้ Canvas2D บางอย่างนั้นทำได้ แต่การนำไปใช้งานซับซ้อนโดยไม่จำเป็น ต่อไปนี้คือการปรับปรุงคุณภาพชีวิตบางส่วนสำหรับนักพัฒนา JavaScript ที่ต้องการใช้ Canvas2D

รีเซ็ตบริบท

ในการอธิบายการล้างภาพพิมพ์แคนวาส เราได้เขียนฟังก์ชันเล็กๆ น้อยๆ ขึ้นมาเพื่อวาดลายย้อนยุค

draw90sPattern();

รูปแบบย้อนยุคของสามเหลี่ยมและสี่เหลี่ยมจัตุรัส

เยี่ยม! เมื่อวาดลายนั้นเสร็จแล้ว ฉันต้องการล้างภาพพิมพ์แคนวาสและวาดอย่างอื่น เดี๋ยวนะ เราจะล้างแคนวาสอีกครั้งได้อย่างไร ใช่ ctx.clearRect() ได้เลย

ctx.clearRect(0, 0, canvas.width, canvas.height);

อ๊ะ… ไม่สำเร็จ ใช่ เราต้องรีเซ็ตการเปลี่ยนรูปแบบก่อน

ctx.resetTransform();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ผืนผ้าใบเปล่า

งั้นก็แจ๋วเลย ผืนผ้าใบเปล่าที่ยอดเยี่ยม มาเริ่มวาดเส้นแนวนอนสวยๆ กัน

ctx.moveTo(10, 10);
ctx.lineTo(canvas.width, 10);
ctx.stroke();

เส้นแนวนอนและเส้นเฉียง

อ๊าาาา ยังไม่ถูกต้อง 😡 บรรทัดพิเศษนั้นอยู่ที่นี่ทำไม และทำไมถึงเป็นสีชมพู โอเค มาตรวจสอบใน StackOverflow กัน

canvas.width = canvas.width;

Why is this so silly? ทำไมการดำเนินการนี้จึงยาก

แต่ตอนนี้ไม่ใช่แล้ว API ใหม่นี้ช่วยให้เราทําสิ่งต่อไปนี้ได้อย่างง่ายดาย สง่างาม และสวยงาม

ctx.reset();

ขออภัยที่ใช้เวลานาน

ตัวกรอง

ฟิลเตอร์ SVG เป็นโลกใบหนึ่ง หากเพิ่งเคยได้ยินเกี่ยวกับฟิลเตอร์เหล่านี้ เราขอแนะนําอย่างยิ่งให้อ่านบทความศิลปะของฟิลเตอร์ SVG และเหตุผลที่ยอดเยี่ยม ซึ่งจะแสดงให้เห็นถึงศักยภาพอันน่าทึ่งของฟิลเตอร์เหล่านี้

ฟิลเตอร์สไตล์ SVG พร้อมใช้งานสำหรับ Canvas2D แล้ว คุณเพียงแค่ต้องส่งฟิลเตอร์เป็น URL ที่ชี้ไปยังองค์ประกอบฟิลเตอร์ SVG อื่นในหน้าเว็บ

<svg>
  <defs>
    <filter id="svgFilter">
      <feGaussianBlur in="SourceGraphic" stdDeviation="5" />
      <feConvolveMatrix kernelMatrix="-3 0 0 0 0.5 0 0 0 3" />
      <feColorMatrix type="hueRotate" values="90" />
    </filter>
  </defs>
</svg>
const canvas = document.createElement('canvas');
canvas.width = 500;
canvas.height = 400;
const ctx = canvas.getContext('2d');
document.body.appendChild(canvas);

ctx.filter = "url('#svgFilter')";
draw90sPattern(ctx);

ซึ่งทำให้รูปแบบของเราสับสนมาก

รูปแบบย้อนยุคที่ใช้เอฟเฟกต์เบลอ

แต่หากคุณต้องการทำข้างต้น แต่ให้อยู่ภายใน JavaScript และไม่ยุ่งกับสตริง ซึ่ง API ใหม่นี้ช่วยให้คุณทําสิ่งนั้นได้

ctx.filter = new CanvasFilter([
  { filter: 'gaussianBlur', stdDeviation: 5 },
  {
    filter: 'convolveMatrix',
    kernelMatrix: [
      [-3, 0, 0],
      [0, 0.5, 0],
      [0, 0, 3],
    ],
  },
  { filter: 'colorMatrix', type: 'hueRotate', values: 90 },
]);

ง่ายนิดเดียว ลองใช้และปรับพารามิเตอร์ในเดโมที่นี่

ส่วนที่ 3: การปรับปรุงประสิทธิภาพ

เมื่อใช้ Canvas2D API เวอร์ชันใหม่ เราต้องการปรับปรุงประสิทธิภาพด้วยหากเป็นไปได้ เราได้เพิ่มฟีเจอร์ 2-3 อย่างเพื่อให้นักพัฒนาซอฟต์แวร์ควบคุมเว็บไซต์ของตนได้อย่างละเอียดมากขึ้น และรองรับอัตราเฟรมให้มากที่สุดเท่าที่จะทำได้

จะอ่านบ่อยครั้ง

ใช้ getImageData() เพื่ออ่านข้อมูลพิกเซลจากภาพพิมพ์แคนวาส ซึ่งอาจช้ามาก API ใหม่นี้ช่วยให้คุณทำเครื่องหมายผืนผ้าแคนวาสสำหรับการอ่านกลับได้อย่างชัดเจน (เช่น สำหรับเอฟเฟกต์ Generative) ซึ่งจะช่วยให้คุณเพิ่มประสิทธิภาพในส่วนที่เป็นหัวใจสำคัญและทำให้ Canvas ทำงานได้อย่างรวดเร็วสำหรับกรณีการใช้งานที่หลากหลายมากขึ้น ฟีเจอร์นี้มีอยู่ใน Firefox มาระยะหนึ่งแล้ว และในที่สุดเราก็ได้ทำให้ฟีเจอร์นี้เป็นส่วนหนึ่งของข้อกำหนด Canvas แล้ว

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });

สูญเสียบริบท

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

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');

canvas.addEventListener('contextlost', onContextLost);
canvas.addEventListener('contextrestored', redraw);

หากต้องการอ่านเพิ่มเติมเกี่ยวกับบริบทและข้อมูลสูญหายของ Canvas ทาง WHATWG มีคำอธิบายที่ดีในวิกิ

บทสรุป

ไม่ว่าคุณจะเพิ่งเริ่มใช้ Canvas2D หรือใช้มาหลายปีแล้ว หรือหลีกเลี่ยงที่จะใช้มาหลายปีแล้ว เราขอแนะนำให้ลองใช้ Canvas อีกครั้ง นั่นคือ API ข้างๆ ที่ใช้มาตลอด