Mundarija (34)
- Bu darsda
- 1. Nega bu kerak?
- 2. SVG va canvas: ikki xil chizish
- 2.1 SVG — har shakl alohida element
- 2.2 Canvas — bitta element, ichida piksellar
- 3. Chart.js va react-chartjs-2
- 3.1 Ro'yxatdan o'tkazish
- 3.2 Eng ko'p uchraydigan xato: ro'yxatdan o'tmagan qism
- 3.3 Katta ma'lumot: decimation
- 4. ECharts: bitta sozlama obyekti
- 4.1 option — grafikning to'liq tavsifi
- 4.2 Canvas yoki SVG — ECharts'da tanlov sizda
- 4.3 ECharts va accessibility
- 5. Nivo va visx: SVG'ning ikki qutbi
- 5.1 Nivo — tayyor, chiroyli
- 5.2 visx — g'isht va loy
- 6. Moliya grafiklari: sham
- 6.1 Sham nimani ko'rsatadi
- 6.2 lightweight-charts (TradingView)
- 6.3 Ma'lumot tartibi har joyda boshqacha
- 7. Grafik va accessibility: kutubxonalar bo'yicha
- 8. Biz sinab ko'rdik: hajm, CSP va narx
- 8.1 Hajm
- 8.2 CSP va Trusted Types
- 8.3 Nega vazifalar o'zgarmaydi
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): bo'sh joylarni to'ldiring
- 2-mashq (o'rta): eng katta o'zgarish
- 3-mashq (qiyin): savdolardan sham yasash
- 4-mashq: tajriba qadami (ixtiyoriy)
- 11. Real ishda
- Xulosa
- Manbalar
React grafik kutubxonalari: Chart.js, ECharts, Nivo, visx va moliya grafiklari
Qisqacha: Grafik ikki xil chiziladi. SVG — har ustun va yozuv alohida DOM elementi (Recharts, Nivo, visx). Canvas — bitta
<canvas>, ichida piksellar (Chart.js, ECharts, lightweight-charts). O'nlab nuqtada SVG qulay, o'n minglab nuqtada canvas tez ishlaydi. Lekin canvas ichini ekran o'quvchi ko'rmaydi — matn muqobili o'zingizdan. Kanon 6 ta kutubxonani o'lchadi: visx +12.2 KB dan ECharts +167.0 KB gacha. ECharts va lightweight-chartsvazifalarning Trusted Types siyosatida yiqildi, Chart.js, Nivo va visx — 0 buzilish.
Bu darsda
- SVG va canvas grafiklar farqini tushuntirasiz va vazifaga qarab tanlaysiz.
- Chart.js'ni react-chartjs-2 bilan ulaysiz: ro'yxatdan o'tkazish, bosish,
aria-label. - 10 000 nuqtali grafikni decimation bilan tez chizasiz va nega "har 20-nuqta" xavfli ekanini ko'rsatasiz.
- ECharts, Nivo va visx qanday ishlashini bilasiz, sham (candlestick) grafigini o'qiysiz va chizasiz.
- Kutubxonani hajm, CSP va a11y bo'yicha o'lchab tanlaysiz.
Oldin bilishingiz kerak: Grafiklar I: Recharts, Canvas 2D asoslari, Brauzer xavfsizlik modeli, XSS va DOM xavfsizligi, useRef II: DOM elementlari.
1. Nega bu kerak?
O'tgan darsda Recharts bilan haftalik sotuv grafigini chizdik — besh ustun. Bugun Jasur akada ikki yangi savol bor.
Birinchisi — tandir. «Bahor»ning yangi tandirida harorat sensori bor, u har daqiqada o'lchaydi. Bir hafta — 10 000 ga yaqin o'lchov. Jasur aka hammasini bitta chiziqda ko'rmoqchi: "Qachon pech o'chib qolgan?".
Ikkinchisi — xarid. Guruch narxi har kuni o'zgaradi. Hafta boshida qancha edi, oxirida qancha, eng arzon va eng qimmat payti qachon? Oddiy chiziq faqat bitta narxni ko'rsatadi. Bu savolga moliya grafigi — sham grafigi javob beradi.
Recharts ikkalasida ham noqulay. 10 000 nuqtali SVG — 10 000 ta DOM elementi. Sham grafigi esa unda tayyor yo'q. Shuning uchun boshqa kutubxonalarni ko'ramiz.
Hayotiy o'xshatish: bino rejasi. SVG — Lego'dan yig'ilgan maket: har g'isht alohida, birini olib, rangini o'zgartirish mumkin. Canvas — fotosurat: tez olinadi, minglab detal sig'adi, lekin undan bitta g'ishtni ajratib bo'lmaydi.
2. SVG va canvas: ikki xil chizish
2.1 SVG — har shakl alohida element
Recharts grafigi ichini eslang: <rect>, <text>, <path>. Har biri DOM'da, har biriga CSS va hodisa ulash mumkin. Ekran o'quvchi va qidiruvchi matnni o'qiydi.
Narxi — element soni. Kanon tajribasida bir xil grafik (ikki ustun, «#bozor» 3, «#uy» 1) har kutubxonada chizildi va DOM elementlari sanaldi (kutubxonalar-21.json):
| Kutubxona | Chizish | Elementlar |
|---|---|---|
| Recharts | SVG | 77 |
| Nivo | SVG | 86 |
| visx | SVG | 5 |
| Chart.js | canvas | 3 |
Recharts va Nivo o'qlar, chiziqlar va yozuvlarni ham chizadi, visx esa faqat biz so'ragan ikki <rect> ni. Chart.js'da esa sarlavha, o'ram va bitta <canvas> — qolgani piksel.
Endi nuqtalar ko'paysa nima bo'lishini o'zingiz ko'ring. Har nuqta — bitta <circle>:
import { useEffect, useState } from "react";
function makePoints(n) {
return Array.from({ length: n }, (_, i) => ({
x: (i / n) * 300,
y: 60 + 40 * Math.sin(i / 20),
}));
}
export default function App() {
const [count, setCount] = useState(100);
const points = makePoints(count);
useEffect(() => {
const n = document.querySelectorAll("circle").length;
console.log(`${count} nuqta → ${n} ta <circle>`);
}, [count]);
return (
<figure>
<svg viewBox="0 0 300 120" width="300" aria-hidden="true">
{points.map((p, i) => (
<circle key={i} cx={p.x} cy={p.y} r="1.5" fill="#1f6f4a" />
))}
</svg>
<button type="button" onClick={() => setCount(1000)}>
1 000 nuqta
</button>
</figure>
);
}Konsolda:
100 nuqta → 100 ta <circle>
1000 nuqta → 1000 ta <circle>Har nuqta — DOM'da bitta element. React har birini yaratadi, brauzer har birining joyini hisoblaydi. 1 000 tagacha bu sezilmaydi. 10 000 da esa sahifa og'irlashadi, sichqoncha bilan yurish qotadi. Har bir yangi ma'lumotda React o'n ming elementni solishtiradi (rekonsilyatsiya).
2.2 Canvas — bitta element, ichida piksellar
Canvas ni Canvas 2D asoslari darsida ko'rgansiz: getContext("2d"), keyin lineTo, fillRect. Natija — rasm. Brauzer uchun 10 000 nuqta ham, 10 nuqta ham bitta element. Shuning uchun katta ma'lumotda canvas kutubxonalar tezroq.
Lekin canvas bilan uchta narsa yo'qoladi:
- Matn DOM'da yo'q. Ekran o'quvchi grafikni "rasm" deb ham tanimaydi —
<canvas>ga o'zingiz nom berasiz. - CSS ishlamaydi. Rang, shrift — JS sozlamasi orqali. Qorong'i temaga o'tish ham kod bilan.
- Hodisa butun canvas'da. Qaysi ustun bosilganini kutubxona koordinatadan hisoblaydi.
Qaysi biri qachon — qisqa yo'l:
flowchart TB
S{"Nuqtalar soni?"} -->|"yuzlab"| A{"Grafik turi?"}
S -->|"minglab va ko'p"| C["Canvas: Chart.js, ECharts"]
A -->|"oddiy: ustun, chiziq"| R["SVG: Recharts, Nivo"]
A -->|"noodatiy, o'z dizayn"| V["SVG: visx yoki o'z SVG"]
A -->|"moliya: sham, birja"| L["lightweight-charts"]
C --> T["+ matn muqobili (jadval)"]Diagrammaning oxirgi tuguniga qarang: canvas tanlasangiz, matn muqobili majburiy qadamga aylanadi.
Tekshirib ko'ring: Grafikdagi har ustunga CSS bilan
:hovereffekt bermoqchisiz. Chart.js'dacanvas rect:hover { … }yozdingiz — ishlamadi. Nega?
Javob
Canvas ichida rect elementi yo'q — u yerda faqat piksellar. CSS selektori hech narsa topmaydi. Chart.js'da hover rangi sozlama bilan beriladi (hoverBackgroundColor), kutubxona uni o'zi chizadi. SVG grafikda esa rect:hover ishlaydi, chunki ustun — haqiqiy DOM elementi.
3. Chart.js va react-chartjs-2
3.1 Ro'yxatdan o'tkazish
Chart.js — eng mashhur canvas grafik kutubxonasi, React'siz ham ishlaydi. React uchun ingichka o'ram — react-chartjs-2: <Bar>, <Line>, <Pie> komponentlari. Kanon o'lchagan versiyalar: chart.js 4.5.1, react-chartjs-2 5.3.1.
npm i -E chart.js@4.5.1 react-chartjs-2@5.3.1Chart.js 4 da har qism — shkala, element, plagin — alohida. Kerakligini o'zingiz ro'yxatdan o'tkazasiz (register). Shunda bundler keraksizini tashlab yuboradi (tree shaking).
Bu blok natija oynasida ishlaydi, lekin tekshiruvchimiz uni chiza olmaydi: jsdom'da canvas yo'q. «Ishga tushir» ni bosing va juma ustunini bosing:
import { useRef } from "react";
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Tooltip,
Legend,
} from "chart.js";
import { Bar, getElementAtEvent } from "react-chartjs-2";
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Tooltip,
Legend,
);
const data = {
labels: ["Du", "Se", "Ch", "Pa", "Ju"],
datasets: [
{
label: "Osh (porsiya)",
data: [32, 41, 28, 45, 60],
backgroundColor: "#1f6f4a",
},
],
};
export default function App() {
const chartRef = useRef(null);
function handleClick(event) {
const [hit] = getElementAtEvent(chartRef.current, event);
if (hit) {
const day = data.labels[hit.index];
console.log(day, data.datasets[0].data[hit.index]);
}
}
return (
<div style={{ maxWidth: 480 }}>
<Bar
ref={chartRef}
data={data}
options={{ plugins: { legend: { position: "bottom" } } }}
role="img"
aria-label="Haftalik osh sotuvi: eng ko'pi juma, 60 porsiya"
onClick={handleClick}
/>
</div>
);
}Biz bu blokni Chrome 155 da (headless) ochib, juma ustunini dasturiy bosdik. Konsolga Ju 60 chiqdi, CSP buzilishi — 0.
Kodda to'rt narsa:
ChartJS.register(...)— modul darajasida, bir marta. Komponent ichida emas.ref— react-chartjs-2 Chart.js nusxasini ref'ga beradi.getElementAtEvent(chart, event)bosilgan nuqtani topadi:index— nechanchi ustun (useRef II).role="img"vaaria-label—<canvas>ga o'tadi. Malika "Haftalik osh sotuvi: eng ko'pi juma…" ni eshitadi. Ichidagi raqamlarni esa eshitmaydi — ular uchun jadval kerak (Recharts darsidagi qoida).responsivesukut bo'yicha yoqilgan: grafik ota-element eniga moslashadi, balandligiaspectRatio(sukut 2) bilan.
Ichki tomoni ham muhim. react-chartjs-2 data o'zgarganda grafikni o'chirib qayta yaratmaydi — Chart.js'ning update() ini chaqiradi. Shunda yangi ustun silliq o'sib chiqadi.
3.2 Eng ko'p uchraydigan xato: ro'yxatdan o'tmagan qism
CategoryScale ni register ga yozish esdan chiqdi, deylik. Konsolda:
Error: "category" is not a registered scale.Tarjimasi: "category ro'yxatdan o'tgan shkala emas". Biz bu xatoni Node 24 da chart.js 4.5.1 bilan oldik. Matn nima yetishmayotganini aniq aytadi: category → CategoryScale, linear → LinearScale, "point" element → PointElement.
Ikki yo'l bor. Kerakli qismlarni bittalab qo'shish — kichik bundle. Yoki hammasi birdan:
import "chart.js/auto"; // hamma qism ro'yxatdan o'tadi — kattaroqchart.js/auto — prototip uchun qulay. Production'da hajmni o'lchab, kerakli qismlarga o'ting.
3.3 Katta ma'lumot: decimation
Endi tandir. 10 000 ta o'lchovni 800 piksellik grafikka chizsak, har piksel ustuniga 12 tadan ortiq nuqta tushadi. Ko'z ularni baribir ajrata olmaydi. Demak, chizishdan oldin nuqtalarni kamaytirish mumkin. Bu — decimation (nuqtalarni siyraklashtirish).
Eng sodda yo'l — har 20-nuqtani olish. Lekin u xavfli. Avval tushunchani kutubxonasiz sinab ko'raylik:
// 10 000 ta o'lchov: harorat sensori, har daqiqada
const points = Array.from({ length: 10_000 }, (_, i) => ({
x: i,
y: 20 + (i % 60) / 10,
}));
points[7777].y = 95; // bitta keskin sakrash (avariya!)
// 1-usul: har 20-nuqtani olish
const every20 = points.filter((p) => p.x % 20 === 0);
// 2-usul: min-max — har bo'lakdan eng kichik va eng katta
function minMax(data, buckets) {
const size = Math.ceil(data.length / buckets);
const result = [];
for (let i = 0; i < data.length; i += size) {
const part = data.slice(i, i + size);
const min = part.reduce((a, b) => (b.y < a.y ? b : a));
const max = part.reduce((a, b) => (b.y > a.y ? b : a));
result.push(...(min.x < max.x ? [min, max] : [max, min]));
}
return result;
}
const decimated = minMax(points, 250);
const peak = (data) => Math.max(...data.map((p) => p.y));
const show = (name, data) =>
console.log(`${name}: ${data.length} nuqta, cho'qqi ${peak(data)}`);
show("asl", points);
show("har 20-chi", every20);
show("min-max", decimated);Konsolda:
asl: 10000 nuqta, cho'qqi 95
har 20-chi: 500 nuqta, cho'qqi 24
min-max: 500 nuqta, cho'qqi 95Ikkalasi ham 500 nuqta qoldirdi. Lekin "har 20-chi" usul avariyani yo'qotdi: 7777-nuqta 20 ga bo'linmaydi va tashlab yuborildi. Grafik "hammasi joyida" deb ko'rsatadi. Min-max esa har bo'lakdan eng kichik va eng katta qiymatni saqlaydi — cho'qqi qoladi. Bu — o'quv modeli. Chart.js'da xuddi shu g'oya tayyor plagin:
import {
Chart as ChartJS,
LinearScale,
LineElement,
PointElement,
Decimation,
Tooltip,
} from "chart.js";
import { Line } from "react-chartjs-2";
ChartJS.register(
LinearScale,
LineElement,
PointElement,
Decimation,
Tooltip,
);
// 10 000 ta o'lchov (oldingi blokdagi kabi), cho'qqi — 7777 da
const points = Array.from({ length: 10_000 }, (_, i) => ({
x: i,
y: i === 7777 ? 95 : 20 + (i % 60) / 10,
}));
const options = {
parsing: false, // ma'lumot tayyor {x, y} — qayta o'qilmaydi
animation: false,
scales: { x: { type: "linear" } },
plugins: {
decimation: { enabled: true, algorithm: "min-max" },
},
elements: { point: { radius: 0 } },
};
export default function App() {
return (
<Line
data={{ datasets: [{ label: "Harorat", data: points }] }}
options={options}
role="img"
aria-label="Sensor harorati: 10 000 o'lchov, bitta cho'qqi 95"
/>
);
}Biz Chrome 155 da o'lchadik: 784 piksel enli grafikda Chart.js 10 000 nuqtadan 1 851 tasini chizdi, cho'qqi 95 saqlandi.
Decimation faqat shartlar bajarilsa ishlaydi. Bu shartlarni Chart.js 4.5.1 manba kodidan tekshirdik (plugin_decimation):
| Shart | Nega |
|---|---|
grafik turi — line |
ustunni siyraklashtirib bo'lmaydi |
x o'qi linear yoki time |
toifalar ("Du") orasida "oraliq" yo'q |
parsing: false |
ma'lumot {x, y} ko'rinishida tayyor bo'lsin |
| nuqtalar > 4 × grafik eni | kam bo'lsa plagin hech narsa qilmaydi |
Shartlardan biri bajarilmasa — xato chiqmaydi, plagin jim o'tib ketadi. Grafik sekin bo'lsa, birinchi shu jadvalni tekshiring. Ikkinchi algoritm — "lttb" (Largest Triangle Three Buckets). U chiziq shaklini yaxshiroq saqlaydi, samples bilan nuqtalar sonini berasiz.
Tekshirib ko'ring: Kunlik sotuv grafigida x o'qi
"Du","Se", … toifalar. Decimation yoqildi, lekin grafik o'zgarmadi. Nega?
Javob
Toifali o'q (category) — shartlardan biri buzilgan. Plagin faqat linear yoki time o'qda ishlaydi, chunki "bu ikki nuqta orasida yana nima bor" degan savol faqat son yoki vaqt o'qida ma'noli. Yana: bir haftada yetti nuqta — kamaytirishning hojati ham yo'q.
4. ECharts: bitta sozlama obyekti
4.1 option — grafikning to'liq tavsifi
Apache ECharts — Xitoyda Baidu boshlagan, hozir Apache fondidagi katta kutubxona. Yuzlab grafik turi bor: issiqlik xaritasi, daraxt, grafik-tarmoq, sham. React'da u komponentlar bilan emas, bitta sozlama obyekti bilan ishlaydi:
import * as echarts from "echarts/core";
import { BarChart } from "echarts/charts";
import { GridComponent, TooltipComponent } from "echarts/components";
import { SVGRenderer } from "echarts/renderers";
import ReactECharts from "echarts-for-react/esm/core";
echarts.use([BarChart, GridComponent, TooltipComponent, SVGRenderer]);
const option = {
tooltip: { trigger: "axis", renderMode: "richText" },
xAxis: { type: "category", data: ["Du", "Se", "Ch"] },
yAxis: { type: "value" },
series: [{ type: "bar", name: "Osh", data: [32, 41, 28] }],
};
<ReactECharts
echarts={echarts}
option={option}
opts={{ renderer: "svg" }}
style={{ height: 240 }}
/>;Recharts'dagi <XAxis> bu yerda xAxis: {…} maydoni. Hamma narsa bitta JSON'ga o'xshash obyektda — uni serverdan ham olsa bo'ladi.
echarts/core + echarts.use([...]) — Chart.js'dagi register ning o'zi: faqat kerakli qism bundle'ga tushadi. Shunda ham kanon o'lchovida hajm eng katta — +167.0 KB (kanon CanvasRenderer bilan o'lchagan; SVG chizuvchili variant o'lchanmagan). import * as echarts from "echarts" (to'liq) yozsangiz, bundan ham ko'p bo'ladi.
echarts-for-react — uchinchi tomon o'rami (kanon tajribasida 3.0.6). U option o'zgarganda setOption ni chaqiradi, komponent o'chganda dispose qiladi.
4.2 Canvas yoki SVG — ECharts'da tanlov sizda
ECharts ikkala usulda ham chiza oladi: renderer: "canvas" (sukut) yoki "svg". Kanon tajribasida ECharts vazifalar ning Trusted Types siyosatida ochilmadi. Kanon qaydida sabab qisqa — "innerHTML, tooltip". Biz dars uchun aniqroq qidirdik: to'rt variantni Chrome 155 da, kanon CSP'si ostida ochdik (bu bizning sinovimiz, kanon emas).
| Variant | TT buzilishi | Natija |
|---|---|---|
| canvas | 1 | sahifa chizilmadi |
| svg | 0 | 9 ta path, o'q yozuvlari DOM'da |
| svg + HTML tooltip, sichqoncha ustida | 1 | grafik bor, tooltip yiqildi |
svg + renderMode: "richText" |
0 | grafik bor |
Birinchi qator kanon qaydini to'ldiradi: grafik tooltip'gacha yetmaydi. Canvas chizuvchisi (zrender kutubxonasi) ishga tushganda konteynerni tozalaydi: root.innerHTML = ''. Bo'sh satr ham Trusted Types'da taqiqlangan — siyosat satrning ichiga qaramaydi, faqat turini tekshiradi (XSS va DOM xavfsizligi).
Uchinchi qator — tooltip. Sukut bo'yicha u HTML: el.innerHTML = content. Sichqoncha ustunga kelishi bilan buzilish chiqdi. renderMode: "richText" tooltip'ni grafikning o'zida chizadi — HTML'siz.
Demak, ECharts'ni qattiq CSP'li ilovada ishlatish mumkin: SVG chizuvchi + richText tooltip. Bu — bitta sinov, hamma grafik turi uchun emas. O'z grafigingizni CSP bilan o'zingiz sinang.
4.3 ECharts va accessibility
ECharts'da aria: { enabled: true } bor: kutubxona grafik uchun avtomatik tavsif yasaydi. SVG variantida biz uni o'qidik:
This is a chart with type Bar chart named Osh.The data is as follows: the data for Du is 0, 32, the data for Se is 1, 41.Ikki muammo ko'rinadi. Matn inglizcha, va "Du is 0, 32" — ustun tartib raqami (0) ham qiymat bilan qo'shilib ketgan. Malika uchun bu chalkash. Tavsifni o'zingiz bering (aria.label.description) yoki grafik yoniga jadval qo'ying.
5. Nivo va visx: SVG'ning ikki qutbi
5.1 Nivo — tayyor, chiroyli
Nivo — d3 hisoblari ustiga qurilgan tayyor React grafiklari. Bitta komponent, ko'p prop:
import { ResponsiveBar } from "@nivo/bar";
<div style={{ height: 240 }}>
<ResponsiveBar
data={[
{ kun: "Du", osh: 32 },
{ kun: "Se", osh: 41 },
]}
keys={["osh"]}
indexBy="kun"
margin={{ top: 10, right: 10, bottom: 30, left: 40 }}
role="img"
ariaLabel="Haftalik osh sotuvi"
/>
</div>;keys — qaysi maydonlar ustun bo'ladi, indexBy — qaysi maydon o'qda. ResponsiveBar ota-element o'lchamini oladi — otaga balandlik bering (Recharts'dagi tuzoq). Nivo ko'p turlarda canvas variantini ham beradi (BarCanvas). Kanon: @nivo/bar 0.99.0 — +81.3 KB, CSP 0.
5.2 visx — g'isht va loy
visx (Airbnb) — grafik emas, grafik yasash uchun qismlar: shkalalar (scaleBand, scaleLinear), shakllar (Bar, LinePath), guruhlar. Kanon tajribasidagi kod:
import { Group } from "@visx/group";
import { scaleBand, scaleLinear } from "@visx/scale";
import { Bar } from "@visx/shape";
const d = [{ teg: "#bozor", n: 3 }, { teg: "#uy", n: 1 }];
const x = scaleBand({
domain: d.map((q) => q.teg),
range: [0, 360],
padding: 0.2,
});
const y = scaleLinear({ domain: [0, 3], range: [200, 0] });
<svg width={360} height={200}>
<Group>
{d.map((q) => (
<Bar
key={q.teg}
x={x(q.teg)}
y={y(q.n)}
width={x.bandwidth()}
height={200 - y(q.n)}
fill="#1f6f4a"
/>
))}
</Group>
</svg>;Shkala (scale) — qiymatni pikselga aylantiruvchi funksiya. y(3) → 0 (tepa), y(0) → 200 (past): SVG'da y o'qi pastga qarab o'sadi. O'qlar, tooltip, legend — alohida paketlar yoki o'zingiz. Natija — eng yengil: +12.2 KB, CSP 0, 5 ta element.
O'tgan darsdagi o'z SVG grafigimiz ham shu yo'l edi — faqat shkalani masshtab deb o'zimiz hisobladik. visx noodatiy dizayn kerak bo'lganda yaxshi: siz chizasiz, u hisoblaydi.
Tekshirib ko'ring: Dizayner Jasur akaga noodatiy grafik chizib berdi: ustunlar o'rniga osh qozonlari, balandligi sotuvga mutanosib. Nivo yoki visx?
Javob
visx. Nivo tayyor ko'rinishlarni beradi, ularni moslashtirish chegaralangan. visx esa faqat hisob beradi (scaleBand, scaleLinear), chizishni sizga qoldiradi: har ustun o'rniga <image> yoki <path> qo'yasiz. Narxi — o'qlar va tooltip'ni ham o'zingiz yozasiz.
6. Moliya grafiklari: sham
6.1 Sham nimani ko'rsatadi
Guruch narxi kun davomida o'zgaradi. Bir davr (kun, hafta) uchun to'rtta son muhim: ochilish (open), eng yuqori (high), eng past (low), yopilish (close). Qisqasi — OHLC. Sham grafigi (candlestick) shu to'rt sonni bitta "sham" bilan chizadi:
- tana — ochilishdan yopilishgacha to'rtburchak;
- pilik — eng past va eng yuqori orasidagi ingichka chiziq;
- rang — narx o'sgan bo'lsa yashil, tushgan bo'lsa qizil.
Kutubxonasiz o'quv modeli — oddiy SVG. Narxlar o'ylab topilgan (o'quv ma'lumoti):
import { useEffect } from "react";
// Guruch, so'm/kg (o'quv ma'lumoti)
const weeks = [
{ name: "1-h", open: 14000, high: 14600, low: 13800, close: 14400 },
{ name: "2-h", open: 14400, high: 14500, low: 13900, close: 14000 },
{ name: "3-h", open: 14000, high: 15200, low: 13900, close: 15000 },
{ name: "4-h", open: 15000, high: 15300, low: 14700, close: 14800 },
];
const W = 320;
const H = 180;
const PAD = 20;
const low = Math.min(...weeks.map((w) => w.low));
const high = Math.max(...weeks.map((w) => w.high));
// shkala: narx → piksel (qimmat — tepada)
const y = (price) =>
PAD + ((high - price) / (high - low)) * (H - 2 * PAD);
const step = W / weeks.length;
function Candle({ w, i }) {
const up = w.close >= w.open;
const x = i * step + step / 2;
const top = y(Math.max(w.open, w.close));
const bottom = y(Math.min(w.open, w.close));
return (
<g>
<line
x1={x}
x2={x}
y1={y(w.high)}
y2={y(w.low)}
stroke="#374151"
/>
<rect
x={x - 12}
y={top}
width={24}
height={Math.max(1, bottom - top)}
fill={up ? "#1f6f4a" : "#b91c1c"}
/>
<text x={x} y={H - 2} textAnchor="middle" fontSize="12">
{w.name}
</text>
</g>
);
}
export default function App() {
useEffect(() => {
for (const w of weeks) {
const diff = w.close - w.open;
console.log(`${w.name}: ${diff > 0 ? "+" : ""}${diff}`);
}
}, []);
return (
<figure>
<svg viewBox={`0 0 ${W} ${H}`} width={W} aria-hidden="true">
{weeks.map((w, i) => (
<Candle key={w.name} w={w} i={i} />
))}
</svg>
<figcaption>Guruch narxi, 4 hafta (so'm/kg)</figcaption>
</figure>
);
}Konsolda:
1-h: +400
2-h: -400
3-h: +1000
4-h: -200Ekranda:
1-h
2-h
3-h
4-h
Guruch narxi, 4 hafta (so'm/kg)«Ishga tushir» — to'rtta sham. 1- va 3-hafta yashil (narx o'sdi), 2- va 4- qizil. 3-hafta shamiga qarang: pilik tepaga uzun cho'zilgan — hafta ichida narx 15 200 gacha chiqqan, yopilishda 15 000. Bitta shamda to'rt son — chiziqli grafik buni ko'rsata olmaydi.
y funksiyasi — visx'dagi scaleLinear ning qo'lda yozilgani. Ekranda: dagi har hafta nomi alohida qatorda — tekshiruvchi SVG <text> ni shunday o'qiydi.
6.2 lightweight-charts (TradingView)
Birjalardagi grafiklar odatda lightweight-charts bilan chiziladi — TradingView kompaniyasining ochiq kodli kutubxonasi. Canvas, juda tez, vaqt o'qi va masshtablash tayyor. React o'rami yo'q — ref va effekt bilan o'zimiz ulaymiz:
import { useEffect, useRef } from "react";
import { createChart, CandlestickSeries } from "lightweight-charts";
import type { CandlestickData } from "lightweight-charts";
export function PriceChart({ data }: { data: CandlestickData[] }) {
const boxRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const chart = createChart(boxRef.current!, {
height: 240,
layout: { attributionLogo: false }, // pastda — nega
});
chart.addSeries(CandlestickSeries).setData(data);
chart.timeScale().fitContent();
return () => chart.remove(); // cleanup: canvas'lar o'chadi
}, [data]);
return <div ref={boxRef} role="img" aria-label="Guruch narxi" />;
}
// data: [{ time: "2026-10-19", open: 14000, high: 14600,
// low: 13800, close: 14400 }, …]5-versiyada seriya chart.addSeries(CandlestickSeries) bilan qo'shiladi. Eski maqolalarda chart.addCandlestickSeries() ni ko'rasiz — bu 4-versiya API'si.
Kanon tajribasida lightweight-charts 5.2.1 Trusted Types'da yiqildi (+51.4 KB). Biz sababini manba kodidan topdik: kutubxona TradingView logotipini chizadi, SVG'ni esa innerHTML bilan qo'yadi. Logotipni o'chirib (attributionLogo: false) qayta sinadik — CSP buzilishi 0, grafik chizildi (7 ta canvas).
Lekin logotip shunchaki bezak emas. Kutubxona Apache 2.0 litsenziyasida, tur faylidagi izoh shunday deydi: NOTICE faylidagi mualliflik yozuvi va tradingview.com'ga havola sahifangizda bo'lishi shart. Logotip shu talabni bajaradi. O'chirsangiz, havolani o'zingiz qo'ying (masalan, grafik ostida "Grafik: TradingView").
6.3 Ma'lumot tartibi har joyda boshqacha
OHLC to'rt son, lekin kutubxonalar ularni turli tartibda kutadi:
| Kutubxona | Bitta sham |
|---|---|
| lightweight-charts | { time, open, high, low, close } |
ECharts (type: "candlestick") |
[open, close, low, high] |
ECharts'da massiv tartibi — ochilish, yopilish, eng past, eng yuqori. OHLC tartibida bersangiz, xato chiqmaydi — shamlar shunchaki noto'g'ri chiziladi. Obyektdan massivga o'tkazishni bitta funksiyaga yig'ing va unga test yozing.
7. Grafik va accessibility: kutubxonalar bo'yicha
O'tgan darsdagi qoidalar (matn muqobili, faqat rang emas, kontrast 3:1, harakatni kamaytirish) hamma kutubxonaga tegishli. Farq — kutubxona nima beradi:
| Kutubxona | Nima bor | Sizdan |
|---|---|---|
| Chart.js | <canvas> ga prop o'tadi |
role="img", aria-label, jadval |
| ECharts | aria — avtomatik tavsif (inglizcha) |
o'zbekcha tavsif yoki jadval |
| Nivo | role, ariaLabel proplari |
jadval |
| visx | hech narsa — siz chizasiz | hammasi |
| lightweight-charts | hech narsa (canvas) | aria-label, jadval |
Sham grafigida rang (yashil/qizil) yolg'iz qolmasin: rang ajrata olmaydigan foydalanuvchi uchun tana ichi bo'sh/to'la yoki jadvalda "+400" kabi belgi qo'shing.
8. Biz sinab ko'rdik: hajm, CSP va narx
8.1 Hajm
Kanon har kutubxonaning eng kichik ishlaydigan misolini Vite 8.3.3 bilan yig'di. Hajm — faqat React'dan ortig'i, gzip:
- visx 4.0.012,2 KB
- Chart.js 4.5.150,6 KB
- lightweight-charts 5.2.151,4 KB
- Nivo 0.99.081,3 KB
- Recharts 3.10.196,6 KB
- ECharts 6.1.0 (core)167 KB
Manba: O'lchandi: kanon tajribasi, Vite 8.3.3, React 19.3.0, gzip -9, Chrome 155, natijalar/tajriba/kutubxonalar-21.txt, 2026-10-17
Taqqoslash uchun: React va react-dom — 66.1 KB. Recharts bu yerda 96.6 KB — vazifalar ichidagi 103.4 KB dan biroz kam, chunki misol kichikroq. ECharts — React'ning ikki yarim barobari.
8.2 CSP va Trusted Types
Xom natija (kutubxonalar-21.txt), xato matnlari qisqartirilgan:
grafik-chartjs + 50.6 KB | CSP 0
grafik-echarts + 167.0 KB | CSP require-trusted-types-for×1 | xato: … TrustedHTML …
grafik-lightweight + 51.4 KB | CSP require-trusted-types-for×2 | xato: … TrustedHTML …
grafik-nivo + 81.3 KB | CSP 0
grafik-recharts + 96.6 KB | CSP 0
grafik-visx + 12.2 KB | CSP 0Hech biri tashqi serverga bormadi. Canvas o'zi CSP muammosi emas — Chart.js canvas va 0 buzilish. Muammo — kutubxona ichidagi bitta innerHTML: ECharts'da konteynerni tozalash, lightweight-charts'da logotip. Ikkalasini ham sozlama bilan aylanib o'tdik («ECharts» va «lightweight-charts» bo'limlari) — bu bizning qo'shimcha sinovimiz, kanon emas.
Yana bir halol eslatma: ECharts kanonda birinchi yig'ishda umuman chizilmadi — konsolda Minified React error #130 (React element turi noto'g'ri). Peer bog'liqliklar qayta o'rnatilgach, o'lchov takrorlandi va yuqoridagi raqam olindi. Birinchi xatoning aniq sababi tajribada yozilmagan.
8.3 Nega vazifalar o'zgarmaydi
Bu darsda kanon kodi o'zgarmaydi. Ilovadagi yagona grafik — teglar grafigi, u o'tgan darsda o'z SVG bilan yozilgan: +0.3 KB, CSP 0. 8 ta ustun uchun canvas ham, ECharts ham kerak emas.
Ammo jadval saqlanib qoladi. vazifalar ga keyin "kunlik bajarilgan vazifalar, bir yil" (365 nuqta) qo'shilsa — Chart.js (50.6 KB, CSP 0) yoki o'z SVG. 10 000 nuqtali jonli ma'lumot bo'lsa — Chart.js + decimation.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
Error: "category" is not a registered scale. |
CategoryScale ro'yxatdan o'tmagan |
ChartJS.register(CategoryScale, …) |
| decimation yoqildi, grafik baribir sekin | parsing, o'q turi yoki grafik turi mos emas |
shartlar jadvalini tekshiring |
| keskin cho'qqi grafikda yo'q | "har N-nuqta" bilan siyraklashtirilgan | min-max yoki LTTB |
| ECharts qattiq CSP'da chizilmaydi | canvas chizuvchi innerHTML = '' |
renderer: "svg" + renderMode: "richText" |
| lightweight-charts TT'da yiqiladi | logotip innerHTML bilan |
attributionLogo: false + o'z havolangiz |
| ECharts shamlari teskari | OHLC tartibida berilgan | [open, close, low, high] |
chart.addCandlestickSeries is not a function |
4-versiya kodi 5-versiyada | addSeries(CandlestickSeries) |
| Nivo grafigi ko'rinmaydi | Responsive… otasida balandlik yo'q |
otaga height |
| ekran o'quvchi grafikni "o'tkazib yuboradi" | <canvas> nomsiz |
role="img" + aria-label, jadval |
10. Mashqlar
Mashqlar kurs/mashqlar/21/05-grafiklar-ii/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.
1-mashq (oson): bo'sh joylarni to'ldiring
- 50 000 nuqtali chiziq uchun chizish usuli:
- Chart.js'da
linearshkalasi uchun ro'yxatdan o'tadigan klass: - Decimation ishlashi uchun
parsingqiymati: - Sham grafigidagi to'rt son (qisqartma):
- Kanon o'lchovida eng yengil grafik kutubxonasi:
Yechim
- canvas — 50 000 ta SVG elementi sahifani qotiradi, canvas uchun esa bu bitta element.
- LinearScale —
type: "linear"o'q shu klass bilan ishlaydi. - false —
parsing: falsebo'lsa, ma'lumot tayyor{x, y}deb olinadi va plagin uni qisqartira oladi. - OHLC — open, high, low, close.
- visx — +12.2 KB, chunki u faqat shkala va shakl beradi.
2-mashq (o'rta): eng katta o'zgarish
«Sham nimani ko'rsatadi» bo'limidagi weeks massividan foydalanib, komponent yozing:
- Har hafta uchun
<li>:1-h: 14 000 → 14 400 (o'sdi)ko'rinishida. Tushgan bo'lsa —(tushdi). - Ro'yxat ostida
<p>:Eng katta o'zgarish: 3-h—close - openning mutlaq qiymati eng katta hafta.
Ishora: sonni guruhlash — Recharts darsidagi som() g'oyasi (muntazam ifoda), mutlaq qiymat — Math.abs.
Yechim
const weeks = [
{ name: "1-h", open: 14000, high: 14600, low: 13800, close: 14400 },
{ name: "2-h", open: 14400, high: 14500, low: 13900, close: 14000 },
{ name: "3-h", open: 14000, high: 15200, low: 13900, close: 15000 },
{ name: "4-h", open: 15000, high: 15300, low: 14700, close: 14800 },
];
const group = (n) => String(n).replace(/\B(?=(\d{3})+(?!\d))/g, " ");
export default function App() {
const biggest = weeks.reduce((a, b) =>
Math.abs(b.close - b.open) > Math.abs(a.close - a.open) ? b : a,
);
return (
<section>
<ul>
{weeks.map((w) => (
<li key={w.name}>
{`${w.name}: ${group(w.open)} → ${group(w.close)} `}
{w.close >= w.open ? "(o'sdi)" : "(tushdi)"}
</li>
))}
</ul>
<p>Eng katta o'zgarish: {biggest.name}</p>
</section>
);
}Ekranda:
1-h: 14 000 → 14 400 (o'sdi)
2-h: 14 400 → 14 000 (tushdi)
3-h: 14 000 → 15 000 (o'sdi)
4-h: 15 000 → 14 800 (tushdi)
Eng katta o'zgarish: 3-hreduce har qadamda ikkitadan kattasini qoldiradi. Bunday ro'yxat — sham grafigining matn muqobili ham: Malika uchun rang emas, "(o'sdi)" so'zi.
3-mashq (qiyin): savdolardan sham yasash
Birjada narx emas, savdolar keladi: vaqt va narx. Kunlik shamni ulardan o'zingiz yasaysiz. toCandles(trades) funksiyasini yozing: savdolarni kun bo'yicha guruhlab, har kun uchun { day, open, high, low, close } qaytarsin. Ochilish — kunning birinchi savdosi, yopilish — oxirgisi (savdolar vaqt bo'yicha tartiblangan).
const trades = [
{ time: "2026-10-19T09:05", price: 14000 },
{ time: "2026-10-19T12:40", price: 14600 },
{ time: "2026-10-19T17:55", price: 14400 },
{ time: "2026-10-20T09:10", price: 14350 },
{ time: "2026-10-20T15:00", price: 13900 },
];Ishora: kun — time.slice(0, 10); guruhlash — Map.groupBy yoki oddiy obyekt.
Yechim
const trades = [
{ time: "2026-10-19T09:05", price: 14000 },
{ time: "2026-10-19T12:40", price: 14600 },
{ time: "2026-10-19T17:55", price: 14400 },
{ time: "2026-10-20T09:10", price: 14350 },
{ time: "2026-10-20T15:00", price: 13900 },
];
function toCandles(list) {
const byDay = Map.groupBy(list, (t) => t.time.slice(0, 10));
return [...byDay].map(([day, items]) => {
const prices = items.map((t) => t.price);
return {
day,
open: prices[0],
high: Math.max(...prices),
low: Math.min(...prices),
close: prices.at(-1),
};
});
}
for (const c of toCandles(trades)) {
console.log(c.day, c.open, c.high, c.low, c.close);
}Konsolda:
2026-10-19 14000 14600 14000 14400
2026-10-20 14350 14350 13900 1390020-oktabrda savdo ikki marta — eng yuqori ochilishga, eng past yopilishga teng. Bunday shamda yuqori pilik yo'q. Map.groupBy kalitlarni birinchi uchragan tartibda saqlaydi, shuning uchun kunlar tartibi buzilmaydi. Haqiqiy birjada bu hisob serverda bo'ladi — brauzer tayyor shamlarni oladi.
4-mashq: tajriba qadami (ixtiyoriy)
vazifalar ga tegmang. Alohida Vite loyihada (kurs/tajriba/grafik/) Chart.js bilan 10 000 nuqtali chiziq chizing (decimation bilan). Keyin index.html ga vazifalar ning CSP <meta> ini qo'shing va konsolni tekshiring. Xuddi shuni ECharts bilan — avval canvas, keyin SVG chizuvchi bilan — takrorlang.
Yechim
Tekshiruv ro'yxati:
npm i -E chart.js@4.5.1 react-chartjs-2@5.3.1— build'da gzip hajmi React'dan tashqari ~50 KB atrofida (kanon: 50.6).- Decimation: DevTools konsolida
Chart.getChart(document.querySelector("canvas")).data.datasets[0].data.length— 10 000 dan ancha kam. - Chart.js + CSP — konsol toza.
- ECharts canvas + CSP —
This document requires 'TrustedHTML' assignment;renderer: "svg"bilan — toza. Tooltip'ga sichqoncha olib boring: HTML tooltip yana buzadi,renderMode: "richText"— yo'q.
11. Real ishda
- Admin panel va dashboard'lar — Chart.js va ECharts eng ko'p uchraydi. Bir sahifada o'nlab grafik bo'lsa, ECharts'ning og'irligi o'zini oqlaydi.
- Monitoring (server yuklamasi, IoT sensorlar) — minglab nuqta, canvas va decimation. Grafana kabi tizimlar ham shu g'oyada.
- Fintech va birjalar — sham grafiklari, lightweight-charts. O'zbekistonda kripto va fond bozori ilovalari, valyuta kursi sahifalari.
- Media va jurnalistika — noodatiy, bir martalik grafiklar: visx yoki to'g'ridan-to'g'ri d3.
- Intervyu savollari: "SVG va canvas farqi?", "100 000 nuqtani qanday chizasiz?", "canvas grafikni ekran o'quvchiga qanday tushuntirasiz?", "kutubxona CSP bilan ishlaydimi — qanday tekshirasiz?".
Xulosa
- SVG — har shakl DOM elementi: CSS, hodisa, a11y qulay, lekin minglab nuqtada sekin. Canvas — bitta element, tez, lekin matn muqobili sizdan.
- Chart.js + react-chartjs-2: qismlar
registerbilan,ref+getElementAtEventbosish uchun,<canvas>garole="img"vaaria-label. - Katta ma'lumot — decimation (min-max, LTTB); "har N-nuqta" cho'qqilarni yo'qotadi. Chart.js plagini faqat
line,linear/timeo'q vaparsing: falseda ishlaydi. - ECharts — bitta
optionobyekti, eng katta (+167.0 KB); qattiq CSP'da — SVG chizuvchi va richText tooltip. - Nivo — tayyor SVG grafiklar (+81.3), visx — shkala va shakllar (+12.2).
- Sham grafigi — OHLC; lightweight-charts (+51.4):
addSeries(CandlestickSeries), logotip TT'ni buzadi — o'chirsangiz, havolani o'zingiz bering.
Keyingi dars: Xaritalar I: Leaflet va OpenStreetMap — «Bahor» filiallari xaritada: plitkalar qanday yuklanadi, marker, popup, foydalanuvchi joylashuvi va nega Leaflet vazifalar CSP'sida import paytidayoq yiqiladi.
Manbalar
- Chart.js 4: "Integration" (tree-shaking,
register), "Data Decimation", "Accessibility" — chartjs.org/docs; manba kodiplugin_decimation(4.5.1) - react-chartjs-2: "Getting started", "getElementAtEvent" — react-chartjs-2.js.org
- Apache ECharts: "Import ECharts" (
echarts/core), "Canvas vs. SVG",tooltip.renderMode,aria,series-candlestick— echarts.apache.org - Nivo: "Bar" — nivo.rocks; visx: "Scale", "Shape" — airbnb.io/visx
- lightweight-charts 5: "Series types", "Migrating from v4 to v5",
attributionLogo— tradingview.github.io/lightweight-charts - WCAG 2.2: 1.1.1 Non-text Content, 1.4.1 Use of Color
- Kanon:
natijalar/tajriba/kutubxonalar-21.txt,kutubxonalar-21.json,kutubxonalar-21-qisman.txt,tajriba/kutubxonalar/src/k/grafik-*.tsx(2026-10-17)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!