HTML5 Canvas · Canvas API & context
State mantığı: bağlam yığını, save ve restore
CanvasRenderingContext2D tek bir nesne gibi görünse de içeride kalıcı
durum
taşır: dolgu rengi, çizgi kalınlığı, dönüşüm matrisi, kırpma bölgesi, şeffaflık ve birleştirme
modu…
Bir bölümü geçici olarak değiştirip sonra eski haline dönmek istediğinizde her özelliği tek tek
geri
yazmak hata üretir. Tarayıcının sunduğu çözüm save() ve restore() ile
yığın tabanlı anlık görüntü saklamaktır — bu sayfa o yığını, olası hataları ve
diğer başlıklarla sınırını canvas üreticisi gözüyle anlatır.
Koordinat ve matris Koordinat sistemi sayfasında; boyut değişince bağlamın sıfırlandığı senaryo Resize mantığı ile kesişir. Path ve dolgu kuralları Path sistemi ve Stroke vs fill başlıklarında; burada yalnızca «hangi stil hangi dalda kaldı?» sorusuna odaklanırız.
Özet: bağlam yığınının ana hatları
| Kavram | İşlev | Dikkat |
|---|---|---|
save() |
O anki bağlam durumunu yığına iter | Her dal için dengeleyici restore |
restore() |
Son kaydı geri yükler (LIFO) | Fazla restore sessiz hata üretebilir |
| Yığın derinliği | Birden fazla iç içe katman | HUD / modal / mini-sahne desenleri |
clip() |
Kırpma mevcut path’ten | Kırpma da save ile korunur |
Kalıcı durum: tek ctx, çok görünüm
2D context sayfasında geçmişti:
fillStyle değişince sonraki tüm dolgular yeni renk kullanır. Bu «tek kalemlik»
modeli hızlıdır; fakat aynı kare içinde biri koyu arka plan, biri yarı saydam ön plan çizmek
istediğinizde renkleri elle sıfırlamak unutulmaya açıktır. save/
restore, bu geçişi otomatik ve sıra güvenli yapar: bir dal
açıp
stil seti uygularsınız, dalı kapatınca önceki set geri gelir.
Zihinsel çerçeve: bağlamı bir «kurulu makine» gibi düşünün — düğmeler stil özellikleri,
dişliler dönüşüm matrisi, cam kırpma alanı. save anlık fotoğraf çeker,
restore makineyi o fotoğraftaki ayarlara döndürür. Fotoğraf sayısı yığın
derinliğidir; çekilen fotoğraf yokken restore çağrısı tarayıcıda genelde
yığından çıkaracak kayıt kalmadığı için işlem etkisiz kalır veya uygulamanız yığın hatasına
düşer — üretimde çift sayım ve kod gözden geçirme şarttır.
Ne kaydedilir, ne kaydedilmez?
Spesifikasyona göre yığına girenler arasında dönüşüm matrisi, kırpma, kompozit ayarları,
gölgeler,
çizgi ve metin stilleri yer alır; path nesnesi (geçerli alt yol tanımı)
kaydın parçası değildir — beginPath ile kurduğunuz yol, save’ten önce veya
sonra
dalınıza göre yönetilir. Pratik kural: clip yapmadan önce path’i kapatıp
save düşünün; path komutları
Path
sistemi
sayfasında detaylanır.
Piksel verisi (bitmap içeriği) yığına girmez; save çizilmiş görüntüyü geri
almaz,
yalnızca çizim ayarlarını saklar. Temizlemek için clearRect
veya
yeniden boyama
Clear
&
redraw düşüncesindedir.
save ve restore: LIFO sözleşmesi
save() çağrısı yığına bir anlık bağlam görüntüsü iter.
restore() en üstteki kaydı çıkarır ve bağlamı o duruma döndürür — bu nedenle
LIFO (son giren ilk çıkar) düşüncesi şarttır. İç içe üç seviye UI
çiziyorsanız üç çift çağrı beklenir: save → … → restore en içteki dal için,
dışa doğru genişleyen kayıtlar üst üste.
function drawPanel(ctx, x, y) {
ctx.save();
ctx.translate(x, y);
ctx.fillStyle = 'rgba(18, 24, 34, 0.92)';
ctx.strokeStyle = '#2ee7f2';
ctx.lineWidth = 2;
ctx.fillRect(0, 0, 220, 120);
ctx.strokeRect(0, 0, 220, 120);
ctx.restore();
// restore sonrası fillStyle, strokeStyle, lineWidth, translate önceki kareye döner
}
Çift sayım disiplini
Üretim kodunda save açtığınız her yolda erken dönüş olsa bile
restore çağrılmasını garanti eden bir yapı tercih edin — örneğin
try/finally veya tek sorumluluklu küçük çizim fonksiyonları. İç
fonksiyon hata fırlatırsa yığın dengesiz kalabilir; sonraki çizimler beklenmedik stilde
devam eder — bu sınıf hataları koddaki tüm save sayısını gözle sayarak bulmak
gerekir.
Context State Stack Visualizer
ile save/restore yığınını, canlı ctx durumunu ve «restore
unut» sızıntısını deneyin.
save() yığına bir frame
ekler; restore() LIFO ile geri alır. «restore unut» ile stil ve dönüşüm
birikir — redraw ile dünya katmanına sızıntıyı görün. Stil komutları için
2D Context
Playground.
İç içe katmanlar: dünya, panel, ipucu
Oyunlarda sık desen: önce dünya için translate(-camera) + dünya çizimi,
ardından
save ile HUD koordinatına geçiş — böylece dünya dönüşümü HUD’u kirletmez.
İkinci save ile küçük bir tooltip çizilir; her sekme kendi restore
ile kapanır. Derinlik, yığındaki kayıt sayısı kadardır; tarayıcılar makul bir üst sınır
uygular, aşırı iç içe çağrıdan kaçının.
Metin çizimi için font, textAlign, textBaseline da
bağlam durumudur; HUD’da merkez hizalı, dünyada sol hizalı yazı aynı karede çakışmasın diye
bu özellikleri dal içinde set edip restore ile temizlemek doğal eşleşmedir.
function drawHudTooltip(ctx, px, py, text) {
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0); // dünya transformundan sıyır
ctx.font = '600 14px DM Sans, sans-serif';
ctx.save();
ctx.translate(px, py);
const w = ctx.measureText(text).width + 16;
ctx.fillStyle = 'rgba(232, 244, 255, 0.95)';
ctx.fillRect(-w / 2, -32, w, 28);
ctx.fillStyle = '#121820';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(text, 0, -18);
ctx.restore();
ctx.restore();
}
clip: kırpma bölgesi ve yığın
clip() (veya clip(path, fillRule)) mevcut alt yolu kırpma maskesi
olarak uygular; sonraki çizimler yalnızca bu kesişim içinde görünür. Kırpma bölgesi de
bağlam
durumundadır — save öncesi ve sonrası farklı maskeler oluşturmak için
kullanılır.
Path’i nasıl kuracağınız
Path
sistemi
konusudur; burada sadece «maskeli dal aç, çiz, dalı kapat» akışı önemlidir.
function drawInCircle(ctx, cx, cy, r, drawContents) {
ctx.save();
ctx.beginPath();
ctx.arc(cx, cy, r, 0, Math.PI * 2);
ctx.clip();
drawContents(ctx); // yalnızca daire içi çizilir
ctx.restore();
}
globalCompositeOperation ve globalAlpha
globalAlpha ve globalCompositeOperation de kalıcı bağlam
özellikleridir
— tek efekt için ayarlayıp unutursanız sonraki tüm çizimler soluk veya yanlış blend modunda
kalır. save/restore bu «sızıntıyı» kesen en pratik araçtır. Tam
mod
tablosu ve örnekler
Composite
operations sayfasındadır; burada yalnızca yığınla birlikte düşünmek yeterli.
globalAlpha, o anda çizilen her şeye çarpan bir üst sınırdır: dolgu ve çizgi
renklerindeki saydamlıkla birlikte işlenir; yarım bırakılan alfa, metin ve sprite’ları aynı
ölçüde soluk gösterir. Bileşik işlem modu da kare boyunca kalır — bu yüzden «önce normal
dünya, sonra bir katmanda özel blend, sonra tekrar varsayılan» düzeninde her özel katman
kendi
save/restore çiftine sığdırılmalıdır. Üst üste bindirme sırasını
değiştirmek istemiyorsanız, zayıf alfa ile UI üzerinde deneme yapmayın; dalı kapatıp çıktıyı
kontrol edin.
Parlama veya «ekle» (lighter) efektini tek sprite için kullanıp ardından
source-over dönmeniz gerekiyorsa bunu dal içinde yapın; aksi halde partikül
sisteminiz tüm sonraki UI katmanını bozar. Aynı desen, geçici olarak koyulaştırma veya
silikleştirme için düşük globalAlpha kullandığınız mini sahneler için de
geçerli:
efektli dal bittikten sonra ana bağlamın opak ve normal bileşimi kaldığından emin olun.
Gölge (shadowBlur, shadowColor …) bağlamın parçasıdır; alfa veya
bileşik mod değişince gölge izi de beklenmedik görünebilir. Özel efekt + gölge
kombinasyonunu
yine tek dalda toplayın; karmaşık birleşimleri dokümante edilmiş yardımcı fonksiyonlara
bölün ki çağıran kod tek satırda «temiz bağlam» varsayabilsin.
function drawAdditivePass(ctx, drawPass) {
ctx.save();
ctx.globalCompositeOperation = 'lighter';
ctx.globalAlpha = 0.55;
drawPass(ctx);
ctx.restore();
// restore sonrası: source-over ve çağıranın alfa / işlem modu
}
Dönüşüm ve save: aynı sahnede birlikte
translate / rotate / scale matrisi yığında saklanır.
Bir nesneyi yerel uzayda çizip sonra dünya uzayına dönmek için
Koordinat sistemi
sayfasındaki translate → çiz → restore kalıbı ile örtüşür.
setTransform
veya resetTransform kullanıyorsanız save, o anki matrisi de paketler — kare
başı
sıfırlama stratejisi
koordinat
sayfasında köprülenmiştir; bu başlıkta odak, «dal içinde serbest dönüş, dal dışında
temiz eksen» disiplinidir.
save() tek anlık görüntüdedir: o anda hem dönüşüm matrisi hem dolgu çizgisi hem
kırpma hem de §5’teki alfa ve bileşik
mod aynı kayda girer. Bu yüzden bir varlığı döndürüp çizmek, ardından dünyayı «eskisi gibi»
sürdürmek için çoğunlukla yalnızca bir çift çağrı yeterli — araya sızıp kalan
rotate kümülatif birikiminden kaçınmış olursunuz. restore
atlaması, sahneyi giderek kaydıran koordinat hatalarının en sık nedenidir.
Yerel uzay dalı ve kümülatif dönüşüm tuzağı
Aynı ctx üzerinde ardışık translate/rotate çağrıları
matrisi çarpar; her adım bir öncekine eklenir. Bir bileşen kendi merkezinde çiziliyorsa
desen şudur: save → öğe için translate/rotate → çizim
→ restore. Böylece bir sonraki öge «temiz» bir üst düzlem transformu ile
başlar. Çoklu ögeyi tek dalda üst üste yığmak yerine, öge başına dal kullanmak hata yüzeyini
daraltır — özellikle animasyon veya dinamik pivotlarda.
Dünya kamerası (ör. translate(-camX, -camY)) genelde karenin büyük dalında,
tek bir varlık ise iç içe daha küçük bir dalda ele alınır; iç dal bittiğinde dış dalın
kamera dönüşümü korunur. §3’teki HUD
örneğinde olduğu gibi, ekran piksel uzayına sıçramak için bazen önce iç
restore, sonra setTransform(1,0,0,1,0,0) ile tam sıfırlama gerekir
— hangi dalın neyi taşıdığını anlamlı isimlerle ayrılmış fonksiyonlara bölmek, yığını akılda
tutmayı kolaylaştırır.
function drawIconAt(ctx, worldX, worldY, angleRad, drawIconLocal) {
ctx.save();
ctx.translate(worldX, worldY);
ctx.rotate(angleRad);
// drawIconLocal: (0,0) ikon merkezi veya sol üst sözleşmesine göre
drawIconLocal(ctx);
ctx.restore();
}
Anti-kalıplar ve hata ayıklama
Eşleşmeyen restore: Bir save eksik veya fazla
restore yığını deler. Üç şüpheli belirti: beklenmedik
globalAlpha, kaymış orijin, kaybolan kırpma. Geçici çözüm: kare başında
setTransform(1,0,0,1,0,0) + tam stil kurulumu (ancak kök nedeni yığını
dengelemek); kalıcı çözüm: save/restore çiftlerini fonksiyon
sınırlarına kilitlemek.
Stil sızıntısı: Kütüphane çağrısı içinde fillStyle değiştirip
geri yazmayan kod, sahneyi kirletir. Her modül kendi
save/restore’unu
sarmalı veya dokümantasyonla «bağlamı değiştirmez» taahhüdü vermelidir.
- İç içe
savesayısı = beklenenrestoresayısı mı? - Dal içinde
returnolsa bilerestoregaranti mi? - Üçüncü parti çizim öncesi/sonrası bağlam «temiz» mi?
Resize: yığın yetmez, bitmap yenilenir
Resize mantığı sayfasında geçmişti:
canvas.width / height atandığında yığın da dahil
tüm
bağlam durumu sıfırlanır — bu, save ile saklanmış hayali bir geri dönüş yoktur.
Boyut değiştikten sonra yığın derinliği anlamını yitirir; uygulama genelde
applyCanvasDefaults ve tam sahne yeniden çizimi yapar. State yönetimi resize
sonrasında «önceki karenin yığını»nı kurtarmaz; yeni karedir.
Bu sayfanın sınırı
Animasyon döngüsünün kendisi (requestAnimationFrame, güncelle/çiz ayrımı)
ayrı klasördedir. Bağlam yığını yalnızca tek çizim geçişindeki dal
yönetimidir; oyun durumu veya sahne grafik düğümleri
Oyun
durumu
gibi konularda modellenir — canvas state ile karıştırmayın.
Sıradaki: path ve şekil çizimi
Stil ve dönüşüm dallarını güvenceye aldıktan sonra geometri komutlarına derinlemesine geçmek için Path sistemi sayfasına devam edebilirsiniz. Önceki adımlar: Resize mantığı, Koordinat sistemi.