HTML5 Canvas · Girdi & etkileşim
Klavye girdisi: tuval odağı ve klavye olayları
Canvas öğesi, görünür bir çizim yüzeyi olduğu hâlde varsayılan olarak
klavye odağı almaz; tarayıcı tuş olaylarını yalnızca odakta olan öğeye veya
yakalama (
capture ) aşamasında dinleyiciye iletir. Basit bir çizim uygulamasında
ok tuşlarıyla imleci kaydırmak, space ile pano açmak veya kısayollarla
araç değiştirmek istiyorsanız önce tuvalin ( veya sarmalayıcısının ) klavye için odaklanabilir
olduğundan emin olmalı, ardından keydown / keyup sözleşmesini doğru
hedefte dinlemelisiniz — bu sayfa modeli Canvas 2D etkileşimine göre kurulur;
fare ve tekerleğin ayrıntısı
Fare girdisi ve
Pointer sistemi komşu sayfalarında
tutulur.
Hareket vektörünü her tuş vuruşunda değil, requestAnimationFrame ile birleştirmek için Update vs render ve Delta time sayfalarına köprü kurulur; böylece tuşlar oyun mantığında «durum», çizim ise «kare başına bir özet» kalır.
Özet: sık klavye olayları
| Olay | Ne zaman? | Canvas’ta tipik kullanım |
|---|---|---|
keydown |
Tuşa basıldı; basılı tutunca tekrar ( repeat ) ile de gelebilir | Anlık eylem, basılı tutma başlangıcı, kısayol |
keyup |
Tuş bırakıldı | Basılı kümeden çıkarma, sürüklemeyi bitirme |
keypress |
Deprecated — yazdırılabilir tuş odaklı eski model | Yeni kodda kullanmayın; keydown tercih edin |
Odak ve tuval hedefi: tabindex, click-to-focus, görünür geri bildirim
Çoğu tarayıcıda <canvas> öğesi varsayılan olarak klavye odağına
aday değildir. Kullanıcı tuşa bastığında sizin dinleyicinizin çalışması için ya öğenin
tabindex="0" ( veya pozitif stratejik sıra ) ile tab sırasına alınması ya da
programlı focus() çağrısı gerekir — aksi halde olaylar belgedeki başka bir
odakta (
örn. adres çubuğu, form, düğme ) tüketilir. Tuvali tıklayınca odaklamak yaygın bir desendir;
erişilebilirlik için odak halkasını (
focus ring ) CSS ile tamamen yok etmek yerine, tuvalin etrafında
hafif
bir çerçeve veya üst başlıkta «klavye kısayolları için tıklayın» ipucu sunmak daha güvenli
olur.
focusin / focusout ile hangi panelin aktif olduğunu izleyebilir,
yalnız tuval odaktayken hareket veya kısayol işleyebilirsiniz. Bu, sayfa üzerinde metin
girişi alanı varken kazara ok tuşlarının tuvali kaydırması gibi çakışmaları azaltır. Ağır
iş yükünü doğrudan keydown içinde değil, aşağıda anlatıldığı gibi durum
önbelleğine yazıp kare döngüsünde değerlendirmek daha sürdürülebilirdir.
/**
* Tuvali klavye odağına aday yapar; tıklayınca odaklar.
* dispose: sayfa/komponent kapanırken çağrılmalıdır.
*/
function attachCanvasKeyboardFocus(canvas) {
if (!canvas.hasAttribute('tabindex')) canvas.tabIndex = 0;
const ac = new AbortController();
const { signal } = ac;
canvas.addEventListener(
'pointerdown',
() => {
canvas.focus({ preventScroll: true });
},
{ signal },
);
return () => ac.abort();
}
code, key ve repeat: fiziksel düzenden bağımsız okuma
KeyboardEvent.code (
örn. KeyW, ArrowLeft, Space ) fiziksel veya
konumsal tuşu tarif eder; klavye düzeni (
QWERTY / AZERTY ) değişse bile aynı fiziksel
tuş aynı code ile gelme eğilimindedir — bu yüzden yön ve hareket mantığında
code tercih etmek daha az sürprizlidir. KeyboardEvent.key ise
kullanıcıya «ne karakter üretildi?» sorusunun cevabıdır; büyük harf, ölü tuş (
dead keys ) ve IME altında farklılaşır.
e.repeat === true otomatik tekrar (
tuşa uzun basınca işletim sisteminin ürettiği tekrar ) demektir; tek atımlık eylemlerde (
sil, kaydet, araç değiştir ) bu bayrağı filtrelemek çift tetiklemeyi önler. Oyunlarda ise
sürekli hareket için keydown ile «basılı» işaretleyip keyup ile
temizlemek, tekrar olaylarına güvenmekten daha deterministiktir.
shiftKey, ctrlKey, altKey, metaKey
bayrakları fare olaylarıyla aynı ailedendir; kısayol seçerken macOS
üzerinde metaKey, Windows üzerinde sıklıkla
ctrlKey beklenir — kullanıcıya metin olarak «Ctrl/⌘+Z» gibi ifadelerle
duyurmak, kodda ise her iki modify bayrağını kabul etmek genelde doğru yaklaşımdır.
Basılı tuş durumu: Set, blur ve sekmeyi kaybetme
Kare tabanlı canvas uygulamalarında tuşları «basıldı mı?» biçiminde modellemek için küçük
bir Set<string> (
anahtar olarak e.code ) yaygındır. keydown içinde
add, keyup içinde delete — hareket vektörünü her
karede bu kümeden türetin. Böylece otomatik tekrar hızına bağlı kalmaz ve aynı karede hem
«sol» hem «yukarı» basılı olabilir.
Kullanıcı sekmeyi değiştirirken (
blur ) veya görünürlük düşerken (
document.visibilityState ) basılı kümenin sıfırlanması gerekir; aksi halde
«odak
yokken bile sanal olarak sürekli basılı tuş» sapması görülür. Tam ekran çıkışı veya tarayıcı
geliştirici araçlarını açmak da benzer şekilde tuşların «asılı» kalmasına yol açabilir —
bu yüzden yedek olarak window düzeyinde blur dinleyip küme
temizliği veya en azından tüm «hareket» bayraklarını düşürmek iyi bir sigortadır.
/**
* hedef: odaklanabilir öğe (canvas veya sarmalayıcı).
* getKeys(): okuma için canlı Set referansı (dışarı kopyalamayın).
*/
function attachKeyboardHeldState(hedef, options = {}) {
const keys = new Set();
const ac = new AbortController();
const { signal } = ac;
const clearAll = options.onClearAll ?? (() => keys.clear());
hedef.addEventListener(
'keydown',
(e) => {
keys.add(e.code);
},
{ signal },
);
hedef.addEventListener(
'keyup',
(e) => {
keys.delete(e.code);
},
{ signal },
);
window.addEventListener('blur', clearAll, { signal });
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') clearAll();
}, { signal });
return {
dispose: () => ac.abort(),
getKeys: () => keys,
};
}
preventDefault ve tarayıcı kısayollarıyla yarış
Ara boşluk ile sayfa kaydırması, Ctrl+R ile
yenileme gibi davranışlar tarayıcıya aittir; preventDefault() çağırmak bu
zinciri
kesebilir — fakat kullanıcı güveni ve erişilebilirlik için yalnız gerçekten tüketmek
istediğiniz kombinasyonlarda, ve tuvale odak varken çağırmalısınız. Tüm
keydown olaylarını koşulsuz iptal etmek hem özgür gezinmeyi bozar hem de ürünün
güven vermesini zayıflatır.
Tam ekran veya imleç kilidi (
pointer lock ) senaryolarında tarayıcı politikası daha katıdır; bu
sayfa
gömülü canvas aracı varsayar ve bu API’lere girmez. Yine de ok tuşlarıyla oyun içi menü
kaydırırken sayfanın da kaymamasını istiyorsanız, hedeflenen yön tuşlarında
preventDefault düşünün — sonra geri almayı test edin (
özellikle mobil dış kulaklık ve ekran okuyucu ile ).
Kare döngüsü ile birleştirme: tuşlar güncelleme, canvas çizim
Tuş olayları imleçten daha seyrek de olsa ana iş parçacığında (
main thread ) çalışır; her keydown’da tüm sahneyi
yeniden çizmek yerine, yalnız basılı küme veya ivme vektörünü güncelleyip
requestAnimationFrame döngüsünde tek çizim yapın (
requestAnimationFrame ). Bu ayrım,
Fare girdisi · yoğun
mousemove bölümündeki rAF birleştirme ile aynı felsefedir: olay yolu ince,
kare yolu kalın.
Dünya uzayında saniye başına sabit hız istiyorsanız basılı süreyi Delta time ile çarpın; yalnız kare başına sabit adım istiyorsanız tuş durumunu okuyup her karede eşit delta uygulayın — ikisini karıştırmamak stil meselesi değil, ölçek hatası meselesidir.
Anti-kalıplar: odağı unutmak ve IME sınırı
Odaksız dinleyici: document üzerinde dinleyip tuvalin aslında
odak almadığını varsaymak; kullanıcı form doldururken veya arama kutusundayken beklenmedik
kısayollar tetiklenir. Ya hedefe bağlayın ya da global dinleyicide açıkça «aktif mod»
bayrağı taşıyın.
keypress ve karakter odaklı model: Yeni kodda
keydown/keyup ile code/key kullanın;
metin düzenleyici yazmak için contenteditable veya <input>
ayrı tutulmalıdır — canvas üzerindeki daktilo deneyimi doğrudan klavye olaylarından yapı
makul değildir.
Tekrar olayına güvenmek: Basılı hareket için yalnız e.repeat
veya yalnız ilk keydown kullanmak, kenar durumlarında sapma üretir; küme
tabanlı
basılı durum daha güvenilirdir.
Kümenin temizlenmemesi: Sekme değişimi veya tam ekrandan çıkış sonrası
hayalet tuşlar; yukarıdaki blur / visibilitychange sigortası
şarttır.
Bu sayfanın sınırı
IME ile kompozisyon (
compositionstart / compositionend ) ve CJK metin girişi bu
sayfada ayrıntılandırılmaz; çok dilli metin yazan bir canvas arayüzü tasarlıyorsanız
önce uygun form kontrollerini kullanmayı değerlendirin. Oyun kumandası için
Gamepad API ayrı bir konudur.
Sürükle-bırak ve imleç yakalama desenleri Sürükleme mantığı ile birleştirilir; fare düğmeleri ve teker için Fare girdisi sayfasına dönün.
- Tuval veya sarmalayıcı
tabindex/ tıklanınca odak akışı tanımlı mı? - Hareket ve karakter ayrımında
codevekeydoğru seçildi mi? - Basılı küme
keyup,blurve gizlenmede temizleniyor mu? preventDefaultyalnız gerektiği kadar ve odak varken mi çağrılıyor?- Ağır çizim rAF ile ayrıldı mı; delta time ile uyumlu mu?