@@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
6
6
import _ from 'lodash' ;
7
7
import Reanimated , { Easing } from 'react-native-reanimated' ;
8
8
import { State } from 'react-native-gesture-handler' ;
9
- import { timing , fract } from 'react-native-redash' ;
9
+ import { timing , fract , between } from 'react-native-redash' ;
10
10
import { Constants } from '../../helpers' ;
11
11
import TabBarContext from './TabBarContext' ;
12
12
import TabBar from './TabBar' ;
@@ -16,12 +16,16 @@ import PageCarousel from './PageCarousel';
16
16
17
17
const {
18
18
and,
19
+ abs,
19
20
cond,
20
21
call,
21
22
Code,
22
23
Clock,
23
24
clockRunning,
25
+ diff,
24
26
eq,
27
+ floor,
28
+ lessThan,
25
29
neq,
26
30
not,
27
31
set,
@@ -52,10 +56,6 @@ class TabController extends Component {
52
56
* callback for when index has change (will not be called on ignored items)
53
57
*/
54
58
onChangeIndex : PropTypes . func ,
55
- // /**
56
- // * callback for when tab selected
57
- // */
58
- // onTabSelected: PropTypes.func,
59
59
/**
60
60
* When using TabController.PageCarousel this should be turned on
61
61
*/
@@ -67,36 +67,22 @@ class TabController extends Component {
67
67
activeOpacity : 0.2
68
68
} ;
69
69
70
- state = {
71
- selectedIndex : this . props . selectedIndex ,
72
- itemStates : [ ]
73
- } ;
74
-
75
- _targetPage = new Value ( this . props . selectedIndex ) ;
76
- _currentPage = new Value ( this . props . selectedIndex ) ;
77
- _carouselOffset = new Value ( this . props . selectedIndex * Math . round ( Constants . screenWidth ) ) ;
78
-
79
- shouldComponentUpdate ( nextProps ) {
80
- if ( nextProps . selectedIndex !== this . props . selectedIndex ) {
81
- return false ;
82
- }
83
- return true ;
84
- }
85
-
86
- getProviderContextValue = ( ) => {
87
- const { itemStates, selectedIndex} = this . state ;
88
- const { onChangeIndex, asCarousel} = this . props ;
89
- return {
90
- selectedIndex,
91
- currentPage : this . _currentPage ,
92
- targetPage : this . _targetPage ,
93
- carouselOffset : this . _carouselOffset ,
94
- itemStates,
70
+ constructor ( props ) {
71
+ super ( props ) ;
72
+
73
+ this . state = {
74
+ selectedIndex : this . props . selectedIndex ,
75
+ asCarousel : this . props . asCarousel ,
76
+ itemStates : [ ] ,
77
+ // animated values
78
+ targetPage : new Value ( this . props . selectedIndex ) ,
79
+ currentPage : new Value ( this . props . selectedIndex ) ,
80
+ carouselOffset : new Value ( this . props . selectedIndex * Math . round ( Constants . screenWidth ) ) ,
81
+ // // callbacks
95
82
registerTabItems : this . registerTabItems ,
96
- onChangeIndex,
97
- asCarousel
83
+ onChangeIndex : this . props . onChangeIndex
98
84
} ;
99
- } ;
85
+ }
100
86
101
87
registerTabItems = ( tabItemsCount , ignoredItems ) => {
102
88
const itemStates = _ . times ( tabItemsCount , ( ) => new Value ( State . UNDETERMINED ) ) ;
@@ -108,11 +94,13 @@ class TabController extends Component {
108
94
} ;
109
95
110
96
renderCodeBlock = ( ) => {
111
- const { itemStates, ignoredItems} = this . state ;
97
+ const { itemStates, ignoredItems, currentPage , targetPage , carouselOffset } = this . state ;
112
98
const { selectedIndex} = this . props ;
113
99
const clock = new Clock ( ) ;
114
100
const fromPage = new Value ( selectedIndex ) ;
115
101
const toPage = new Value ( selectedIndex ) ;
102
+ const isAnimating = new Value ( 0 ) ;
103
+ const isScrolling = new Value ( 0 ) ;
116
104
117
105
return block ( [
118
106
/* Page change by TabBar */
@@ -123,38 +111,48 @@ class TabController extends Component {
123
111
cond ( and ( eq ( state , State . END ) , ! ignoredItem ) , [
124
112
set ( fromPage , toPage ) ,
125
113
set ( toPage , index ) ,
126
- set ( this . _targetPage , index )
114
+ set ( targetPage , index )
127
115
] ) )
128
116
] ;
129
117
} ) ,
130
118
131
- cond ( neq ( this . _currentPage , toPage ) ,
132
- set ( this . _currentPage ,
133
- timing ( { clock, from : fromPage , to : toPage , duration : 300 , easing : Easing . bezier ( 0.34 , 1.56 , 0.64 , 1 ) } ) ) ) ,
134
-
135
- /* Page change by Carousel */
136
- onChange ( this . _carouselOffset , [
137
- cond ( not ( clockRunning ( clock ) ) , [
138
- set ( this . _currentPage ,
139
- interpolate ( round ( this . _carouselOffset ) , {
119
+ // Animate currentPage to its target
120
+ cond ( neq ( currentPage , toPage ) , [
121
+ set ( isAnimating , 1 ) ,
122
+ set ( currentPage ,
123
+ timing ( { clock, from : fromPage , to : toPage , duration : 300 , easing : Easing . bezier ( 0.34 , 1.56 , 0.64 , 1 ) } ) )
124
+ ] ) ,
125
+ // Set isAnimating flag off
126
+ cond ( and ( eq ( isAnimating , 1 ) , not ( clockRunning ( clock ) ) ) , set ( isAnimating , 0 ) ) ,
127
+
128
+ /* Page change by Carousel scroll */
129
+ onChange ( carouselOffset , [
130
+ set ( isScrolling , lessThan ( round ( abs ( diff ( carouselOffset ) ) ) , round ( Constants . screenWidth ) ) ) ,
131
+ cond ( and ( not ( isAnimating ) ) , [
132
+ set ( currentPage ,
133
+ interpolate ( round ( carouselOffset ) , {
140
134
inputRange : itemStates . map ( ( v , i ) => Math . round ( i * Constants . screenWidth ) ) ,
141
135
outputRange : itemStates . map ( ( v , i ) => i )
142
136
} ) ) ,
143
- set ( toPage , this . _currentPage ) ,
144
- cond ( eq ( fract ( this . _currentPage ) , 0 ) , set ( this . _targetPage , this . _currentPage ) )
137
+ set ( toPage , currentPage )
145
138
] )
146
139
] ) ,
140
+ // Update/Sync target page when scrolling is done
141
+ cond ( and ( eq ( isScrolling , 1 ) , eq ( floor ( abs ( diff ( carouselOffset ) ) ) , 0 ) ) , [
142
+ set ( isScrolling , 0 ) ,
143
+ cond ( not ( between ( fract ( currentPage ) , 0.1 , 0.9 , 1 ) ) , set ( targetPage , round ( currentPage ) ) )
144
+ ] ) ,
147
145
148
146
/* Invoke index change */
149
- onChange ( toPage , call ( [ toPage ] , this . onPageChange ) )
147
+ onChange ( targetPage , call ( [ targetPage ] , this . onPageChange ) )
150
148
] ) ;
151
149
} ;
152
150
153
151
render ( ) {
154
152
const { itemStates} = this . state ;
155
153
156
154
return (
157
- < TabBarContext . Provider value = { this . getProviderContextValue ( ) } >
155
+ < TabBarContext . Provider value = { this . state } >
158
156
{ this . props . children }
159
157
{ ! _ . isEmpty ( itemStates ) && < Code > { this . renderCodeBlock } </ Code > }
160
158
</ TabBarContext . Provider >
0 commit comments