HTML5 Canvas · Girdi & etkileşim
Event koordinatları: tuval pikseline güvenilir köprü
DOM olayları size ekran / görünüm tabanlı sayılar verir; Canvas 2D bağlamı ise
bitmap piksel ızgarası üzerinde çizer. İkisi arasındaki oran, CSS ile verilen
görüntüleme boyutu ile canvas.width /
canvas.height özniteliklerinin farkından doğar — aynı çizgi, düşük DPR ekranda
«yeterli», retina ekranda ise bulanık veya kaymış görünür. Bu sayfa, tek bir
client → canvas dönüşümünü sabitleyerek; fare, işaretçi ve klavye ile kesişen
tüm araçların aynı matematiği paylaşmasını hedefler — uygulama olayları
Pointer sistemi ve
Fare girdisi sayfalarında ayrıntır;
burada yalnızca sayıların anlamları ve köprü formülü ön plandadır.
Taşıma ve tutma ofseti desenleri Sürükleme mantığı · tutma ofseti köprüsüyle doğrudan bağlanır; yoğun hareket olaylarında çizimi Update vs render · kirli bayrak ile ayırma düşüncesi bu köprünün üzerinde çalışır — fakat köprü yanlışsa kare başına en iyi kod da imzayı düzeltmez.
Özet: sınama ve çizim uzayları
| Alan / özellik | Referans | Canvas’ta rolü |
|---|---|---|
clientX / clientY |
Görünüm alanı ( viewport ), kaydırma hariç | getBoundingClientRect ile tuval sınırına göre göreli konum |
pageX / pageY |
Sayfa kökü, belge kaydırması dahil | Tuval sabit görünür alandaysa çoğu zaman client* yeter; kaydırılan
gömülerde dikkat |
offsetX / offsetY |
Olay target öğesine göre |
Kolaylık; transform / iç içe düzen / DPR ile sürpriz üretebilir — tek başına piksel sanmayın |
canvas.width / height |
Bitmap boyutu ( backing store ) | Çizim koordinatlarının üst sınırı; DPR ile bilinçli büyütmek keskinlik verir |
getBoundingClientRect() |
CSS pikseli cinsinden ekrandaki kutu | İstemci noktasını tuval içi orana bölmek için referans kutu |
İstemci uzayı: clientX ve görünür dikdörtgen
MouseEvent ve PointerEvent için clientX /
clientY, belge kaydırmasından bağımsız olarak pencerenin görünür alanına göre
konumu verir. Tuvalin köşelerine göre göreli konumu bulmak için öğenin
getBoundingClientRect() çıktısı kullanılır; çağrı anı ile olay anı aynı karede
olduğu sürece (
senkron işleyici içinde ) senkronizasyon tutumludur. Kaydırma, animasyon veya üst çubukların
açılıp kapanması dikdörtgeni hareket ettirir — uzun süre önbelleğe alınmış rect ile güncel
olmayan olayı birleştirmek ince sapmalar üretir.
Çoklu monitör, tarayıcı yakınlaştırması (
page zoom ) ve işletim sistemi ölçeği client* ölçeğini
değiştirir; fakat aynı kare içinde tuval ve olay birlikte yorumlandığında rect oranı genelde
doğru kalır. Özet: tuval içi x için önce clientX - rect.left,
sonra bu uzaklığı rect.width üzerinden oransal olarak
canvas.width ile çarpın — dikey eksen aynayıdır.
Tek köprü: iç boyut oranı ve keskin çizim
Tuval öğesinin CSS genişliği görüntülenen boyuttur; width özniteliği ise arka
tampon genişliğidir. İkisi farklıysa tarayıcı görüntüyü ölçekler — bu istenendir
çünkü devicePixelRatio üzerinden retina keskinliği sağlamak için bitmap’i daha
geniş
tutarsınız. Köprü oranı
canvas.width / rect.width ve
canvas.height / rect.height ile bulunur; en/boy oranı bozulmadıkça genelde
ikisi yaklaşık eşidir, fakat müstakil ölçeklemelerde (
örn. hatalı stil ) ayrı ayrı kullanmak şarttır.
Piksel hizalı çizim (
1 px çizgiler, kare ızgara ) istiyorsanız dönüşüm sonrası
Math.floor veya 0.5 ofset stratejilerini merkezi yardımcıda
uygulayın — dağınık yuvarlamalar çizgi titreşimi yaratır. Bu sayfa yuvarlama politikasını
ürün kararı olarak bırakır; yalnızca tek yerde toplanması gerektiğini vurgular.
/**
* İstemci (viewport) pikseli → tuval bitmap pikseli.
* rect ve canvas ölçüleri anlık okunur; olay işleyicisi içinde çağırmak tutumludur.
*/
function clientToCanvas(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,
};
}
offsetX ve offsetY: pratik ama bağlama duyarlı
offsetX / offsetY olayın target öğesine göre
tanımlanır; birçok örnekte tek satırda tuval içi koordinat verir. Ancak üst düğümlerde
transform, filter, iç içe kaydırma bölgeleri veya
object-fit ile ölçeklenmiş gömülü içerik söz konusuysa bu değerler ile
getBoundingClientRect köprüsü aynı sonucu vermeyebilir. Üretim kodunda kritik
etkileşimlerde clientToCanvas kalıbını standart yapmak, ekip içi
«neden ofset farklı?» tartışmalarını keser.
Olay kabarcıklanırken target ile currentTarget farklı olabilir;
offset değerleri hedef öğeye bağlıdır — tuval yerine sarmalayıcıda dinliyorsanız offset
tuval pikseli olmayabilir. Bu yüzden merkezi köprü, mümkünse doğrudan
canvas referansı ve client* üzerinden kurulur.
Cihaz piksel oranı ve tuval boyutlandırma kararı
Yaygın desen: CSS boyutu düzen akışına göre sabitlenir, arka tampon
canvas.width = Math.round(cssWidth * devicePixelRatio) (
ve yükseklik aynı ) ile büyütülür; böylece aynı CSS alanında daha fazla bitmap pikseli
sunulur. Köprü formülü yine geçerlidir — çünkü getBoundingClientRect CSS
pikseli döndürür, oran bitmap / CSS olur. DPR değişimlerinde (
pencere ekranlar arası taşınması ) yeniden boyutlandırma veya yeniden çizim tetiklemek
gerekir; aksi halde bir süre bulanıklık veya kırpma görülür.
Tam tersi yaklaşım ( bitmap = CSS 1:1 ) basit eğitim sayfalarında iş görür fakat yüksek DPR ekranda pürüzlüdür. Hangi politikayı seçerseniz seçin, uygulama genelinde aynı köprüyü kullanın; bir modülde DPR ile çarpılmış, diğerinde çevrim yapılmamış koordinatlar bir araya gelince imza kayar.
Kaydırma, dönüşüm ve gömülü düzen kayması
Gövde veya ara paneller kaydırıldığında tuvalin rect’i hareket eder; client*
ile
birlikte okunduğunda göreli konum doğru kalır. Sorun, saklanmış rect veya
sayfa koordinatları ile güncel olmayan bir önbellek kullanıldığında ortaya çıkar — örneğin
animasyon bitiminde tek seferlik rect saklayıp sonra uzun süre o değerle çarpan kod.
CSS transform uygulanmış ata öğe, görünen kutu ile düzen kutusunu ayırabilir;
getBoundingClientRect dönüştürülmüş sınırı verir, bu da tuval köprüsü için
doğru kaynaktır. Karmaşık sahne grafikleri (
zoom / pan ) için dünya koordinatına ayrı bir ters dönüşüm katmanı gerekir — bu sayfa
yalnızca DOM tuvalinin kendi dikdörtgenine göre köprüyü sabitler; iç zoom yığınınız
ürün katmanınızdadır.
Anti-kalıplar: offset tek başına ve dağınık formül
offset = bitmap: offsetX / offsetY bazen tuval
içinde doğru gibi görünür; fakat ata hiyerarşide transform, filtre veya iç içe
kaydırma ile aynı matematiksel anlamı getBoundingClientRect köprüsüyle
vermez. DPR büyütülmüş bitmap ile CSS kutusu farklı ölçekteyken «tek satırlık offset»
yanıltıcıdır — özellikle görünür şekil ile gerçek tıklama hedefi (
hit slop ) ayrı tasarlandığında. Çözüm:
Tek köprü üzerinden
client* normalleştirmek;
offset alanları bölümündeki uyarılarla
uyumlu kalın. Fare sayfasındaki aynı tema için
Fare girdisi · anti-kalıplar
çapraz okunabilir — içerik tekrarı değil, hata sınıfının sabitlenmesidir.
Eski rect: Başlangıçta bir kez alınan getBoundingClientRect
sonucu,
sayfa kaydırıldığında, yan menü açıldığında veya esnek düzen tuvalleri yeniden
ölçülendirdiğinde
geçersiz kalır. Önbellekli rect ile çarpılan nokta, ekranda imleç doğru yerde olsa bile
bitmap
içinde kayar; hata ara sıra tetiklenir ve kötüye üne yol açar. Çözüm: olay işleyicisinde (
veya aynı kare içinde tetiklenen rAF yolunda ) rect’i yeniden okuyun veya boyut değişimini
dinleyen katmanda köprüyü geçersiz kılın —
Kaydırma ve transform ile birlikte düşünün.
Yalnız genişlik oranı: sx = canvas.width / rect.width değerini
hem x hem y için kullanmak, tuvalin CSS’te oranı bozulmuş (
non-uniform scale ) durumunda daireleri elips, kareleri dikdörtgen
imleç uzayında eğer. Kare korumalı dünya modeli istiyorsanız en/boy oranını CSS’te koruyun;
streç bilinçli ise sx ve sy’yi
clientToCanvas
örneğindeki gibi ayrı tutun. Dağınık dosyalarda bir yerde tek oran, başka yerde çift
oran kullanımı en sık «yalnız yatay doğru» sapmasını üretir.
pageX everywhere: pageX belge kaydırmasına göre mutlak
konumdur;
clientX görünür alana göredir. Gömülü uygulama, iframe veya iç
kaydırma bölgelerinde ikisi farklı sapma gösterir. Tuval, görünür dikdörtgenle hizalanmış
tek bir hedefse pratikte client* + rect köprüsü yeterli olur; «her zaman
page» yaklaşımı tuval dışı bileşenlerle aynı kodu paylaştığınızda sessizce kırılır. Farklı
strateji şartsa dönüşümü yine tek yardımcıya alın ve girdi kaynağını (
client / page ) parametreyle seçin — formülün kopyalanması değil, tek çatı altında iki giriş
noktası hedeftir.
Dağınık formül ve mikro sapma: Bir dosyada bölme, başka dosyada yuvarlama,
üçüncü dosyada ters eksen; 0,5 px kaymalar toplanıp seçim ve çizgi hizasında titreme
oluşturur. Üretimde «koordinat politikasını» tek modül (
örn. clientToCanvas + isteğe bağlı snap ) olarak versiyonlayın; kod
incelemesinde
getBoundingClientRect ve offset geçen her satır bu modüle
yönlendirilmelidir.
Bu sayfanın sınırı
3B projeksiyon veya kamera matrisi ile dünya uzayı dönüşümü burada ele alınmaz — bu sayfa 2D Canvas bitmap hedefleyicisidir. WebGL derinlikli koordinatları ayrı içerik haritasına aittir.
clientToCanvastek modülde mi; kopya formül dağılmış mı?- Olay anında
getBoundingClientRectmi okunuyor? - DPR / yeniden boyutlandırma sonrası arka tampon güncelleniyor mu?
sx/systreç senaryosunda ayrı mı; tek oran varsayımına düşülmedi mi?- Belgede
page*ileclient*karışık mı; köprü tek girişten mi besleniyor?