HTML5 Canvas · Girdi & etkileşim
Fare girdisi: canvas üzerinde mouse olayları
Canvas öğesi, DOM ağacında düğüm olduğu sürece çoğu klasik fare olayı için hedef olabilir: taşınma, basma, bırakma, tekerlek ve tıklama zinciri. Önemli olan, olay nesnesinden okunan koordinatların ekran veya CSS düzeni uzayında olduğu, piksel çizmek için ise canvas bağlam koordinatlarına dönüştürmeniz gerektiğidir — bu dönüşümün ayrıntısı Event koordinatları sayfasında; burada olay türleri, düğme modeli, dinleyici güvenliği ve tuvale özgü düşünce tarzı ön plandadır.
Birleştirilmiş çizim ve kirli bayrak desenleri Update vs render ile; sürekli sürükleme uygulaması Sürükleme mantığı ile — burada çapraz platform için de temel olan Pointer Events öncesi klasik mouse modeli ele alınır ( Pointer sistemi köprü).
Özet: sık mouse olayları
| Olay | Ne zaman? | Canvas’ta tipik kullanım |
|---|---|---|
mousemove |
İşaretçi öğe üzerindeyken hareket | Fırça, hover seçim, okuma çubuğu |
mousedown / mouseup |
Düğme basıldı / bırakıldı | Araç seçimi, geçici ön izleme |
click |
Aynı öğede bas-bırak tamamlandı | Düğme, hücre seçimi (basit) |
wheel |
Tekerlek / dokunmatik yatay iki parmak | Yakınlaştırma, kaydırma |
mouseenter / mouseleave |
Öğe sınırı geçildi | HUD vurgusu, imleç ipuçları |
Olay ailesi: hedef, kabarcıklanma ve çift tıklama
Fare olayları çoğu zaman kabarcıklanır (
bubble ): canvas üzerindeki mousedown önce
tuvalde tetiklenir, sonra ata öğelere doğru yükselir. Olayı yalnız tuvalde işlemek
istiyorsanız
stopPropagation düşünün — fakat form veya üst katman overlay ile çakışma
yaratmazsanız genelde bırakmak daha az sürprizlidir.
click, aynı öğe üzerinde uyumlu bas-bırak sonrası üretilir; ara taşımalarda
basma bir alt öğede, bırakma tuvalde olursa click beklediğiniz gibi
gelmeyebilir —
sürükle-bırak ve hassas seçim için çoğu zaman mousedown/
mouseup çifti veya
Pointer sistemi tercih edilir.
dblclick tarayıcı ve işletim sistemi gecikme ayarlarına duyarlıdır; oyun veya
çizim aracında çift tıklama ile tek tıklama çakışmasını önlemek için zaman eşiği veya tool
modu şarttır. Mobil cihazlarda çift dokunma jesti farklı davranabilir — canvas hedefli
ürünlerde dokunmatik yolu göz önünde bulundurun.
Düğmeler ve modify tuşları: button ve buttons
MouseEvent.button hangi fiziksel düğmenin bu olayı tetiklediğini
sayar: genelde sol 0, orta 1, sağ 2 — fakat solak sistem ayarı veya cihaz sürücüsü sapma
gösterebilir. buttons ise o an basılı tutulan düğmelerin
bit maskesidir; hareket sırasında sol basılıyken sağ da basılırsa maske güncellenir.
shiftKey, ctrlKey, altKey, metaKey ile
kısayol semantiği kurulur; örneğin şekil çiziminde shift ile eksen
kilidi yaygındır. Bu bayraklar klavye olayı değildir — yine de tuval odağı olmadan
okunabilir;
kısayol çatışması yaşamamak için tarayıcı veya işletim sistemi rezervlerini gözden geçirin.
Sağ tık bağlam menüsü için contextmenu olayını dinleyip
preventDefault ile yerel menüyü bastırabilirsiniz; kullanıcı arayüzü
erişilebilirlik
açısından alternatif menü yolu sunmak iyi pratiktir.
const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };
function primaryAction(e) {
return e.button === MOUSE.LEFT;
}
function modifiers(e) {
return {
shift: e.shiftKey,
ctrl: e.ctrlKey,
alt: e.altKey,
meta: e.metaKey,
};
}
Koordinat dönüşümü köprüsü: client’tan tuval pikseline
clientX / clientY görünüm penceresine göre verilir; CSS ile
ölçeklenmiş veya
object-fit ile sığdırılmış canvas’ta iç piksel ızgarası ile
bire bir örtüşmez. Üretimde getBoundingClientRect() ile görünür dikdörtgen
okunur, iç width/height ile ölçek oranı kurulur — tam türetim,
kenar durumları ve devicePixelRatio ilişkisi
Event koordinatları ve
Resize
mantığı ile sınırlı örtüşür; burada yalnızca «mouse olayı → sayı → çizim argümanı»
zincirinin canvas üreticisi için zorunlu olduğu vurgulanır.
Aynı anda birden fazla canvas veya dönüşüm matrisi (
ctx.translate …) ile çalışıyorsanız, olayı dünya uzayına taşıma tek yerde
toplanmalıdır — aksi halde hata ayıklamada hangi katmanda kaydığınızı bulmak zorlaşır.
Dokunmatik cihazlarda fare olayları sentetik olarak üretilebilir veya gecikmeli olabilir; çapraz cihaz hedefliyorsanız uzun vadede Pointer sistemi tercih edilir — bu sayfa klasik mouse sözleşmesini sabitler.
/**
* CSS ölçekli canvas → dahili piksel koordinatı (yaklaşım; edge-case Event koordinatları sayfasında).
*/
function clientPointToCanvas(canvas, clientX, clientY) {
const r = canvas.getBoundingClientRect();
const sx = canvas.width / r.width;
const sy = canvas.height / r.height;
return { x: (clientX - r.left) * sx, y: (clientY - r.top) * sy };
}
Dinleyici bağlama: passive, capture ve temizlik
canvas.addEventListener('mousemove', handler) en yaygın yoldur;
capture: true nadiren gerektirir — üst katmanlar olayı yutmadan önce yakalamak
istediğinizde kullanılır. Kaldırma için aynı fonksiyon referansıyla
removeEventListener şarttır; anonim ok fonksiyonları tek seferlik bağlantı için
uygundur fakat sonra kaldıramazsınız.
Modern tarayıcılarda AbortController ile { signal } geçmek,
tek satırda toplu temizlik sağlar: sinyal iptal edildiğinde tüm dinleyiciler düşer — React /
Vue cleanup aşamasında özellikle kullanışlıdır.
wheel ile preventDefault (sayfa kaydırmayı engellemek için)
çağıracaksanız dinleyiciyi { passive: false } ile kaydetmelisiniz — aksi halde
uyarı veya yok sayılma ile karşılaşırsınız. Bu, kaydırma hissi ve erişilebilirlik için
dikkatle
kullanılmalıdır; tüm sayfayı tuvalde kilitlemek kullanıcıyı rahatsız edebilir.
/**
* canvas üzerinde mouse dinleyicilerini signal ile bağlar; dispose'da AbortController.abort().
*/
function attachMouse(canvas, handlers) {
const ac = new AbortController();
const opt = { signal: ac.signal };
for (const [type, fn] of Object.entries(handlers)) {
canvas.addEventListener(type, fn, opt);
}
return () => ac.abort();
}
Odak, imleç ve CSS: pointer-events ile üst öğeler
Canvas üzerinde şeffaf bölgelerde fare «görünmez» öğelere düşebilir — üstte
position: absolute bir katman varsa olaylar tuvali atlar. Lekeli cam etkisi
istiyorsanız üst katmanda pointer-events: none vererek olayları alta
iletirsiniz;
yalnız belirli düğmeler tıklanacaksa alt tuval + üst interaktif HTML karışımı bilinçli
tasarlanır.
tabindex ile tuval odaklanabilir yapılabilir; klavye olayları için gereklidir (
Klavye girdisi ). Fare tek
başına çoğu zaman odağı taşımaz — tıklama ile çizim başlatırken odak davranışını
gözlemleyin;
tarayıcı önceden seçili öğeyi etkileyebilir.
Özel imleç ( cursor: crosshair vb.) CSS ile atanırsa, araç değişiminde sınıf değiştirmek canvas içi mantığı okumayı kolaylaştırır — imleç stilini sık sık bağlam özelliğinden değiştirmek yerine kapsayıcı üzerinde tutmak sızıntı riskini azaltır.
Yoğun mousemove ve kare ile birleştirme
mousemove saniyede yüzlerce kez tetiklenebilir; her seferinde tam sahne çizmek
CPU’yu yakar. Üretim deseni: olayda yalnız durumu veya ara koordinatı güncelle, çizimi
requestAnimationFrame içinde tekilleştir (
requestAnimationFrame ). Böylece kare başına en fazla bir
kompozit geçiş yapılır; imleç örnekleme oranı ekran yenilemesinden hızlı olsa bile piksel
yolu tıkanmaz.
Kirli bayrak (
Update vs render · kirli bayrak ) ile birlikte
düşünülebilir:
yalnız imleç hareket ettiyse ve görsel gerçekten güncellenecekse
requestAnimationFrame
planlayın; statik sahnede gereksiz plan iptalleri de bellek tahsisini tetikleyebilir —
«planlı
kimlik var mı?» kontrolü basit bir bayrakla çözülür.
Global window üzerinde mousemove dinlemek, tuval dışında da
ateşlenir; sürüklerken tuval dışına çıkıldığında hala koordinat almak için bazen gereklidir
—
fakat mutlaka mouseup (
ve mümkünse mouseleave / blur yedekleri )
ile eşleştirerek serbest bırakıldığında dinleyiciyi kaldırın, aksi halde bellekte ve CPU’da
kalıcı dinleyici kalır. Oturum lost pointer capture benzeri kenar
durumları için
Sürükleme mantığı sayfası derinleşir;
burada yalnızca «global dinleyici = geçici kira» kuralı hatırlatılır.
Throttle ( lodash throttle vb.) alternatiftir; sabit ms ile üst sınır koyar ve düşük güçlü cihazlarda iş kuyruğunu budar. Fakat tarayıcı zaten ekran hızına göre boyar — çoğu canvas etkileşiminde rAF birleştirme daha az sürprizlidir çünkü görsel çıktı zaten kare tabanlıdır; throttle’ı ağ isteği veya DOM ölçümü gibi rAF dışı yan işlerle birlikte değerlendirin.
Aynı turda hem olay hem rAF içinde ağır hesap yapmak kazancı yerler: olay yolunda yalnız
koordinat ve hafif bayraklar; yoğun geometri ve drawImage yığınları kare
planlı blokta kalmalıdır.
/**
* toCanvas: örn. clientPointToCanvas. render: tek karede çizim.
* Aynı karede birden fazla mousemove tek render çağrısında birleşir.
*/
function attachMoveCoalesced(canvas, toCanvas, render) {
const ac = new AbortController();
let rafId = 0;
let latest = null;
function flush() {
rafId = 0;
if (!latest) return;
render(latest);
}
canvas.addEventListener(
'mousemove',
(e) => {
latest = toCanvas(canvas, e.clientX, e.clientY);
if (!rafId) rafId = requestAnimationFrame(flush);
},
{ signal: ac.signal },
);
return () => {
cancelAnimationFrame(rafId);
ac.abort();
};
}
Anti-kalıplar: koordinat ve dinleyici sızıntısı
offsetX doğrudan piksel sanması: offsetX /
offsetY
bazen tüval piksellerine denk gelmemekle birlikte — özellikle CSS transform /
object-fit, iç içe ofsetli üst öğeler veya yüksek DPR — kullanıcılar tarafından
«tek
satırlık» kolaylık sanılır. Canvas üzerinde çizim koordinatı için
getBoundingClientRect + iç boyut oranı (
Client → canvas köprüsü ) tek
kaynak olmalı; aynı formülün farklı olaylarda kopyalanması en küçük sabit farkla metreler
sapma üretir. Evrensel koordinat terimi için
Event koordinatları sayfasına
dönün.
window mousemove kalıntısı: Sürüklerken document /
window dinlemek makuldür; fakat mouseup ( ve sekme kaybı için
blur ) sonrası removeEventListener veya
AbortController ile tamamen sökülmezse, kullanıcı artık sürüklemese bile her
imleç hareketi rAF, çarpışma veya günlük yazar. Bu kalıntı CPU ve pil profiliyle değil,
«neden boşta %5 CPU?» ile ortaya çıkar. Geçici kira desenini
Yoğun mousemove ve kare ile birleştirme
ve
Sürükleme mantığı ile hizalayın.
passive wheel: Birçok yığın wheel dinleyicisini varsayılan
olarak
passive: true kaydeder; bu durumda zoom / pan için
preventDefault() sessizce işe yaramaz ve sayfa kaydırması ile tuval zoom’u aynı
jestte yarışır. Zoom’u tuvalde kilitlemek istiyorsanız açıkça
passive: false istemeli ve gerçekten iptal edecekseniz çağırmalısınız — aksi
halde ana iş parçacığını gereksiz yere bloke edersiniz. Ayrıntı
Dinleyici yaşam döngüsü içindeydi.
button ile buttons karışması: mousedown / click /
contextmenu gibi olaylarda tetikleyici düğme genelde net okunur; oysa
mousemove ve mouseenter akışında buttons bit kümesi,
button alanından farklı semantik tutabilir ( tarayıcı ve platform farkları
dahil ). Çok tuşlu sürükleme, orta teker «pan» ve araç değiştirme mantığını yalnız
masaüstünde
değil, farklı işletim sistemlerinde de test edin.
Düğme alanları ile çapraz kontrol
şarttır.
Bu sayfanın sınırı
Oyun kumandası, stylüs baskı eğrisi ve çoklu dokunuş
Pointer Events ve özel API’lerle daha iyi modellenir — bu sayfa
klasik DOM MouseEvent modelidir.
İmleç kilidi ( pointer lock ) ve tam ekran birinci şahıs deneyimi ayrı güvenlik / kullanıcı izni konusudur; burada yalnızca embed canvas etkileşimi varsayılır.
- Piksel dönüşümü tek
client → canvasyardımcıda mı;offsetXile karışık ikinci yol kalmadı mı? wheeliçinpassive: falsegerekiyor mu, gerekiyorsa ayarlandı mı?- Ağır
mousemoverAF ile birleştirildi mi? - Global dinleyiciler sürükleme sonunda kaldırıldı mı?
- Üst overlay
pointer-eventsyüzünden olay tuvali mi atlıyor?