aframe-webxr

安装量: 43
排名: #16968

安装

npx skills add https://github.com/freshtechbro/claudedesignskills --skill aframe-webxr

A-Frame WebXR Skill When to Use This Skill Build VR/AR experiences with minimal JavaScript Create cross-platform WebXR applications (desktop, mobile, headset) Prototype 3D scenes quickly with HTML primitives Implement VR controller interactions Add 3D content to web pages declaratively Build 360° image/video experiences Develop AR experiences with hit testing Core Concepts 1. Entity-Component-System (ECS) A-Frame uses an entity-component-system architecture where: Entities are containers (like

in HTML) Components add functionality/appearance to entities Systems provide global functionality < a-entity geometry = " primitive: box; width: 2 " material = " color: red; metalness: 0.5 " position = " 0 1.5 -3 " rotation = " 0 45 0 " > Primitives are shortcuts for common entity + component combinations: < a-box color = " red " position = " 0 1.5 -3 " rotation = " 0 45 0 " width = " 2 " > < a-entity geometry = " primitive: box; width: 2 " material = " color: red " position = " 0 1.5 -3 " rotation = " 0 45 0 " > 2. Scene Setup Every A-Frame app starts with : < html > < head > < script src = " https://aframe.io/releases/1.7.1/aframe.min.js " > < body > < a-scene > < a-box position = " -1 0.5 -3 " color = " #4CC3D9 " > < a-sphere position = " 0 1.25 -5 " radius = " 1.25 " color = " #EF2D5E " > < a-cylinder position = " 1 0.75 -3 " radius = " 0.5 " height = " 1.5 " color = " #FFC65D " > < a-plane position = " 0 0 -4 " rotation = " -90 0 0 " width = " 4 " height = " 4 " color = " #7BC8A4 " > < a-sky color = " #ECECEC " > The scene automatically injects: Default camera (position: 0 1.6 0 ) Look controls (mouse drag) WASD controls (keyboard movement) 3. Camera Systems Default Camera (auto-injected if none specified): < a-entity camera = " active: true " look-controls wasd-controls position = " 0 1.6 0 " > Custom Camera : < a-camera position = " 0 2 5 " look-controls wasd-controls = " acceleration: 50 " > Camera Rig (for independent movement and rotation): < a-entity id = " rig " position = " 0 0 0 " > < a-camera look-controls > VR Camera Rig with Controllers : < a-entity id = " rig " position = " 0 0 0 " > < a-camera position = " 0 1.6 0 " > < a-entity hand-controls = " hand: left " laser-controls = " hand: left " > < a-entity hand-controls = " hand: right " laser-controls = " hand: right " > 4. Lighting Ambient Light (global illumination): < a-entity light = " type: ambient; color: #BBB; intensity: 0.5 " > Directional Light (like sunlight): < a-entity light = " type: directional; color: #FFF; intensity: 0.8 " position = " 1 2 1 " > Point Light (radiates in all directions): < a-entity light = " type: point; color: #F00; intensity: 2; distance: 50 " position = " 0 3 0 " > Spot Light (cone-shaped beam): < a-entity light = " type: spot; angle: 45; intensity: 1.5 " position = " 0 5 0 " rotation = " -90 0 0 " > 5. Materials and Textures Standard Material : < a-sphere material = " color: #FF0000; metalness: 0.5; roughness: 0.3 " position = " 0 1 -3 " > Textured Material : < a-assets > < img id = " woodTexture " src = " wood.jpg " > < a-box material = " src: #woodTexture " position = " 0 1 -3 " > Flat Shading (no lighting): < a-plane material = " shader: flat; color: #4CC3D9 " > 6. Animations Property Animation : < a-box position = " 0 1 -3 " animation = " property: rotation; to: 0 360 0; loop: true; dur: 5000 " > Multiple Animations (use animation__* naming): < a-sphere position = " 0 1 -3 " animation__position = " property: position; to: 0 3 -3; dir: alternate; loop: true; dur: 2000 " animation__rotation = " property: rotation; to: 360 360 0; loop: true; dur: 4000 " animation__scale = " property: scale; to: 1.5 1.5 1.5; dir: alternate; loop: true; dur: 1000 " > Event-Based Animation : < a-box color = " blue " animation__mouseenter = " property: scale; to: 1.2 1.2 1.2; startEvents: mouseenter " animation__mouseleave = " property: scale; to: 1 1 1; startEvents: mouseleave " animation__click = " property: rotation; from: 0 0 0; to: 0 360 0; startEvents: click " > 7. Assets Management Preload assets for better performance: < a-scene > < a-assets > < img id = " texture1 " src = " texture.jpg " > < img id = " skyTexture " src = " sky.jpg " > < video id = " video360 " src = " 360video.mp4 " autoplay loop > < audio id = " bgMusic " src = " music.mp3 " preload = " auto " > < a-asset-item id = " tree " src = " tree.gltf " > < a-mixin id = " redMaterial " material = " color: red; metalness: 0.7 " > < a-entity gltf-model = " #tree " position = " 2 0 -5 " > < a-sphere mixin = " redMaterial " position = " 0 1 -3 " > < a-sky src = " #skyTexture " > 8. Custom Components Register custom components to encapsulate logic: AFRAME . registerComponent ( 'rotate-on-click' , { // Component schema (configuration) schema : { speed : { type : 'number' , default : 1 } } , // Lifecycle: called once when component attached init : function ( ) { this . el . addEventListener ( 'click' , ( ) => { this . rotating = ! this . rotating ; } ) ; } , // Lifecycle: called every frame tick : function ( time , timeDelta ) { if ( this . rotating ) { var rotation = this . el . getAttribute ( 'rotation' ) ; rotation . y += this . data . speed ; this . el . setAttribute ( 'rotation' , rotation ) ; } } } ) ; < a-box rotate-on-click = " speed: 2 " position = " 0 1 -3 " > Common Patterns Pattern 1: VR Controller Interactions Problem : Enable object grabbing and manipulation in VR Solution : Use hand-controls and custom grab component < a-scene > < a-entity id = " rig " > < a-camera position = " 0 1.6 0 " > < a-entity id = " leftHand " hand-controls = " hand: left " laser-controls = " hand: left " > < a-entity id = " rightHand " hand-controls = " hand: right " laser-controls = " hand: right " > < a-box class = " grabbable " position = " -1 1.5 -3 " color = " #4CC3D9 " > < a-sphere class = " grabbable " position = " 1 1.5 -3 " color = " #EF2D5E " > < script > AFRAME . registerComponent ( 'grabbable' , { init : function ( ) { var el = this . el ; el . addEventListener ( 'triggerdown' , function ( evt ) { console . log ( 'Grabbed by' , evt . detail . hand ) ; el . setAttribute ( 'color' , 'green' ) ; } ) ; el . addEventListener ( 'triggerup' , function ( evt ) { el . setAttribute ( 'color' , 'blue' ) ; } ) ; el . addEventListener ( 'gripdown' , function ( evt ) { // Attach object to controller var controllerEl = evt . detail . controller ; controllerEl . object3D . attach ( el . object3D ) ; } ) ; el . addEventListener ( 'gripup' , function ( evt ) { // Detach from controller var sceneEl = el . sceneEl . object3D ; sceneEl . attach ( el . object3D ) ; } ) ; } } ) ; // Apply grabbable component document . querySelectorAll ( '.grabbable' ) . forEach ( el => { el . setAttribute ( 'grabbable' , '' ) ; } ) ; Pattern 2: 360° Image Gallery Problem : Create an interactive 360° photo viewer Solution : Use sky primitive and clickable thumbnails < a-scene > < a-assets > < img id = " city " src = " city.jpg " > < img id = " forest " src = " forest.jpg " > < img id = " beach " src = " beach.jpg " > < img id = " city-thumb " src = " city-thumb.jpg " > < img id = " forest-thumb " src = " forest-thumb.jpg " > < img id = " beach-thumb " src = " beach-thumb.jpg " > < audio id = " click-sound " src = " click.mp3 " > < a-sky id = " image-360 " src = " #city " rotation = " 0 -130 0 " > < a-entity id = " menu " position = " 0 1.6 -2 " > < a-entity class = " link " geometry = " primitive: plane; width: 0.7; height: 0.7 " material = " shader: flat; src: #city-thumb " position = " -1 0 0 " sound = " on: click; src: #click-sound " event-set__mouseenter = " scale: 1.2 1.2 1 " event-set__mouseleave = " scale: 1 1 1 " event-set__click = " _target: #image-360; material.src: #city " > < a-entity class = " link " geometry = " primitive: plane; width: 0.7; height: 0.7 " material = " shader: flat; src: #forest-thumb " position = " 0 0 0 " sound = " on: click; src: #click-sound " event-set__mouseenter = " scale: 1.2 1.2 1 " event-set__mouseleave = " scale: 1 1 1 " event-set__click = " _target: #image-360; material.src: #forest " > < a-entity class = " link " geometry = " primitive: plane; width: 0.7; height: 0.7 " material = " shader: flat; src: #beach-thumb " position = " 1 0 0 " sound = " on: click; src: #click-sound " event-set__mouseenter = " scale: 1.2 1.2 1 " event-set__mouseleave = " scale: 1 1 1 " event-set__click = " _target: #image-360; material.src: #beach " > < a-camera > < a-cursor raycaster = " objects: .link " > Pattern 3: AR Hit Testing (Place Objects in Real World) Problem : Place virtual objects on detected real-world surfaces Solution : Use ar-hit-test component < a-scene webxr = " optionalFeatures: hit-test, dom-overlay; overlayElement: #overlay " ar-hit-test = " target: #furniture; type: footprint " > < a-assets > < a-asset-item id = " chair " src = " chair.gltf " > < a-entity id = " furniture " gltf-model = " #chair " scale = " 0.5 0.5 0.5 " > < div id = " overlay " style = " position : fixed ; bottom : 20 px ; left : 50 % ; transform : translateX ( -50 % ) ; background : rgba ( 0 , 0 , 0 , 0.7 ) ; color : white ; padding : 15 px ; border-radius : 8 px ; font-family : sans-serif ; " > < p id = " message " > Tap to enter AR mode
</
a-scene
>
<
script
>
const
sceneEl
=
document
.
querySelector
(
'a-scene'
)
;
const
message
=
document
.
getElementById
(
'message'
)
;
sceneEl
.
addEventListener
(
'enter-vr'
,
function
(
)
{
if
(
this
.
is
(
'ar-mode'
)
)
{
message
.
textContent
=
''
;
this
.
addEventListener
(
'ar-hit-test-start'
,
function
(
)
{
message
.
innerHTML
=
'Scanning environment, finding surface.'
;
}
,
{
once
:
true
}
)
;
this
.
addEventListener
(
'ar-hit-test-achieved'
,
function
(
)
{
message
.
innerHTML
=
'Tap on the screen to place the object.'
;
}
,
{
once
:
true
}
)
;
this
.
addEventListener
(
'ar-hit-test-select'
,
function
(
)
{
message
.
textContent
=
'Object placed!'
;
setTimeout
(
(
)
=>
message
.
textContent
=
''
,
2000
)
;
}
,
{
once
:
true
}
)
;
}
}
)
;
sceneEl
.
addEventListener
(
'exit-vr'
,
function
(
)
{
message
.
textContent
=
'Tap to enter AR mode'
;
}
)
;
</
script
>
Pattern 4: Mouse/Gaze Interactions
Problem
Enable click interactions with desktop mouse or VR gaze
Solution
Use cursor component and raycaster < a-scene

< a-box class = " interactive " position = " -1 1.5 -3 " color = "

4CC3D9

" event-set__mouseenter = " color: yellow " event-set__mouseleave = " color: #4CC3D9 " event-set__click = " scale: 1.5 1.5 1.5 "

</ a-box

< a-sphere class = " interactive " position = " 1 1.5 -3 " color = "

EF2D5E

" event-set__click = " color: orange; scale: 2 2 2 "

</ a-sphere

< a-plane position = " 0 0 -4 " rotation = " -90 0 0 " width = " 10 " height = " 10 " color = "

7BC8A4

"

</ a-plane

< a-camera position = " 0 1.6 0 "

<
a-cursor
raycaster
=
"
objects: .interactive
"
fuse
=
"
true
"
fuse-timeout
=
"
1500
"
>
</
a-cursor
>
</
a-camera
>
</
a-scene
>
<
script
>
// Advanced click handling with JavaScript
document
.
querySelectorAll
(
'.interactive'
)
.
forEach
(
el
=>
{
el
.
addEventListener
(
'click'
,
function
(
evt
)
{
console
.
log
(
'Clicked:'
,
this
.
id
||
this
.
tagName
)
;
console
.
log
(
'Intersection point:'
,
evt
.
detail
.
intersection
.
point
)
;
}
)
;
}
)
;
</
script
>
Pattern 5: Dynamic Scene Generation
Problem
Programmatically create and manipulate entities
Solution
Use JavaScript DOM manipulation < a-scene

< a-camera position = " 0 1.6 5 "

</ a-camera

< a-entity light = " type: ambient; color: #888 "

</ a-entity

< a-entity light = " type: directional; color: #FFF " position = " 1 2 1 "

</ a-entity

</ a-scene

< script

const scene = document . querySelector ( 'a-scene' ) ; // Create sphere function createSphere ( x , y , z , color ) { const entity = document . createElement ( 'a-entity' ) ; entity . setAttribute ( 'geometry' , { primitive : 'sphere' , radius : 0.5 } ) ; entity . setAttribute ( 'material' , { color : color , metalness : 0.5 , roughness : 0.3 } ) ; entity . setAttribute ( 'position' , { x , y , z } ) ; // Add animation entity . setAttribute ( 'animation' , { property : 'position' , to : ${ x } ${ y + 1 } ${ z } , dir : 'alternate' , loop : true , dur : 2000 } ) ; scene . appendChild ( entity ) ; return entity ; } // Generate grid of spheres for ( let x = - 3 ; x <= 3 ; x += 1.5 ) { for ( let z = - 5 ; z <= - 2 ; z += 1.5 ) { const color = `

${
Math
.
floor
(
Math
.
random
(
)
*
16777215
)
.
toString
(
16
)
}
`
;
createSphere
(
x
,
1
,
z
,
color
)
;
}
}
// Listen to component changes
scene
.
addEventListener
(
'componentchanged'
,
function
(
evt
)
{
console
.
log
(
'Component changed:'
,
evt
.
detail
.
name
)
;
}
)
;
// Access Three.js objects directly
setTimeout
(
(
)
=>
{
const
entities
=
document
.
querySelectorAll
(
'a-entity[geometry]'
)
;
entities
.
forEach
(
el
=>
{
el
.
object3D
.
visible
=
true
;
// Direct Three.js manipulation
}
)
;
}
,
1000
)
;
</
script
>
Pattern 6: Environment and Skybox
Problem
Create immersive environments quickly
Solution
Use community components and 360 images < html

< head

< script src = " https://aframe.io/releases/1.7.1/aframe.min.js "

</ script

< script src = " https://cdn.jsdelivr.net/npm/@fern-solutions/aframe-sky-background/dist/sky-background.umd.min.js "

</ script

< script src = " https://cdn.jsdelivr.net/gh/c-frame/aframe-extras@7.5.0/dist/aframe-extras.min.js "

</ script

</ head

< body

< a-scene

< a-sky-background top-color = "

4A90E2

" bottom-color = "

87CEEB

"

</ a-sky-background

< a-entity ocean = " density: 20; width: 50; depth: 50; speed: 4 " material = " color: #9CE3F9; opacity: 0.75; metalness: 0; roughness: 1 " rotation = " -90 0 0 "

</ a-entity

<
a-entity
particle-system
=
"
preset: snow; particleCount: 2000; color: #FFF
"
>
</
a-entity
>
<
a-entity
light
=
"
type: ambient; color: #888
"
>
</
a-entity
>
<
a-entity
light
=
"
type: directional; color: #FFF; intensity: 0.7
"
position
=
"
1 2 1
"
>
</
a-entity
>
</
a-scene
>
</
body
>
</
html
>
Pattern 7: GLTF Model Loading
Problem
Load and display 3D models
Solution
Use gltf-model component with asset management < a-scene

< a-assets

< a-asset-item id = " robot " src = " robot.gltf "

</ a-asset-item

< a-asset-item id = " building " src = " building.glb "

</ a-asset-item

</ a-assets

< a-entity gltf-model = "

robot

" position = " 0 0 -3 " scale = " 0.5 0.5 0.5 " animation = " property: rotation; to: 0 360 0; loop: true; dur: 10000 "

</ a-entity

< a-entity gltf-model = "

building

" position = " 5 0 -10 " animation-mixer = " clip: *; loop: repeat "

</ a-entity

< a-camera position = " 0 1.6 5 "

</ a-camera

< a-entity light = " type: ambient; intensity: 0.5 "

</ a-entity

< a-entity light = " type: directional; intensity: 0.8 " position = " 2 4 2 "

</ a-entity

</ a-scene

< script

// Handle model loading events document . querySelector ( '[gltf-model="#robot"]' ) . addEventListener ( 'model-loaded' , ( evt ) => { console . log ( 'Model loaded:' , evt . detail . model ) ; // Access Three.js object const model = evt . detail . model ; model . traverse ( node => { if ( node . isMesh ) { console . log ( 'Mesh found:' , node . name ) ; } } ) ; } ) ; document . querySelector ( '[gltf-model="#robot"]' ) . addEventListener ( 'model-error' , ( evt ) => { console . error ( 'Model loading error:' , evt . detail ) ; } ) ; </ script

Integration Patterns With Three.js Access underlying Three.js objects: // Get Three.js scene const scene = document . querySelector ( 'a-scene' ) . object3D ; // Get entity's Three.js object const box = document . querySelector ( 'a-box' ) ; const threeObject = box . object3D ; // Direct Three.js manipulation threeObject . position . set ( 1 , 2 , 3 ) ; threeObject . rotation . y = Math . PI / 4 ; // Add custom Three.js objects const geometry = new THREE . BoxGeometry ( 1 , 1 , 1 ) ; const material = new THREE . MeshStandardMaterial ( { color : 0xff0000 } ) ; const mesh = new THREE . Mesh ( geometry , material ) ; scene . add ( mesh ) ; With GSAP (Animation) Animate A-Frame entities with GSAP: < script src = " https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js "

</ script

< script

const box = document . querySelector ( 'a-box' ) ; // Animate position gsap . to ( box . object3D . position , { x : 3 , y : 2 , z : - 5 , duration : 2 , ease : 'power2.inOut' } ) ; // Animate rotation gsap . to ( box . object3D . rotation , { y : Math . PI * 2 , duration : 3 , repeat : - 1 , ease : 'none' } ) ; // Animate attributes gsap . to ( box . components . material . material , { opacity : 0.5 , duration : 1 } ) ; </ script

With React Integrate A-Frame in React components: import React , { useEffect , useRef } from 'react' ; import 'aframe' ; function VRScene ( ) { const sceneRef = useRef ( null ) ; useEffect ( ( ) => { const scene = sceneRef . current ; // Create entities dynamically const entity = document . createElement ( 'a-sphere' ) ; entity . setAttribute ( 'position' , '0 1.5 -3' ) ; entity . setAttribute ( 'color' , '#EF2D5E' ) ; scene . appendChild ( entity ) ; // Listen to events scene . addEventListener ( 'enter-vr' , ( ) => { console . log ( 'Entered VR mode' ) ; } ) ; } , [ ] ) ; return ( < a-scene ref = { sceneRef }

< a-box position = " -1 0.5 -3 " rotation = " 0 45 0 " color = "

4CC3D9

" /> < a-sphere position = " 0 1.25 -5 " radius = " 1.25 " color = "

EF2D5E

" /> < a-cylinder position = " 1 0.75 -3 " radius = " 0.5 " height = " 1.5 " color = "

FFC65D

" /> < a-plane position = " 0 0 -4 " rotation = " -90 0 0 " width = " 4 " height = " 4 " color = "

7BC8A4

" /> < a-sky color = "

ECECEC

" /> </ a-scene

) ; } export default VRScene ; Performance Best Practices 1. Use Asset Management Preload assets to avoid blocking: < a-assets

< img id = " texture1 " src = " large-texture.jpg "

< video id = " video360 " src = " 360video.mp4 " preload = " auto "

</ video

< a-asset-item id = " model " src = " complex-model.gltf "

</ a-asset-item

</ a-assets

  1. Pool Entities Reuse entities instead of creating/destroying: AFRAME . registerComponent ( 'bullet-pool' , { init : function ( ) { this . pool = [ ] ; this . used = [ ] ; // Pre-create bullets for ( let i = 0 ; i < 20 ; i ++ ) { const bullet = document . createElement ( 'a-sphere' ) ; bullet . setAttribute ( 'radius' , 0.1 ) ; bullet . setAttribute ( 'visible' , false ) ; this . el . sceneEl . appendChild ( bullet ) ; this . pool . push ( bullet ) ; } } , getBullet : function ( ) { if ( this . pool . length

0 ) { const bullet = this . pool . pop ( ) ; bullet . setAttribute ( 'visible' , true ) ; this . used . push ( bullet ) ; return bullet ; } } , returnBullet : function ( bullet ) { bullet . setAttribute ( 'visible' , false ) ; const index = this . used . indexOf ( bullet ) ; if ( index

- 1 ) { this . used . splice ( index , 1 ) ; this . pool . push ( bullet ) ; } } } ) ; 3. Optimize Geometry Use low-poly models and LOD:

< a-sphere radius = " 1 " segments-width = " 8 " segments-height = " 6 "

</ a-sphere

< a-sphere radius = " 1 " segments-width = " 32 " segments-height = " 32 "

</ a-sphere

  1. Limit Draw Calls Use instancing for repeated objects: AFRAME . registerComponent ( 'instanced-trees' , { init : function ( ) { // Use Three.js InstancedMesh for repeated geometry const scene = this . el . sceneEl . object3D ; const geometry = new THREE . ConeGeometry ( 0.5 , 2 , 8 ) ; const material = new THREE . MeshStandardMaterial ( { color : 0x228B22 } ) ; const mesh = new THREE . InstancedMesh ( geometry , material , 100 ) ; // Position instances for ( let i = 0 ; i < 100 ; i ++ ) { const matrix = new THREE . Matrix4 ( ) ; matrix . setPosition ( Math . random ( ) * 20 - 10 , 0 , Math . random ( ) * 20 - 10 ) ; mesh . setMatrixAt ( i , matrix ) ; } scene . add ( mesh ) ; } } ) ;
  2. Throttle tick() Functions Don't update every frame if unnecessary: AFRAME . registerComponent ( 'throttled-update' , { init : function ( ) { this . lastUpdate = 0 ; this . updateInterval = 100 ; // ms } , tick : function ( time , timeDelta ) { if ( time - this . lastUpdate = this . updateInterval ) { // Expensive operation here this . lastUpdate = time ; } } } ) ;
  3. Use Stats Component for Monitoring < a-scene stats
</
a-scene
>
Common Pitfalls and Solutions
Pitfall 1: Entities Not Appearing
Problem
Entity added but not visible
Causes
:
Entity positioned behind camera
Scale is 0 or very small
Material opacity is 0
Entity outside camera frustum
Solution
:
// Wait for scene to load
const
scene
=
document
.
querySelector
(
'a-scene'
)
;
scene
.
addEventListener
(
'loaded'
,
(
)
=>
{
const
entity
=
document
.
createElement
(
'a-box'
)
;
entity
.
setAttribute
(
'position'
,
'0 1.5 -3'
)
;
// In front of camera
entity
.
setAttribute
(
'color'
,
'red'
)
;
scene
.
appendChild
(
entity
)
;
}
)
;
// Debug: Check entity position
console
.
log
(
entity
.
getAttribute
(
'position'
)
)
;
// Debug: Check if entity is in scene
console
.
log
(
entity
.
parentNode
)
;
// Should be
Pitfall 2: Events Not Firing
Problem
Click/mouseenter events don't trigger
Cause
Missing raycaster or cursor Solution :

< a-camera

< a-cursor raycaster = " objects: .interactive "

</ a-cursor

</ a-camera

< a-box class = " interactive " position = " 0 1 -3 "

</ a-box

<
a-entity
raycaster
=
"
objects: [geometry]
"
cursor
>
</
a-entity
>
Pitfall 3: Performance Degradation
Problem
Low FPS with many entities
Causes
:
Too many draw calls
Complex geometries
Unoptimized textures
Too many tick() updates
Solutions
:
// 1. Use object pooling (see Performance section)
// 2. Simplify geometry
// 3. Optimize textures (reduce size, use compression)
// 4. Throttle updates
AFRAME
.
registerComponent
(
'optimize-far-entities'
,
{
tick
:
function
(
)
{
const
camera
=
this
.
el
.
sceneEl
.
camera
;
const
entities
=
document
.
querySelectorAll
(
'[geometry]'
)
;
entities
.
forEach
(
el
=>
{
const
distance
=
el
.
object3D
.
position
.
distanceTo
(
camera
.
position
)
;
// Hide distant entities
el
.
object3D
.
visible
=
distance
<
50
;
}
)
;
}
}
)
;
Pitfall 4: Z-Fighting (Overlapping Surfaces)
Problem
Flickering when surfaces overlap
Cause
Two surfaces at same position Solution :

< a-plane position = " 0 0.01 0 " rotation = " -90 0 0 "

</ a-plane

< a-plane position = " 0 0.02 0 " rotation = " -90 0 0 "

</ a-plane

<
a-entity
geometry
=
"
primitive: plane
"
material
=
"
src: #texture1; transparent: true
"
class
=
"
has-render-order
"
>
</
a-entity
>
<
script
>
document
.
querySelector
(
'.has-render-order'
)
.
object3D
.
renderOrder
=
1
;
</
script
>
Pitfall 5: Mobile VR Performance
Problem
Low performance on mobile VR Solutions :

< a-scene renderer = " maxCanvasWidth: 1920; maxCanvasHeight: 1920 "

< a-sphere radius = " 1 " segments-width = " 8 " segments-height = " 6 "

</ a-sphere

< a-entity light = " type: ambient; intensity: 0.6 "

</ a-entity

< a-entity light = " type: directional; intensity: 0.4 " position = " 1 2 1 "

</ a-entity

<
a-scene
renderer
=
"
antialias: false
"
>
</
a-scene
>
Pitfall 6: Asset Loading Issues
Problem
Assets not loading or CORS errors Solutions :

< a-assets

< img id = " texture " src = " https://example.com/texture.jpg " crossorigin = " anonymous "

</ a-assets

< script

const assets = document . querySelector ( 'a-assets' ) ; assets . addEventListener ( 'loaded' , ( ) => { console . log ( 'All assets loaded' ) ; // Safe to use assets now } ) ; assets . addEventListener ( 'timeout' , ( ) => { console . error ( 'Asset loading timeout' ) ; } ) ; </ script

< script

const img = document . querySelector ( 'img#texture' ) ; img . addEventListener ( 'error' , ( ) => { console . error ( 'Failed to load texture' ) ; // Use fallback img . src = 'fallback-texture.jpg' ; } ) ; </ script

Resources A-Frame Documentation A-Frame GitHub A-Frame School A-Frame Community Components WebXR Device API Three.js Documentation (A-Frame built on Three.js)

返回排行榜