Framework entegrasyonları · Angular + Three
Pointer Bağları & Picking: Host'tan İsabet'e
Angular'da tıklama çoğu zaman canvas
HostListener ile girer; hangi mesh'in seçildiği ise
ışın isabetidir. Bu sayfa DOM bağını Three picking'e bağlar: R3F
onClick prop yüzeyinin kopyası değildir.
Holodepth bu yaprağı DOM events 101 veya WebXR uzamsal UI olarak yazmaz. R3F isabet yüzeyi Pointer olayları & picking yaprağındadır. Raycast matematik Three etkileşim omurgasındadır. Host tuvali Host & kök ; Zone tik Kare döngüsü & NgZone yapraklarındadır. HTML Canvas pointer Pointer sistemi ile akrabadır. glTF clickable metadata bu kapının konusu değildir.
Sayfayı bitirdiğinizde şu cümleyi kurabilmelisiniz: «Pointer canvas host'tan girer. NDC + Raycaster isabet üretir. Dinleme Zone dışında, seçim output'u seyrek Zone içinde olabilir. Overlay CSS hit'i yutabilir. Her rAF pick yoktur. Sonraki kapı Zone kaçışı & performanstır.»
Bu sayfanın sınırı · komşu konularla ayrım
Bu sayfa Angular + Three'de pointer bağları / picking köprüsünü anlatır:
- HostListener giriş · NDC haritası
- Raycaster servis · Output seçim
- Zone frekansı · overlay CSS
- Üç etkileşim profili
Bilinçli olarak dışarıda bırakılan konular:
- R3F onPointer* prop yüzeyi → R3F · Pointer & picking
- Host / context → Host & kök
- NgZone döngü → Kare döngüsü & NgZone
- OnDestroy → Yükleme & OnDestroy
- Ağır kaçış → Zone kaçışı & performans
- Raycast BVH teorisi · WebXR spatial · Angular events 101 · glTF
Kısa ayrım: Bu sayfa «Angular host'tan isabet nasıl bağlanır?» · R3F picking «Fiber prop yüzeyi» · Three raycast «ışın matematiği» · NgZone «dinleme / tik frekansı».
Giriş kapısı: HostListener
R3F'de isabet mesh prop'una yazılır. Angular + klasik Three'de olay
çoğu zaman canvas (veya host div) üzerinde
@HostListener('pointerdown') /
pointermove / template (click) ile yakalanır.
Holodepth: tek giriş kapısı seçin; hem window hem canvas hem child
div'e dağınık bağlamak teşhisi öldürür.
angular-three benzeri kataloglar mesh düzeyinde event soyutlayabilir; altta yine host koordinatı + raycast vardır. Soyutlamayı sihir saymayın.
Olay DOM'dan girer; isabet grafıtan çıkar.
İstemci px → NDC
clientX/Y canvas
getBoundingClientRect ile normalize edilip
[-1, 1] NDC'ye çevrilir; Raycaster
setFromCamera bunu yer. Holodepth: CSS scale /
letterbox'ta rect güncel olmalı; stale rect sapmış isabet üretir.
Formül Three omurgasında derinleşir; burada köprü: host ölçüsü yanlışsa
pick her zaman yanlışdır.
Raycaster sahipliği
Her click'te new THREE.Raycaster() allocate etmek gereksizdir.
Holodepth: host veya injectable bir picker servisinde tek Raycaster +
yeniden kullanılan Vector2. intersectsObject / layers
listesini dar tutun; tüm scene traverse pahalıdır.
| Küme | Ne zaman? | Risk |
|---|---|---|
| Yalnız seçilebilir mesh'ler | Ürün vitrini | Liste güncel mi? |
| Group recursive | Montaj | Süs çocuklar isabet çalar |
| Tüm scene | Debug | Kalabalıkta pahalı |
Zone frekansı
Pointermove selini Zone içinde dinlemek CD yangınıdır. Holodepth:
dinlemeyi OutsideAngular'da tutun (veya passive host); seçim id
değişince seyrek zone.run / Output emit. Her move'da
şablon hover rengi için Angular state yağmurundan kaçının; materyal
rengi Object3D tarafında da güncellenebilir.
NgZone yaprağı genel tik politikasını mühürledi; burada yalnız pointer şeridinin aynı diplomasiye uyduğunu kilitleyoruz.
Output: seçimi dışarı ver
İsabet sonucu host içinde kalmamalı; üst özellik
@Output() partSelected (veya output()) ile id / name /
userData taşır. Holodepth: yetkiyi yalnız client pick id'sine
bağlamayın; sunucu doğrulaması gereken eylem pick'i sinyal kabul eder,
karar vermez.
Missed (boş tıklama): seçimi temizleyen ayrı çıktı veya null emit.
R3F onPointerMissed ile aynı ürün ihtiyacı, farklı API.
Occluder ve süs mesh
Öndeki şeffaf / görünmez collider arkadaki butonu çalar.
raycast = () => {} veya aday listesinden çıkarma
politikası yazın. visible=false her sürüme/ayıra göre raycast'ı
kapatmayabilir; bilinçli katılım şarttır.
DOM overlay diplomasi
Angular template'te canvas üstü HTML toolbar sık görülür.
pointer-events: none / hedef düğmede auto
bölgeleri net yazılmazsa ya 3D ölür ya toolbar tıklanmaz. Host kök
sayfasındaki kutu buranın zeminidir.
Overlay hit yerse, Raycaster'a iş kalmaz.
Her rAF pick yok
Sürekli raycast drag/snap istisnasında seyreltilir. Varsayılan: pointer olayında pick. Move selini throttle edin. Bu, Zone kaçışı yaprağındaki vergi eşiğinin etkileşim kuzenidir.
Güvenli bağ: simüle pick
Örnekler öğretim amaçlıdır. Kamera/scene host'tan gelir. Angular event temeli öğretilmez.
Simüle · HostListener + Output.
import {
Component, ElementRef, EventEmitter, HostListener, Input, NgZone, Output, ViewChild,
} from '@angular/core'
import * as THREE from 'three'
@Component({
selector: 'app-pick-host',
template: `<canvas #glCanvas class="pick-canvas"></canvas>`,
styles: [`.pick-canvas { display:block; width:100%; height:100%; }`],
})
export class PickHostComponent {
@ViewChild('glCanvas', { static: true })
private canvasRef!: ElementRef<HTMLCanvasElement>
@Input({ required: true }) camera!: THREE.Camera
/** Yalnız seçilebilir kökler · tüm scene değil */
@Input() pickRoots: THREE.Object3D[] = []
@Output() partSelected = new EventEmitter<string | null>()
private readonly raycaster = new THREE.Raycaster()
private readonly pointer = new THREE.Vector2()
constructor(private readonly zone: NgZone) {}
@HostListener('click', ['$event'])
onClick(ev: MouseEvent): void {
// Eğitim: dinleme host canvas'ta. Zone baskısını azaltmak için
// listen Outside'da da kurulabilir; emit seyrek Zone içinde.
const hit = this.pick(ev.clientX, ev.clientY)
this.zone.run(() => this.partSelected.emit(hit))
}
private pick(clientX: number, clientY: number): string | null {
const canvas = this.canvasRef.nativeElement
const rect = canvas.getBoundingClientRect()
if (rect.width < 1 || rect.height < 1) return null
this.pointer.x = ((clientX - rect.left) / rect.width) * 2 - 1
this.pointer.y = -((clientY - rect.top) / rect.height) * 2 + 1
this.raycaster.setFromCamera(this.pointer, this.camera)
const hits = this.raycaster.intersectObjects(this.pickRoots, true)
if (!hits.length) return null
return hits[0].object.name || hits[0].object.uuid
}
}
Simüle · throttle'lu hover (Outside).
import { NgZone } from '@angular/core'
/** pointermove yağmurunu ~50ms'ye indir · Angular state yok */
export function attachThrottledPointerMove(
zone: NgZone,
el: HTMLElement,
onMove: (x: number, y: number) => void,
): () => void {
let last = 0
let raf = 0
const handler = (ev: PointerEvent) => {
const now = performance.now()
if (now - last < 50) return
last = now
const x = ev.clientX
const y = ev.clientY
zone.runOutsideAngular(() => {
cancelAnimationFrame(raf)
raf = requestAnimationFrame(() => onMove(x, y))
})
}
el.addEventListener('pointermove', handler, { passive: true })
return () => {
el.removeEventListener('pointermove', handler)
cancelAnimationFrame(raf)
}
}
Güvenlik: pick id ile yetki açmayın. cursor'u
document.body'de unutmayın. Dinleyiciyi OnDestroy'da
çıkarın (yükleme yaprağındaki ömür ruhu).
Picking tuzakları
- Stale rect: resize sonrası NDC sapması.
- Tüm scene pick: süs isabeti + maliyet.
- Zone içi move: CD yangını.
- Overlay yutması: CSS pointer-events.
- rAF pick: gereksiz sürekli test.
- Missed yok: seçim temizlenmez.
- R3F onClick ezberi: mesh prop aramak.
Üç etkileşim profili
-
01 · Bağ
Vitrin seçimi
- Giriş
- click HostListener
- Aday
- Dar pickRoots
- Risk
- Occluder
-
02 · Bağ
Hover önizleme
- Move
- Throttle + Outside
- Görsel
- Materyal / emissive
- Risk
- Her move Output
-
03 · Bağ
DOM + Canvas
- CSS
- pointer-events bölgeleri
- HUD
- Angular template
- Risk
- Toolbar'ın tüm hit'i yutması
Erişilebilirlik notu
Kritik seçimleri yalnız pointer'a kilitlemeyin. Holodepth: eşdeğer
Angular list / klavye kontrolü sunun. Canvas'a anlamlı
aria-label düşünün.
Holodepth perspektifleri
Tek giriş kapısı
Dağınık listener teşhisi öldürür.
Adayı daraltın
pickRoots ürün listesidir; scene.dump değildir.
Frekansı ayırın
Dinleme Outside; UI emit seyrek.
Teşhis maddeleri
- HostListener / bind doğru elemanda mı?
- getBoundingClientRect güncel mi?
- pickRoots dar mı?
- Zone içi move yağmuru var mı?
- Missed / null emit var mı?
- Overlay pointer-events yazılı mı?
- rAF pick varsayılan mı olmuş?
- R3F prop veya glTF bu sayfaya sızmış mı?
Holodepth içgörüsü
«Tıklamıyor» çoğu zaman raycast formülü değil; rect, overlay, aday kümesi veya Zone selidir. Önce host bağını okuyun.
Sonraki kapı: Zone kaçışı
Etkileşim ve yük oturunca kalan soru: ne zaman daha agresif Zone / imperatif ada açılır? Sonraki sayfa Zone kaçışı & performans bu eşiği anlatır. Angular + Three izleğinin son yaprağıdır.