@@ -157,6 +157,7 @@ export class ARRenderer extends EventDispatcher<
157
157
private pitchDamper = new Damper ( ) ;
158
158
private rollDamper = new Damper ( ) ;
159
159
private scaleDamper = new Damper ( ) ;
160
+ private wasTwoFingering = false ;
160
161
161
162
private onExitWebXRButtonContainerClick = ( ) => this . stopPresenting ( ) ;
162
163
@@ -813,8 +814,15 @@ export class ARRenderer extends EventDispatcher<
813
814
} else if ( fingers . length === 2 ) {
814
815
box . show = true ;
815
816
this . isTwoHandInteraction = true ;
816
- const { separation} = this . fingerPolar ( fingers ) ;
817
- this . firstRatio = separation / scene . pivot . scale . x ;
817
+ const { separation, angle} = this . fingerPolar ( fingers ) ;
818
+ this . lastAngle = angle ; // Initialize lastAngle, do not update goalYaw
819
+ if ( this . firstRatio === 0 ) {
820
+ this . firstRatio = separation / scene . pivot . scale . x ;
821
+ }
822
+ if ( scene . canScale ) {
823
+ this . setScale ( separation ) ;
824
+ }
825
+ return ;
818
826
}
819
827
} ;
820
828
@@ -829,7 +837,7 @@ export class ARRenderer extends EventDispatcher<
829
837
} ;
830
838
831
839
private fingerPolar ( fingers : XRTransientInputHitTestResult [ ] ) :
832
- { separation : number , deltaYaw : number } {
840
+ { separation : number , deltaYaw : number , angle : number } {
833
841
const fingerOne = fingers [ 0 ] . inputSource . gamepad ! . axes ;
834
842
const fingerTwo = fingers [ 1 ] . inputSource . gamepad ! . axes ;
835
843
const deltaX = fingerTwo [ 0 ] - fingerOne [ 0 ] ;
@@ -841,10 +849,10 @@ export class ARRenderer extends EventDispatcher<
841
849
} else if ( deltaYaw < - Math . PI ) {
842
850
deltaYaw += 2 * Math . PI ;
843
851
}
844
- this . lastAngle = angle ;
845
852
return {
846
853
separation : Math . sqrt ( deltaX * deltaX + deltaY * deltaY ) ,
847
- deltaYaw : deltaYaw
854
+ deltaYaw : deltaYaw ,
855
+ angle : angle
848
856
} ;
849
857
}
850
858
@@ -862,24 +870,34 @@ export class ARRenderer extends EventDispatcher<
862
870
const scene = this . presentedScene ! ;
863
871
const scale = scene . pivot . scale . x ;
864
872
865
- // Check for two-finger interaction first
873
+ // Robust two-finger gesture handling
866
874
if ( fingers . length === 2 ) {
867
- this . isTranslating = false ;
868
- this . isRotating = false ;
869
- this . isTwoHandInteraction = true ;
870
- const { separation} = this . fingerPolar ( fingers ) ;
871
- if ( this . firstRatio === 0 ) {
875
+ if ( ! this . wasTwoFingering ) {
876
+ // New two-finger gesture starting
877
+ const { separation, angle} = this . fingerPolar ( fingers ) ;
872
878
this . firstRatio = separation / scale ;
879
+ this . lastAngle = angle ;
880
+ this . wasTwoFingering = true ;
881
+ this . isTwoHandInteraction = true ;
882
+ // Do not apply rotation or scale on this frame
883
+ return ;
873
884
}
885
+ // Ongoing two-finger gesture
886
+ const { separation, deltaYaw, angle} = this . fingerPolar ( fingers ) ;
887
+ this . goalYaw += deltaYaw ;
888
+ this . lastAngle = angle ;
874
889
if ( scene . canScale ) {
875
890
this . setScale ( separation ) ;
876
891
}
892
+ this . isTwoHandInteraction = true ;
877
893
return ;
878
- } else if ( this . isTwoHandInteraction && fingers . length < 2 ) {
879
- // If we lose the second finger, stop scaling
880
- this . isTwoHandInteraction = false ;
881
- this . firstRatio = 0 ;
882
- return ;
894
+ } else {
895
+ if ( this . wasTwoFingering ) {
896
+ // Two-finger gesture ended
897
+ this . wasTwoFingering = false ;
898
+ this . isTwoHandInteraction = false ;
899
+ this . firstRatio = 0 ;
900
+ }
883
901
}
884
902
885
903
if ( ! this . isTranslating && ! this . isTwoHandInteraction && ! this . isRotating ) {
0 commit comments