11import React , { PureComponent } from 'react'
2- import { View , TouchableWithoutFeedback , StyleSheet , PanResponder , Animated } from 'react-native'
2+ import { View , ScrollView , TouchableWithoutFeedback , StyleSheet , Animated , Easing } from 'react-native'
33import Sprite from '../sprite'
44import options from '../../config'
55
6+ const cannonHalf = options . cannonSize / 2
7+
68export default class Controls extends PureComponent {
79 constructor ( props ) {
810 super ( props )
11+ this . scrollView = React . createRef ( )
12+ this . cannonXPosition = this . props . width / 2
13+ this . translateY = new Animated . Value ( 0 )
14+ this . opacity = new Animated . Value ( 1 )
15+ }
916
10- this . state = {
11- width : this . props . width ,
12- translateX : new Animated . Value ( 0 ) ,
13- }
14-
15- // coolDown qui per evitare il re-render del componente
16- this . coolDown = false
17- this . cannonXPosition = this . state . width / 2
18-
19- this . cannonRef = React . createRef ( )
20-
21- this . _panResponder = PanResponder . create ( {
22-
23- // Il pan responder è attivo, ma risponde solo allo scrolling (?)
24- onMoveShouldSetPanResponder : ( e , gestureState ) => true ,
25-
26- // A inizio scrolling imposta un offset per partire dall'ultima posizione, e non dal centro
27- onPanResponderGrant : ( e , gestureState ) => {
28- this . state . translateX . setOffset ( this . state . translateX . _value )
29- } ,
30-
31- // Allo scrolling prende il valore dello spostamento sull'asse X, che sarà usato in un transform
32- onPanResponderMove : ( e , gestureState ) => {
33-
34- // Posizione corrente del cannone (il suo centro)
35- this . cannonRef . current . measure ( ( x , y , elWidth , elHeight , posX , posY ) => {
36- this . cannonXPosition = posX
37- } )
38-
39- //console.log(this.cannonXPosition)
40- if ( this . cannonXPosition > 25 && this . cannonXPosition < ( this . state . width - 25 ) ) {
41- this . state . translateX . setValue ( gestureState . dx )
42- } else {
43- //Se va fuori schermo, imposta un dX più o meno pari a metà cannone per riportarlo dentro
44- //const safeDx = gestureState.dx + (gestureState.dx < 0 ? 25 : -25)
45- const safeDx = gestureState . dx + ( gestureState . dx < 0 ? - 25 : - 25 )
46- this . state . translateX . setValue ( safeDx )
47-
48- }
49- //const boundaries = this._calcBoundaries(e.nativeEvent.locationX)
50- } ,
51-
52- // A fine scrolling, aggiunge l'offset al valore finale e lo reimposta a 0
53- onPanResponderRelease : ( e , gestureState ) => {
54- this . state . translateX . flattenOffset ( )
55- this . props . updatePlayerPosition ( this . cannonXPosition )
17+ componentDidMount ( ) {
18+ const { width } = this . props
19+ // Centra il cannone
20+ // Senza timeout non chiama scrollTo.. perché?
21+ setTimeout ( ( ) => this . scrollView . current . scrollTo ( { x : width / 2 - cannonHalf , y : 0 , animated : false } ) , 250 )
22+ Animated . timing (
23+ this . translateY ,
24+ {
25+ toValue : - options . cannonSize ,
26+ easing : Easing . bezier ( .04 , .38 , .18 , .93 ) ,
27+ delay : 200 ,
28+ duration : 600 ,
29+ useNativeDriver : true
5630 }
57-
58- } )
31+ ) . start ( )
5932 }
6033
34+ componentDidUpdate ( prevProps , prevState ) {
35+ if ( this . props . lives > 0 && prevProps . lives !== this . props . lives ) {
36+ Animated . sequence ( [
37+ Animated . timing ( this . opacity , {
38+ toValue : 0.2 ,
39+ easing : Easing . linear ,
40+ duration : 80 ,
41+ useNativeDriver : true
42+ } ) ,
43+ Animated . timing ( this . opacity , {
44+ toValue : 1 ,
45+ easing : Easing . linear ,
46+ duration : 80 ,
47+ useNativeDriver : true
48+ } )
49+ ] ) . start ( )
50+ }
51+ }
6152
62- afire = ( ) => {
63- const { fire, height } = this . props
53+ fire = ( ) => {
54+ const { fire } = this . props
6455
6556 // Passa a fire() la posizione del cannone, per sincronizzare il proiettile
6657 if ( ! this . coolDown ) {
67- fire ( { x : this . cannonXPosition , y : 50 } )
58+ fire ( { x : this . cannonXPosition , y : options . cannonSize } )
6859 this . coolDown = true
6960 setTimeout ( ( ) => this . coolDown = false , options . rocketCoolDown )
7061 }
7162
7263 }
7364
65+ calculateCannonPosition ( offset ) {
66+ const { width, updatePlayerPosition } = this . props
67+ const currentPosition = ( width - options . cannonSize ) - offset
68+ this . cannonXPosition = currentPosition
69+ // Posizione cannone: offset da inizio schermo a sinista fino a lato sinistro della view
70+ updatePlayerPosition ( this . cannonXPosition )
71+ }
72+
7473 render ( ) {
75- const { translateX } = this . state
76- const { height, winner } = this . props
74+ const { width, height } = this . props
7775
7876 console . log ( 'Controls rendered' )
7977
80- // Il dx è passato a transform, per traslare la View del valore dx
81- const animatedStyle = { transform : [ { translateX } ] }
78+ const animatedStyle = { transform : [ { translateY : this . translateY } ] }
79+ const touchableArea = [ styles . innerView , { width : width * 2 - options . cannonSize } ]
8280
8381 return (
84- < View style = { [ styles . outer , { height : height / 2 } ] } { ...this . _panResponder . panHandlers } >
85-
86- < View style = { styles . inner } >
87-
88- < Animated . View
89- style = { animatedStyle }
90- //onLayout={({nativeEvent}) => this.cannonXPosition = nativeEvent.layout.x}
91- >
92- < TouchableWithoutFeedback onPress = { this . afire } >
93- < View ref = { this . cannonRef } >
94- { winner !== 2 && < Sprite image = 'cannon' /> }
95- </ View >
96- </ TouchableWithoutFeedback >
97- </ Animated . View >
98-
99- </ View >
100-
101- </ View >
82+ < Animated . View style = { [ styles . base , { height : height / 2 , ...animatedStyle } ] } >
83+ < ScrollView
84+ ref = { this . scrollView }
85+ horizontal
86+ bounces = { false }
87+ showsHorizontalScrollIndicator = { false }
88+ overScrollMode = 'never'
89+ decelerationRate = { 0.01 }
90+ scrollEventThrottle = { 50 }
91+ onScroll = { ( { nativeEvent } ) => this . calculateCannonPosition ( nativeEvent . contentOffset . x ) }
92+ >
93+ < TouchableWithoutFeedback onPress = { this . fire } >
94+ < View style = { touchableArea } >
95+ < Animated . View style = { [ styles . flashView , { opacity : this . opacity } ] } >
96+ < Sprite image = 'cannon' width = { options . cannonSize } />
97+ </ Animated . View >
98+ </ View >
99+ </ TouchableWithoutFeedback >
100+ </ ScrollView >
101+ </ Animated . View >
102102 )
103103 }
104104}
105105
106106const styles = StyleSheet . create ( {
107- outer : {
108- //backgroundColor: 'green',
107+ base : {
109108 position : 'absolute' ,
110- justifyContent : 'flex-end' ,
111- bottom : 4 ,
109+ bottom : - options . cannonSize ,
112110 left : 0 ,
113- right : 0 ,
114111 zIndex : 2
115112 } ,
116- inner : {
117- //backgroundColor: 'red',
118- height : 40 ,
119- justifyContent : 'center' ,
120- alignItems : 'center'
113+ innerView : {
114+ //backgroundColor: 'orangered',
115+ flexDirection : 'row' ,
116+ alignItems : 'flex-end' ,
117+ justifyContent : 'center'
118+ } ,
119+ flashView : {
120+ // Senza background, su Android non fa l'animazione alla prima chiamata a CDU (!?)
121+ backgroundColor : 'rgba(0,0,0,0)'
121122 }
122123} )
0 commit comments