IlmHamroh
JavaScript Full-stack/13-qism. Brauzer API'lari, performans va xavfsizlik15/32-dars28 daqiqa
Mundarija (43)

JavaScript Canvas 2D: <canvas> ga shakl, matn va rasm chizish

Qisqacha: <canvas> — JavaScript piksellar bilan chizadigan bo'sh mato. Chizish uchun undan getContext("2d") bilan kontekst olinadi: fillRect to'rtburchak, beginPath → moveTo → lineTo → stroke chiziq, arc doira, fillText matn, drawImage rasm chizadi. Chizilgan narsa obyekt bo'lib qolmaydi — faqat piksel; o'zgartirish uchun qayta chizasiz. Telefonda tiniq bo'lishi uchun mato o'lchami devicePixelRatio ga ko'paytiriladi.

Bu darsda

  • <canvas> dan 2D kontekst olib, to'rtburchak, chiziq, uchburchak va doira chiza olasiz.
  • Rang, gradient, chiziq qalinligi va shaffoflikni sozlaysiz.
  • Matn yozib, uni markazlab, enini o'lchay olasiz.
  • Rasmni (drawImage) yuklanib bo'lgach chizasiz.
  • Grafik telefonda xira chiqmasligi uchun devicePixelRatio bilan matoni sozlaysiz.

Oldin bilishingiz kerak: canvas, image map va boshqa joylash teglari, requestAnimationFrame, Element o'lchamlari va scroll, ResizeObserver va MutationObserver.

1. Nega bu kerak?

Jasur aka har dushanba bitta savol beradi: "O'tgan hafta qaysi kuni ko'p buyurtma bo'ldi?" Sardor raqamlarni jadvalga chiqardi. Jasur aka jadvalga qaradi-yu: "Rasm qilib ko'rsat — bir qarashda tushunay", dedi.

Ustunli grafikni <div> lar bilan ham yasash mumkin. Lekin grafik murakkablashsa — o'qlar, chiziqlar, egri chiziq, minglab nuqta — DOM og'irlashadi. Har nuqta alohida element bo'lib, brauzer ularning har birini kuzatib boradi.

HTML qismida bunga mos tegni ko'rgan edik — canvas. Unda shunday degan edik: "Ichidagi rasmni JavaScript chizadi". Endi o'sha JavaScript'ni yozamiz.

Bir o'xshatish. DOM — muzlatgichdagi magnitlarga o'xshaydi: har birini keyin surish, olib tashlash mumkin. Canvas esa devorga bo'yoq bilan chizilgan rasm. Chizdingiz — bo'yoq qoldi. Chizilgan mushukni "surib" bo'lmaydi: o'chirib, yangi joyga qayta chizasiz. Bu kamchilikdek tuyuladi, lekin aynan shu tezlik beradi — brauzer minglab obyektni eslab qolmaydi, faqat piksellarni.

2. Birinchi chizma

2.1 Mato va kontekst

<canvas> tegi — faqat mato. Unga to'g'ridan-to'g'ri chizilmaydi. Chizish uchun undan kontekst (context) so'raladi — chizish asboblari to'plami:

js
const canvas = document.getElementById("board");
const ctx = canvas.getContext("2d");
  • getContext("2d") — "menga ikki o'lchamli chizish asboblarini ber". Qaytgan obyekt (CanvasRenderingContext2D) — hamma chizish metodlari shunda.
  • ctx — dasturchilar shu nomni ishlatishadi, "context" ning qisqartmasi. Kursda ham shunday yozamiz.
  • Boshqa kontekst turlari ham bor: "webgl", "webgpu" — 3D grafika uchun. Ular kursdan tashqarida.

Kontekst bir marta olinadi va saqlab qo'yiladi. Ikkinchi marta getContext("2d") chaqirsangiz, o'sha obyektning o'zi qaytadi.

2.2 Koordinatalar

Mato — piksellar to'ri. Har nuqtaning manzili — ikki son: x (chapdan necha piksel) va y (tepadan necha piksel):

text
(0, 0) ─────────────────► x
  │
  │        (120, 40)
  │           •
  │
  ▼
  y

Maktabdagi matematikadan farqi bor: u yerda y yuqoriga o'sadi, bu yerda pastga. (0, 0) — chap yuqori burchak. Boshida g'alati tuyulishi tabiiy. Ekranda matn ham yuqoridan pastga o'qiladi, shuning uchun brauzerlarda y doim pastga qarab o'sadi — CSS'dagi top ham shunday.

2.3 To'rtburchaklar

Eng oddiy shakl — to'rtburchak. Uning uchun uchta tayyor metod bor:

html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  canvas { border: 1px solid #c8d3c8; }
</style>
<canvas id="board" width="300" height="150">
  Yashil to'rtburchak ichida oq teshik va qizil ramka.
</canvas>
<script>
  const canvas = document.getElementById("board");
  const ctx = canvas.getContext("2d");

  ctx.fillStyle = "#2e7d32";
  ctx.fillRect(20, 20, 120, 80); // x, y, eni, bo'yi

  ctx.strokeStyle = "#c0392b";
  ctx.lineWidth = 4;
  ctx.strokeRect(160, 20, 120, 80);

  ctx.clearRect(50, 45, 60, 30); // shu joyni o'chirish
  console.log(canvas.width, canvas.height);
</script>

Konsolda:

text
300 150

Qatorma-qator:

  • fillStyle — bo'yoq rangi. Avval rang tanlanadi, keyin chiziladi — xuddi cho'tkani bo'yoqqa botirgandek. Rang CSS'dagi har qanday yozuvda bo'ladi: "red", "#2e7d32", "rgb(46 125 50)".
  • fillRect(x, y, eni, bo'yi) — to'ldirilgan to'rtburchak.
  • strokeStyle va lineWidth — chiziq rangi va qalinligi. strokeRect faqat ramka chizadi, ichi bo'sh qoladi.
  • clearRect — berilgan sohani o'chiradi: piksellar shaffof bo'ladi. Yashil to'rtburchak o'rtasida "teshik" ochildi — orqadan sahifaning oq foni ko'rinadi.
  • canvas.width va canvas.height — matoning piksel o'lchami. Atributda yozilganini oldik: 300 va 150.

Diqqat qiling: rang "eslab qolinadi". fillStyle ni bir marta yashil qilsangiz, keyingi hamma fill... chaqiruvlar ham yashil bo'ladi — siz uni o'zgartirmaguningizcha. Kontekst — bitta cho'tka, unda doim bitta rang turadi.

Tekshirib ko'ring: ctx.fillRect(0, 0, canvas.width, canvas.height) nima qiladi? Uni rasm chizishdan oldin yoki keyin chaqirish farq qiladimi?

Javob

Butun matoni joriy fillStyle rangi bilan to'ldiradi — fon chizadi. Farq juda katta: canvas'da keyin chizilgan narsa avvalgisining ustiga tushadi. Fonni oxirida chizsangiz, u hamma narsani bo'yab yuboradi. Shuning uchun tartib doim: avval fon, keyin shakllar.

2.4 width atributi va CSS o'lchami — ikki xil narsa

Canvas'ning ikkita o'lchami bor va bu boshlovchilarni ko'p chalg'itadi:

O'lcham Qayerda beriladi Nimani belgilaydi
Mato o'lchami width/height atributlari Nechta piksel chiziladi
Ko'rinish o'lchami CSS width/height Sahifada qancha joy egallaydi

Atribut berilmasa, mato 300 × 150 piksel. CSS berilmasa, ko'rinish o'lchami mato o'lchamiga teng. Ikkalasi farq qilsa, brauzer tayyor rasmni cho'zadi — xuddi kichik rasmni katta qilib ko'rsatgandek. Natija: xira, cho'zilgan shakllar.

Masalan, <canvas width="100" height="50"> ga CSS bilan width: 300px bersangiz, har piksel uch baravar kattalashadi. Doira tuxumga aylanishi mumkin. Qoida: matoni atribut bilan (yoki JavaScript'da canvas.width = ...) o'lchang, CSS faqat joylashuv uchun. Bu qoidaning telefon uchun muhim davomi — "Telefonda tiniq chizish" bo'limida.

3. Yo'llar: chiziq, uchburchak va doira

3.1 Qalam bilan chizish

To'rtburchakdan boshqa hamma shakl yo'l (path) bilan chiziladi. Yo'l — qalamning qog'oz ustidagi harakati. Avval yo'l "o'ylab olinadi", keyin bir buyruq bilan bo'yaladi:

flowchart LR
  B["beginPath()<br/>yangi yo'l"] --> M["moveTo(x, y)<br/>qalamni qo'yish"]
  M --> L["lineTo / arc<br/>yo'lni chizish"]
  L --> C["closePath()<br/>boshiga ulash"]
  C --> F["fill() yoki stroke()<br/>bo'yash"]

Nimaga qarang: lineTo hali hech narsani ko'rsatmaydi. U faqat yo'lga bitta bo'lak qo'shadi. Ekranda chiziq stroke() yoki fill() chaqirilgandagina paydo bo'ladi.

  • beginPath() — eski yo'lni unutib, yangisini boshlash.
  • moveTo(x, y) — qalamni ko'tarib, shu nuqtaga qo'yish (chiziqsiz).
  • lineTo(x, y) — joriy nuqtadan shu nuqtagacha to'g'ri chiziq.
  • closePath() — oxirgi nuqtani birinchisiga ulash (uchburchakning uchinchi tomoni).
  • stroke() — yo'l chizig'ini chizish, fill() — ichini bo'yash.

3.2 Doira: arc

Doira va yoy uchun — arc(x, y, radius, boshlanish, tugash). x, y — markaz. Burchaklar radian (radian) da beriladi — gradusda emas. Radian — burchakni o'lchashning matematik birligi. Bilish kerak bo'lgani faqat shu: to'liq aylana 360° = 2 * Math.PI radian.

Gradus Radian Kodda
90° π/2 Math.PI / 2
180° π Math.PI
360° 2π Math.PI * 2

Gradusdan radianga o'tkazish formulasi: gradus * Math.PI / 180. Burchak 0 — o'ng tomon (soat 3), keyin soat mili yo'nalishida o'sadi (yana y pastga qaragani uchun).

Bo'sh joyni to'ldiring: 45° — bu 180° ning to'rtdan biri, ya'ni kodda Math.PI /

Endi «Bahor» uchun kichik belgi chizamiz: likopcha va undan ko'tarilayotgan bug':

html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  canvas { border: 1px solid #c8d3c8; }
</style>
<canvas id="logo" width="300" height="160">
  «Bahor» belgisi: sariq bayroqcha, osh solingan likopcha va bug'.
</canvas>
<script>
  const ctx = document.getElementById("logo").getContext("2d");

  // 1) Bayroqcha — uchburchak
  ctx.beginPath();
  ctx.moveTo(20, 140);
  ctx.lineTo(70, 40);
  ctx.lineTo(120, 140);
  ctx.closePath();
  ctx.fillStyle = "#f2c94c";
  ctx.fill();
  ctx.lineWidth = 3;
  ctx.strokeStyle = "#8a6d1d";
  ctx.stroke();

  // 2) Likopcha — to'liq doira
  ctx.beginPath();
  ctx.arc(210, 100, 50, 0, Math.PI * 2);
  ctx.fillStyle = "#ffffff";
  ctx.fill();
  ctx.lineWidth = 6;
  ctx.strokeStyle = "#2e7d32";
  ctx.stroke();

  // 3) Osh — yarim doira (pastki yarmi)
  ctx.beginPath();
  ctx.arc(210, 100, 32, 0, Math.PI);
  ctx.fillStyle = "#e0a458";
  ctx.fill();

  // 4) Bug' — uchta qisqa chiziq
  ctx.beginPath();
  for (const x of [195, 210, 225]) {
    ctx.moveTo(x, 40);
    ctx.lineTo(x, 20);
  }
  ctx.lineWidth = 3;
  ctx.lineCap = "round";
  ctx.strokeStyle = "#90a4ae";
  ctx.stroke();
</script>

Bir nechta narsaga e'tibor bering:

  • Har shakl beginPath() bilan boshlanadi. Nega — keyingi bo'limda.
  • arc(..., 0, Math.PI) — 0 dan 180° gacha. Burchak soat mili bo'yicha o'sgani uchun yoy o'ng tomondan pastdan aylanib chapga boradi — kosaning pastki yarmi chiqdi.
  • Bug'ning uchta chizig'i bitta yo'lda: har biri moveTo bilan boshlanadi (qalam ko'tariladi), oxirida bitta stroke(). Bu bir nechta stroke() dan tezroq.
  • lineCap = "round" — chiziq uchlari yumaloq. Boshqa qiymatlari: "butt" (standart, tekis) va "square". Chiziqlar birlashgan burchak shakli esa lineJoin bilan beriladi.

To'rtburchakni ham yo'l sifatida chizish mumkin: ctx.rect(x, y, eni, bo'yi). Burchaklari yumaloq to'rtburchak uchun — ctx.roundRect(x, y, eni, bo'yi, radius). Ikkalasi fillRect dan farqli ravishda faqat yo'lga qo'shadi — keyin fill() yoki stroke() kerak.

Tekshirib ko'ring: Yuqoridagi arc(210, 100, 32, 0, Math.PI) o'rniga arc(210, 100, 32, Math.PI, 0) yozilsa, osh qayerda chiziladi?

Javob

Doiraning yuqori yarmida. Endi yoy 180° dan (chap tomondan) boshlanib, soat mili bo'yicha 360° gacha — ya'ni tepadan aylanib o'ngga boradi. Yo'nalishni teskari qilish uchun arc ning oltinchi argumenti bor: true — soat miliga qarshi.

3.3 Tuzoq: beginPath ni unutish

beginPath() yangi yo'l boshlaydi. Uni unutsangiz, eski yo'l o'chmaydi — yangi chiziqlar unga qo'shiladi. Keyingi stroke() esa butun yo'lni, eskisini ham, qaytadan chizadi. Sinab ko'ring:

html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  canvas { border: 1px solid #c8d3c8; }
</style>
<canvas id="lines" width="300" height="120">
  Ikki chiziq: biri qizil bo'lishi kerak edi, lekin ikkalasi ko'k.
</canvas>
<script>
  const ctx = document.getElementById("lines").getContext("2d");
  ctx.lineWidth = 8;

  ctx.beginPath();
  ctx.moveTo(20, 30);
  ctx.lineTo(280, 30);
  ctx.strokeStyle = "red";
  ctx.stroke(); // birinchi chiziq — qizil

  // ❌ beginPath() yo'q
  ctx.moveTo(20, 90);
  ctx.lineTo(280, 90);
  ctx.strokeStyle = "blue";
  ctx.stroke(); // ikkala chiziq ham ko'k bo'ladi!
</script>

Ikkala chiziq ham ko'k. Ikkinchi stroke() yo'ldagi hamma bo'laklarni ko'k rang bilan qayta chizdi — qizil chiziq ustidan. Biz tekshirdik: yuqori chiziq o'rtasidagi piksel — sof ko'k (0, 0, 255). Ikkinchi moveTo oldiga ctx.beginPath() qo'shsangiz, yuqori chiziq qizil qoladi.

Bu xato animatsiyada yanada yomon: har kadrda yo'l uzayib boradi va chizish asta-sekin sekinlashadi. Qoida oddiy: har yangi shakl — beginPath() bilan.

4. Rang, gradient va shaffoflik

4.1 Gradient

fillStyle ga faqat rang emas, gradient (gradient — bir rangdan boshqasiga silliq o'tish) ham berish mumkin. CSS'dagi linear-gradient ning canvas'dagi ko'rinishi:

js
const gradient = ctx.createLinearGradient(0, 0, 320, 0);
gradient.addColorStop(0, "#2e7d32");   // boshida — to'q yashil
gradient.addColorStop(1, "#a5d6a7");   // oxirida — och yashil
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 320, 120);
  • createLinearGradient(x0, y0, x1, y1) — gradient chizig'i: qayerdan qayerga. (0, 0) dan (320, 0) gacha — chapdan o'ngga.
  • addColorStop(joy, rang) — joy 0 dan 1 gacha: 0 — boshi, 1 — oxiri, 0.5 — o'rtasi. Nechta to'xtash qo'shsangiz, shuncha rang.
  • Doiraviy gradient — createRadialGradient(x0, y0, r0, x1, y1, r1): ikki aylana orasida. Masalan, chiroq yorug'ligi.

Muhim nuqta: gradient koordinatalari matoga bog'langan, shaklga emas. To'rtburchakni x = 500 ga chizsangiz, lekin gradient 0–320 oralig'ida bo'lsa, to'rtburchak butunlay oxirgi rangda chiqadi.

4.2 Shaffoflik va soya

  • ctx.globalAlpha = 0.5 — keyingi hamma chizmalar yarim shaffof. 0 — ko'rinmas, 1 — to'liq.
  • Rangning o'zida ham shaffoflik berish mumkin: "rgb(46 125 50 / 0.3)". Bu faqat shu rangga ta'sir qiladi.
  • Soya: shadowColor, shadowBlur, shadowOffsetX/Y. Chiroyli, lekin har chizishda qimmat — ko'p shaklda ishlatmang.

Tekshirib ko'ring: Sardor ctx.globalAlpha = 0.5 yozib, bitta doira chizdi. Keyin yana ikki to'rtburchak chizdi — ular ham xira chiqdi. Nega?

Javob

globalAlpha ham fillStyle kabi kontekstda eslab qolinadi. Uni o'zgartirmaguncha keyingi hamma chizmalar yarim shaffof bo'ladi. Tuzatish: doiradan keyin ctx.globalAlpha = 1 ga qaytarish. Yoki keyingi bo'limdagi save/restore — u bunday sozlamalarni avtomatik qaytaradi.

5. Matn yozish

5.1 fillText va shrift

Canvas'da matn ham chiziladi — piksel bo'lib. Asosiy metod: fillText(matn, x, y). Shrift CSS'dagi font qisqa yozuvi bilan beriladi: qalinlik, o'lcham, oila.

«Bahor» oynasi uchun "Bugungi taom" banneri:

html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
</style>
<canvas id="banner" width="320" height="120">
  Bugungi taom: osh, 35 000 so'm.
</canvas>
<script>
  const ctx = document.getElementById("banner").getContext("2d");

  const gradient = ctx.createLinearGradient(0, 0, 320, 0);
  gradient.addColorStop(0, "#2e7d32");
  gradient.addColorStop(1, "#81c784");
  ctx.fillStyle = gradient;
  ctx.fillRect(0, 0, 320, 120);

  ctx.fillStyle = "#ffffff";
  ctx.textAlign = "center";
  ctx.textBaseline = "middle";

  ctx.font = "700 24px system-ui, sans-serif";
  ctx.fillText("Bugungi taom: osh", 160, 42);

  ctx.font = "20px system-ui, sans-serif";
  const price = "35 000 so'm";
  ctx.fillText(price, 160, 80);

  // narx tagiga chiziq — matn eniga teng
  const width = ctx.measureText(price).width;
  ctx.fillRect(160 - width / 2, 94, width, 2);
</script>

Qatorma-qator:

  • textAlign = "center" — x nuqta matnning o'rtasi bo'ladi (standart — chap cheti). Shuning uchun x = 160 — matoning qoq o'rtasi.
  • textBaseline = "middle" — y matnning vertikal o'rtasi. Standartda y — harflar "turadigan" chiziq ("alphabetic"), bu boshlovchini ko'p adashtiradi: fillText("Salom", 0, 0) ekranda ko'rinmaydi, chunki harflar 0 chizig'idan yuqoriga — mato tashqarisiga chiziladi.
  • measureText(matn).width — matn eni pikselda, joriy shrift bo'yicha. Biz uni narx tagiga aynan matn enidagi chiziq chizish uchun ishlatdik.

measureText natijasi kompyuterga bog'liq: system-ui Windows'da bitta shrift, telefonda boshqasi. Shuning uchun aniq sonni oldindan yozib bo'lmaydi — doim o'lchab ishlating.

5.2 Matn sig'masa

fillText ning to'rtinchi argumenti — maxWidth. Matn undan keng bo'lsa, brauzer uni siqadi (harflar torayadi). Bu qisqa yorliqlar uchun qulay. Uzun matnni esa canvas qatorlarga bo'lmaydi — \n ham ishlamaydi. Bir necha qatorli matnni so'zma-so'z measureText bilan o'zingiz bo'lasiz. Bu canvas'ning DOM'dan zaif tomoni: matn ko'p bo'lsa, oddiy HTML yaxshiroq.

Tekshirib ko'ring: ctx.fillText("Osh", 10, 0) chaqirildi, ekranda hech narsa yo'q. textBaseline standart holatda. Nega?

Javob

Standart textBaseline = "alphabetic" da y — harflarning pastki chizig'i. y = 0 bo'lsa, harflar 0 dan yuqoriga, ya'ni mato tashqarisiga chiziladi. Tuzatish: y ni shrift balandligicha pastga suring (masalan, 24) yoki ctx.textBaseline = "top" qiling.

6. Kontekst holati: save va restore

Siz fillStyle, lineWidth, font kabi sozlamalarni o'zgartirib borasiz. Bitta yordamchi funksiya rangni qizil qilsa, undan keyingi hamma kod ham qizil chizadi — chunki cho'tka bitta. Bunga qarshi ikkita metod bor:

  • ctx.save() — joriy sozlamalarni "suratga olib" stekka qo'yadi (likopchalar taxlami kabi: oxirgi qo'yilgani birinchi olinadi).
  • ctx.restore() — oxirgi saqlangan holatga qaytaradi.
js
function drawBadge(ctx, x, y) {
  ctx.save();                 // eski holat saqlandi
  ctx.fillStyle = "#c0392b";
  ctx.font = "700 14px system-ui, sans-serif";
  ctx.fillText("YANGI", x, y);
  ctx.restore();              // rang va shrift qaytdi
}

Shu ikki metod bilan o'zgartirishlar (transformations) ham ishlatiladi: translate(x, y) — koordinata boshini surish, rotate(burchak) — aylantirish, scale(sx, sy) — kattalashtirish. Masalan, grafik ostidagi yorliqni qiya yozish uchun: save() → translate → rotate(-Math.PI / 4) → fillText → restore(). scale ni esa tez orada — tiniqlik bo'limida ishlatamiz.

7. Rasm chizish: drawImage

7.1 Rasm yuklanib bo'lishini kutish

Canvas'ga tayyor rasm ham chizish mumkin: <img>, boshqa <canvas>, hatto <video> kadri. Metod — drawImage. Lekin bitta shart bor: rasm yuklanib bo'lgan bo'lishi kerak. Yuklanmagan rasmni chizsangiz, xato chiqmaydi — shunchaki hech narsa chizilmaydi.

Shuning uchun rasm chizish doim load hodisasi ichida (yoki img.decode() Promise'idan keyin) bo'ladi. Natija oynasida tashqi fayl yo'q, shuning uchun rasmni data: URL bilan beramiz — bu SVG rasm satr ichida:

html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  canvas { border: 1px solid #c8d3c8; }
</style>
<canvas id="photos" width="320" height="140">
  Choynak rasmi uch xil o'lchamda.
</canvas>
<script>
  const ctx = document.getElementById("photos").getContext("2d");
  const svg = `<svg xmlns="http://www.w3.org/2000/svg"
    width="80" height="80" viewBox="0 0 80 80">
    <ellipse cx="40" cy="50" rx="28" ry="22" fill="#2e7d32"/>
    <rect x="32" y="20" width="16" height="10" fill="#1b5e20"/>
    <path d="M66 46 q12 -4 10 -16" stroke="#2e7d32"
      stroke-width="5" fill="none"/>
  </svg>`;

  const img = new Image();
  img.addEventListener("load", () => {
    ctx.drawImage(img, 10, 30);            // asl o'lchamda
    ctx.drawImage(img, 110, 50, 40, 40);   // 40 × 40 ga kichik
    ctx.drawImage(img, 180, 10, 120, 120); // 120 × 120 ga katta
    console.log(`Rasm o'lchami: ${img.width} × ${img.height}`);
  });
  img.src = "data:image/svg+xml," + encodeURIComponent(svg);
</script>

Konsolda:

text
Rasm o'lchami: 80 × 80
  • new Image() — <img> elementini JavaScript'da yaratish (sahifaga qo'shilmaydi).
  • Tinglovchi src dan oldin qo'shildi. Aks holda keshdan tez yuklangan rasmning load hodisasi tinglovchidan oldin o'tib ketishi mumkin.
  • drawImage(img, x, y) — asl o'lchamda; drawImage(img, x, y, eni, bo'yi) — berilgan o'lchamga cho'zib yoki kichraytirib.
  • SVG — vektor rasm, shuning uchun 120 × 120 da ham tiniq chiqdi. Oddiy PNG yoki JPG rasmni kattalashtirsangiz, u xiralashadi.

drawImage ning yana bir — 9 argumentli shakli bor: rasmning faqat bir bo'lagini kesib chizadi. U rasmni qirqish (crop) uchun kerak, keyingi darsda ishlatamiz.

7.2 Boshqa saytdagi rasm

Rasm boshqa domendan (masalan, https://example.com/osh.jpg) kelsa, uni chizish mumkin. Lekin shundan keyin canvas "ifloslangan" (tainted) hisoblanadi: undan piksel o'qish yoki rasm qilib eksport qilish taqiqlanadi. Bu xavfsizlik qoidasi — begona saytdagi shaxsiy rasmlarni o'g'irlashning oldini oladi. Batafsil — Canvas amaliyotda darsida, eksport bilan birga.

8. Telefonda tiniq chizish: devicePixelRatio

8.1 Nega telefonda xira?

Bu darsdagi chizmalarni telefonda ochsangiz, matn va chiziqlar biroz xira ko'rinishi mumkin. Kompyuterda esa tiniq. Sababi — ekranlardagi piksel zichligi.

CSS'dagi 1px — har doim ham ekrandagi bitta nuqta emas. Zamonaviy telefonda bitta CSS piksel ichida 2 × 2 yoki 3 × 3 ta haqiqiy (jismoniy) nuqta bor. Bu nisbat window.devicePixelRatio da saqlanadi:

  • oddiy monitor — 1;
  • ko'p telefonlar — 2 yoki 3;
  • brauzerda masshtabni oshirsangiz (Ctrl + +), kompyuterda ham o'zgaradi: 125% da 1.25.

300 piksellik matoni telefon 300 CSS pikselga, ya'ni 900 jismoniy nuqtaga cho'zadi. Har piksel 3 × 3 bo'lakka "yoyiladi" — "Ko'rinish o'lchami" jadvalidagi cho'zilish aynan shu. HTML va CSS matni bunday xiralashmaydi, chunki brauzer uni har safar kerakli zichlikda chizadi. Canvas esa tayyor piksellar — ular cho'ziladi.

8.2 Yechim: matoni zichlikka ko'paytirish

Yechim uch qadam:

  1. Matoni (atribut) devicePixelRatio marta katta qilish: 300 × 3 = 900 piksel.
  2. CSS'da ko'rinish o'lchamini avvalgidek qoldirish: 300px.
  3. ctx.scale(dpr, dpr) — shunda kodda avvalgidek "300 lik" koordinatalar bilan chizasiz, brauzer esa ularni 3 ga ko'paytiradi.

Bu ishni bitta yordamchi funksiyaga yig'amiz — keyingi misollarda shuni ishlatamiz:

js
function setupCanvas(canvas, width, height) {
  const dpr = window.devicePixelRatio || 1;
  canvas.style.width = `${width}px`;
  canvas.style.height = `${height}px`;
  canvas.width = Math.round(width * dpr);
  canvas.height = Math.round(height * dpr);
  const ctx = canvas.getContext("2d");
  ctx.scale(dpr, dpr);
  return ctx;
}

Ikki oynani yonma-yon solishtiring. Chapdagisi oddiy, o'ngdagisi setupCanvas bilan:

html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
  .row { display: flex; gap: 1rem; flex-wrap: wrap; }
  canvas { border: 1px solid #c8d3c8; }
</style>
<div class="row">
  <canvas id="plain" width="150" height="60">Osh 35 000</canvas>
  <canvas id="sharp">Osh 35 000</canvas>
</div>
<p id="info"></p>
<script>
  function setupCanvas(canvas, width, height) {
    const dpr = window.devicePixelRatio || 1;
    canvas.style.width = `${width}px`;
    canvas.style.height = `${height}px`;
    canvas.width = Math.round(width * dpr);
    canvas.height = Math.round(height * dpr);
    const ctx = canvas.getContext("2d");
    ctx.scale(dpr, dpr);
    return ctx;
  }

  function drawLabel(ctx) {
    ctx.fillStyle = "#1b5e20";
    ctx.font = "700 18px system-ui, sans-serif";
    ctx.fillText("Osh 35 000", 12, 36);
    ctx.beginPath();
    ctx.arc(130, 30, 10, 0, Math.PI * 2);
    ctx.fill();
  }

  const plain = document.getElementById("plain");
  const sharp = document.getElementById("sharp");
  plain.style.width = "150px";
  drawLabel(plain.getContext("2d"));
  drawLabel(setupCanvas(sharp, 150, 60));

  document.getElementById("info").textContent =
    `devicePixelRatio: ${window.devicePixelRatio}; o'ngdagi mato: `
    + `${sharp.width} × ${sharp.height} piksel`;
</script>

Kompyuterda (devicePixelRatio: 1) ikkalasi bir xil — farq yo'q, chunki ko'paytiradigan narsa yo'q. Telefonda esa chapdagisining harflari "loyqa", o'ngdagisi tiniq. Kompyuterda farqni ko'rish uchun brauzer masshtabini 200% qiling va «Qayta ishga tushirish» ni bosing: o'ngdagi mato endi 300 × 120 piksel bo'ladi.

Bo'sh joyni to'ldiring: devicePixelRatio 2 bo'lgan telefonda setupCanvas(canvas, 150, 60) dan keyin canvas.width ning qiymati

Diqqat: canvas.width yoki canvas.height ga qiymat berish matoni tozalaydi va kontekst holatini (rang, shrift, scale) boshlang'ich holatga qaytaradi. Shuning uchun o'lchamni o'zgartirgach, scale ni qayta chaqirish va hammasini qayta chizish kerak. setupCanvas aynan shunday tartibda yozilgan.

8.3 Element o'lchami o'zgarsa

ResizeObserver darsida va'da qilgan edik: canvas o'zi "cho'zilmaydi". Konteyner kengaysa, mato eski piksellar bilan qoladi. To'g'ri yo'l — konteyner o'lchamini kuzatib, matoni yangi o'lchamga sozlash va grafikni qayta chizish:

js
const box = document.getElementById("chart-box");
const canvas = document.getElementById("chart");

const observer = new ResizeObserver(([entry]) => {
  const width = entry.contentRect.width;
  const ctx = setupCanvas(canvas, width, 200);
  drawChart(ctx, width, 200); // grafikni boshidan chizish
});
observer.observe(box);

Bu yerda kuzatuvchi konteynerni kuzatadi, canvas'ning o'zini emas. Callback canvas o'lchamini o'zgartiradi — canvas'ni kuzatsak, bu yangi callback'ni uyg'otib, ResizeObserver darsidagi sikl tuzog'iga tushardik.

9. Hammasi birga: «Bahor» haftalik grafigi

Endi Jasur akaning savoliga javob beramiz. Grafik o'tgan haftaning buyurtmalarini ko'rsatadi. Kod uzunroq, lekin unda yangi narsa yo'q — hammasi yuqoridagi bo'limlardan:

html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
</style>
<canvas id="chart" role="img"
  aria-label="Haftalik buyurtmalar: eng ko'pi shanba, 210 ta">
  <table>
    <caption>Haftalik buyurtmalar</caption>
    <tr><th>Du</th><td>120</td></tr>
    <tr><th>Sh</th><td>210</td></tr>
  </table>
</canvas>
<script>
  function setupCanvas(canvas, width, height) {
    const dpr = window.devicePixelRatio || 1;
    canvas.style.width = `${width}px`;
    canvas.style.height = `${height}px`;
    canvas.width = Math.round(width * dpr);
    canvas.height = Math.round(height * dpr);
    const ctx = canvas.getContext("2d");
    ctx.scale(dpr, dpr);
    return ctx;
  }

  const orders = [
    { day: "Du", count: 120 }, { day: "Se", count: 95 },
    { day: "Ch", count: 140 }, { day: "Pa", count: 110 },
    { day: "Ju", count: 180 }, { day: "Sh", count: 210 },
    { day: "Ya", count: 160 },
  ];
  const width = 320;
  const height = 200;
  const chartTop = 24;     // tepada raqamlar uchun joy
  const chartBottom = 170; // pastda kun nomlari uchun joy
  const ctx = setupCanvas(document.getElementById("chart"),
    width, height);

  const max = Math.max(...orders.map((o) => o.count));
  const slot = width / orders.length;   // bitta ustun joyi
  const barWidth = slot * 0.6;

  ctx.font = "12px system-ui, sans-serif";
  ctx.textAlign = "center";

  orders.forEach((order, i) => {
    const barHeight = (order.count / max) * (chartBottom - chartTop);
    const x = i * slot + (slot - barWidth) / 2;
    const y = chartBottom - barHeight;

    ctx.fillStyle = order.count === max ? "#2e7d32" : "#a5d6a7";
    ctx.fillRect(x, y, barWidth, barHeight);

    ctx.fillStyle = "#1b1b1b";
    ctx.fillText(String(order.count), x + barWidth / 2, y - 6);
    ctx.fillText(order.day, x + barWidth / 2, chartBottom + 18);
  });

  // pastki o'q chizig'i
  ctx.beginPath();
  ctx.moveTo(0, chartBottom + 0.5);
  ctx.lineTo(width, chartBottom + 0.5);
  ctx.strokeStyle = "#555555";
  ctx.lineWidth = 1;
  ctx.stroke();

  const best = orders.find((o) => o.count === max);
  console.log(`Eng ko'p: ${best.day} — ${best.count} ta`);
</script>

Konsolda:

text
Eng ko'p: Sh — 210 ta

Asosiy hisoblar:

  • slot = width / orders.length — har kunga teng joy. Ustun shu joyning 60 foizi, qolgani — ustunlar orasidagi bo'shliq.
  • barHeight = (count / max) * (chartBottom - chartTop) — masshtab: eng katta son grafikning to'liq balandligini oladi, qolganlari unga nisbatan. Shanba — 210 / 210 = 1, to'liq balandlik.
  • y = chartBottom - barHeight — y pastga o'sgani uchun ustun tepasini pastki chiziqdan ayirib topamiz. Bu canvas'dagi eng ko'p uchraydigan hisob.
  • chartBottom + 0.5 — 1 px chiziqni piksel chegarasiga emas, o'rtasiga qo'yish. Aks holda chiziq ikki piksel orasiga tushib, 2 px kulrang bo'lib xiralashadi.

9.1 Malika uchun

Malika «Bahor» saytini NVDA ekran o'quvchisi bilan ochadi. Canvas ichidagi ustunlar unga ko'rinmaydi — u faqat piksel. Shuning uchun ikki narsa qildik:

  • role="img" va aria-label — ekran o'quvchi grafikni "rasm" deb, qisqa mazmuni bilan o'qiydi.
  • Teg ichidagi jadval — HTML darsidagi zaxira kontent. Misolda joy tejash uchun ikki kun yozdik; haqiqiy saytda hammasini yozing yoki grafik ostiga to'liq jadvalni ko'rinadigan qilib qo'ying.

Grafik ma'lumoti o'zgarsa, aria-label ni ham yangilashni unutmang — aks holda Malika eski raqamni eshitadi.

10. Canvas, SVG yoki oddiy HTML?

Vazifa Tanlov
Bir necha o'nlab ustun, bosiladigan, tiniq SVG yoki HTML
Minglab nuqta, tez yangilanadigan grafik Canvas
O'yin, animatsiya, fizika Canvas
Rasmni qayta ishlash, filtr, eksport Canvas
Ko'p matn, havolalar, formalar HTML

Canvas'ning narxi: unda element yo'q. Ustunga sichqoncha olib borilganda nima bo'lishini bilish uchun koordinatalarni o'zingiz solishtirasiz. Ekran o'quvchi, matnni belgilash, qidiruv — hech biri ishlamaydi. Shuning uchun canvas "imkon bo'lsa — yo'q, kerak bo'lsa — ha" asbobi.

11. Node'da canvas yo'q

Canvas — brauzer imkoniyati. Node'da document ham, <canvas> ham yo'q:

js
const canvas = document.createElement("canvas");

Node 24 da:

text
ReferenceError: document is not defined

Tarjimasi: "document aniqlanmagan". Serverda rasm chizish kerak bo'lsa (masalan, ijtimoiy tarmoq uchun avtomatik rasm), canvas API'sini Node'ga olib keladigan npm paketlari bor (@napi-rs/canvas kabi). npm paketlarini kursda keyinroq chuqur o'rganamiz, hozir bilish shart emas.

12. Ko'p uchraydigan xatolar

12.1 Element topilmadi

js
// ❌ id xato yozilgan yoki skript <canvas> dan oldin ishladi
const ctx = document.getElementById("chrat").getContext("2d");

Chrome konsolida:

text
Uncaught TypeError: Cannot read properties of null (reading 'getContext')

Tarjimasi: "null ning getContext xususiyatini o'qib bo'lmaydi". getElementById element topmasa null qaytaradi, null da esa metod yo'q. Tuzatish: id ni tekshiring va skriptni <canvas> dan keyin (yoki defer bilan) ulang.

12.2 CSS bilan o'lcham berish

<canvas> ga faqat CSS'da width: 600px berildi, atribut yo'q. Mato 300 × 150 bo'lib qoladi va ikki baravar cho'ziladi — hamma narsa xira va noto'g'ri nisbatda. Tuzatish: mato o'lchamini atribut yoki canvas.width bilan bering ("width atributi va CSS o'lchami" bo'limi).

12.3 beginPath siz yangi shakl

Oldingi shakllar yangi rang bilan qayta chiziladi, animatsiyada esa yo'l cheksiz uzayadi. Tuzatish: har shakldan oldin beginPath().

12.4 Gradusda burchak

ctx.arc(100, 100, 50, 0, 360) — 360 radian, ya'ni 57 marta aylanish. To'liq doira chiqadi, shuning uchun xato sezilmaydi. Lekin arc(..., 0, 90) chorak doira o'rniga yana to'liq doira beradi. Tuzatish: gradus * Math.PI / 180.

12.5 Rasm yuklanmasdan chizish

js
// ❌ rasm hali yuklanmagan — hech narsa chizilmaydi
const img = new Image();
img.src = "osh.png";
ctx.drawImage(img, 0, 0);

Xato ham chiqmaydi — eng yomoni shu. Tuzatish: img.addEventListener("load", ...) yoki await img.decode() dan keyin chizing.

13. Mashqlar

1-mashq (oson): Narx yorlig'i

«Bahor» menyusi uchun 220 × 80 o'lchamli yorliq chizing: burchaklari 12 px yumaloq yashil fon (roundRect, keyin fill) va markazda oq matn Manti — 30 000 so'm. Matnni markazlash uchun textAlign va textBaseline ni "Matn yozish" bo'limidagidek sozlang.

Yechim
html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
</style>
<canvas id="tag" width="220" height="80">Manti — 30 000 so'm</canvas>
<script>
  const ctx = document.getElementById("tag").getContext("2d");

  ctx.beginPath();
  ctx.roundRect(0, 0, 220, 80, 12);
  ctx.fillStyle = "#2e7d32";
  ctx.fill();

  ctx.fillStyle = "#ffffff";
  ctx.font = "700 18px system-ui, sans-serif";
  ctx.textAlign = "center";
  ctx.textBaseline = "middle";
  ctx.fillText("Manti — 30 000 so'm", 110, 40);
</script>

roundRect faqat yo'lga qo'shadi, shuning uchun undan oldin beginPath(), keyin fill(). Matn markazi — matoning o'rtasi: 220 / 2 = 110 va 80 / 2 = 40.

2-mashq (o'rta): Doiraviy diagramma

Bugungi buyurtmalar ulushi: osh 45, lag'mon 25, manti 20, ko'k choy 10. Har taomni doiraning bo'lagi (sektori) qilib chizing. Bo'lak burchagi: count / jami * Math.PI * 2. Har bo'lak — beginPath(), markazga moveTo, arc(markaz, radius, boshi, oxiri), closePath(), fill(). Keyingi bo'lak oldingisining oxiridan boshlansin. Konsolga jami sonni chiqaring.

Ishora: let start = -Math.PI / 2 dan boshlasangiz, birinchi bo'lak soat 12 dan boshlanadi.

Yechim
html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
</style>
<canvas id="pie" width="220" height="220" role="img"
  aria-label="Osh 45, lag'mon 25, manti 20, ko'k choy 10">
</canvas>
<script>
  const ctx = document.getElementById("pie").getContext("2d");
  const parts = [
    { name: "Osh", count: 45, color: "#2e7d32" },
    { name: "Lag'mon", count: 25, color: "#f2c94c" },
    { name: "Manti", count: 20, color: "#e0a458" },
    { name: "Ko'k choy", count: 10, color: "#90a4ae" },
  ];
  const total = parts.reduce((sum, p) => sum + p.count, 0);
  let start = -Math.PI / 2; // soat 12 dan

  for (const part of parts) {
    const angle = (part.count / total) * Math.PI * 2;
    ctx.beginPath();
    ctx.moveTo(110, 110);
    ctx.arc(110, 110, 100, start, start + angle);
    ctx.closePath();
    ctx.fillStyle = part.color;
    ctx.fill();
    start += angle;
  }
  console.log(`Jami: ${total} ta buyurtma`);
</script>

Konsolda:

text
Jami: 100 ta buyurtma

moveTo(110, 110) — qalam markazda. arc markazdan yoyning boshigacha avtomatik chiziq tortadi, closePath esa yoy oxiridan markazga qaytaradi — "pirog bo'lagi" chiqadi. start += angle — keyingi bo'lak shu joydan boshlanadi.

3-mashq (qiyin): Qayta ishlatiladigan drawBarChart

drawBarChart(canvas, data, { width, height }) funksiyasini yozing. data — { label, value } massivi. Funksiya:

  • matoni setupCanvas bilan tiniq qilsin;
  • eng katta qiymatga qarab masshtablasin va eng kattasini to'q rang bilan ajratsin;
  • canvas'ning aria-label iga qisqa mazmun yozsin: "Eng ko'p: <label> — <value>".

Uni «Bahor» taomlari bilan chaqiring: osh 45, lag'mon 25, manti 20, somsa 30 — 300 × 180. Konsolga aria-label ni chiqaring.

Yechim
html
<style>
  body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
</style>
<canvas id="dishes" role="img"></canvas>
<script>
  function setupCanvas(canvas, width, height) {
    const dpr = window.devicePixelRatio || 1;
    canvas.style.width = `${width}px`;
    canvas.style.height = `${height}px`;
    canvas.width = Math.round(width * dpr);
    canvas.height = Math.round(height * dpr);
    const ctx = canvas.getContext("2d");
    ctx.scale(dpr, dpr);
    return ctx;
  }

  function drawBarChart(canvas, data, { width, height }) {
    const ctx = setupCanvas(canvas, width, height);
    const top = 20;
    const bottom = height - 24;
    const max = Math.max(...data.map((d) => d.value));
    const slot = width / data.length;
    const barWidth = slot * 0.6;

    ctx.font = "12px system-ui, sans-serif";
    ctx.textAlign = "center";
    data.forEach((item, i) => {
      const barHeight = (item.value / max) * (bottom - top);
      const x = i * slot + (slot - barWidth) / 2;
      ctx.fillStyle = item.value === max ? "#2e7d32" : "#a5d6a7";
      ctx.fillRect(x, bottom - barHeight, barWidth, barHeight);
      ctx.fillStyle = "#1b1b1b";
      ctx.fillText(item.label, x + barWidth / 2, bottom + 16);
    });

    const best = data.find((d) => d.value === max);
    canvas.setAttribute("aria-label",
      `Eng ko'p: ${best.label} — ${best.value}`);
  }

  const canvas = document.getElementById("dishes");
  drawBarChart(canvas, [
    { label: "Osh", value: 45 },
    { label: "Lag'mon", value: 25 },
    { label: "Manti", value: 20 },
    { label: "Somsa", value: 30 },
  ], { width: 300, height: 180 });
  console.log(canvas.getAttribute("aria-label"));
</script>

Konsolda:

text
Eng ko'p: Osh — 45

Funksiya canvas, ma'lumot va o'lchamni tashqaridan oladi — shuning uchun uni istalgan grafik uchun qayta ishlatish mumkin. O'lcham o'zgarsa, uni "Element o'lchami o'zgarsa" bo'limidagi ResizeObserver ichidan qayta chaqirasiz.

4-mashq: Amaliy tajriba — vazifalar progress halqasi

vazifalar ilovasida nechta vazifa bajarilgani faqat matnda. Kanon kodga tegmasdan tajriba qiling: kurs/mashqlar/13/15-halqa/index.html — massivda besh vazifa ({ id, matn, bajarildi }, uchtasi bajarilgan). 120 × 120 canvas'da halqa chizing: kulrang to'liq aylana va ustidan yashil yoy — bajarilganlar ulushicha. Markazda 3/5 matni. aria-label — "5 tadan 3 tasi bajarildi". Live Server bilan oching.

Ishora: halqa — fill emas, qalin stroke (lineWidth = 12) bilan chizilgan arc. Yoy soat 12 dan boshlansin.

Yechim
html
<canvas id="halqa" role="img"></canvas>
<script>
  const vazifalar = [
    { id: 1, matn: "Non olish", bajarildi: true },
    { id: 2, matn: "Hisobot yozish", bajarildi: true },
    { id: 3, matn: "Mijozga qo'ng'iroq", bajarildi: false },
    { id: 4, matn: "Menyuni yangilash", bajarildi: true },
    { id: 5, matn: "Kassa hisobi", bajarildi: false },
  ];

  function setupCanvas(canvas, width, height) {
    const dpr = window.devicePixelRatio || 1;
    canvas.style.width = `${width}px`;
    canvas.style.height = `${height}px`;
    canvas.width = Math.round(width * dpr);
    canvas.height = Math.round(height * dpr);
    const ctx = canvas.getContext("2d");
    ctx.scale(dpr, dpr);
    return ctx;
  }

  function drawProgressRing(canvas, done, total) {
    const ctx = setupCanvas(canvas, 120, 120);
    const start = -Math.PI / 2;
    ctx.lineWidth = 12;

    ctx.beginPath();
    ctx.arc(60, 60, 48, 0, Math.PI * 2);
    ctx.strokeStyle = "#e0e0e0";
    ctx.stroke();

    ctx.beginPath();
    ctx.arc(60, 60, 48, start, start + (done / total) * Math.PI * 2);
    ctx.strokeStyle = "#2e7d32";
    ctx.lineCap = "round";
    ctx.stroke();

    ctx.fillStyle = "#1b1b1b";
    ctx.font = "700 22px system-ui, sans-serif";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.fillText(`${done}/${total}`, 60, 60);
    canvas.setAttribute("aria-label",
      `${total} tadan ${done} tasi bajarildi`);
  }

  const done = vazifalar.filter((v) => v.bajarildi).length;
  drawProgressRing(document.getElementById("halqa"),
    done, vazifalar.length);
</script>

Kulrang aylana — "yo'l", yashil yoy — bosib o'tilgan qismi. Yoy stroke bilan chizildi, shuning uchun ichi bo'sh, faqat qalin chiziq. vazifalar dagi vazifa bajarilganda drawProgressRing ni qayta chaqirish yetarli — canvas'da "yangilash" degani shu: qayta chizish.

bash
git add 13/15-halqa/index.html
git commit -m "13/15: vazifalar uchun canvas progress halqasi"

14. Real ishda

  • Grafik kutubxonalari. Chart.js va ECharts canvas'ga chizadi: bu darsdagi masshtab, fillRect, fillText va devicePixelRatio sozlamasi ularning ichida bor. Kutubxonani sozlayotganda "nega grafik xira?" savoliga endi javob bilasiz.
  • Xaritalar va dizayn vositalari. Onlayn xaritalar, Figma va Excalidraw kabi chizish ilovalari minglab obyektni canvas (yoki WebGL) bilan chizadi — DOM bunga bardosh bermaydi.
  • Rasm generatsiyasi. Sertifikat, chek, ijtimoiy tarmoq uchun avtomatik rasm — canvas'da chizilib, faylga eksport qilinadi. Buni keyingi darsda qilamiz.
  • Intervyu. "Canvas va SVG farqi?", "Canvas nega retina ekranda xira?", "beginPath nima uchun kerak?" — frontend intervyularida uchraydi.

Xulosa

  • <canvas> — mato, getContext("2d") — chizish asboblari. Koordinata boshi chap yuqorida, y pastga o'sadi.
  • To'rtburchak — fillRect/strokeRect/clearRect; qolgan shakllar — yo'l: beginPath → moveTo/lineTo/arc → fill/stroke.
  • Burchaklar radianda: to'liq aylana Math.PI * 2. Har yangi shakl — beginPath() bilan.
  • Matn — font, textAlign, textBaseline, fillText; eni — measureText. Rasm — faqat yuklangach drawImage.
  • Mato o'lchami (atribut) va ko'rinish o'lchami (CSS) — boshqa-boshqa. Tiniqlik uchun matoni devicePixelRatio ga ko'paytirib, ctx.scale qiling.
  • Canvas'da element yo'q: o'zgartirish — qayta chizish; ekran o'quvchi uchun aria-label va zaxira kontent.

Keyingi dars: Canvas amaliyotda — canvas'ni harakatga keltiramiz: animatsiya va o'yin sikli, rasmni kichraytirish va qirqish, piksellar bilan ishlash va tayyor rasmni toBlob bilan faylga yuklab olish.

Manbalar

  • HTML Living Standard: "The canvas element", "CanvasRenderingContext2D" — html.spec.whatwg.org
  • MDN: Canvas tutorial, CanvasRenderingContext2D, Window.devicePixelRatio — developer.mozilla.org
  • web.dev: "High DPI Canvas" — web.dev
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript Canvas 2D: <canvas> ga shakl, matn va rasm chizish — IlmHamroh