3
3
Line ,
4
4
useLineTransitions ,
5
5
} from "./line-transitions"
6
+ import { Lines } from "./lines"
6
7
import { easing , tween } from "./tween"
7
8
8
9
export { SmoothLines }
@@ -19,11 +20,10 @@ type Props = {
19
20
nextFocus : number [ ]
20
21
overscroll ?: boolean
21
22
center ?: boolean
23
+ minZoom ?: number
22
24
maxZoom ?: number
23
25
}
24
26
25
- const OFF_OPACITY = 0.33
26
-
27
27
function SmoothLines ( {
28
28
progress,
29
29
containerHeight,
@@ -35,9 +35,22 @@ function SmoothLines({
35
35
prevFocus,
36
36
nextFocus,
37
37
center,
38
+ minZoom = 0 ,
38
39
maxZoom = 1.2 ,
39
40
} : Props ) {
40
41
const lines = useLineTransitions ( prevLines , nextLines )
42
+
43
+ const focusWidth = Array . isArray ( lineWidth )
44
+ ? tween (
45
+ {
46
+ fixed : false ,
47
+ interval : [ 0 , 1 ] ,
48
+ extremes : lineWidth ,
49
+ } ,
50
+ progress
51
+ )
52
+ : lineWidth
53
+
41
54
const prevExtremes = [
42
55
Math . min ( ...prevFocus ) ,
43
56
Math . max ( ...prevFocus ) ,
@@ -46,53 +59,79 @@ function SmoothLines({
46
59
Math . min ( ...nextFocus ) ,
47
60
Math . max ( ...nextFocus ) ,
48
61
]
49
- const prevCenter =
50
- ( prevExtremes [ 0 ] + prevExtremes [ 1 ] + 1 ) / 2
51
- const nextCenter =
52
- ( nextExtremes [ 0 ] + nextExtremes [ 1 ] + 1 ) / 2
53
- const yCenter =
62
+ const prevFocusHeight =
63
+ prevExtremes [ 1 ] - prevExtremes [ 0 ] + 3
64
+ const nextFocusHeight =
65
+ nextExtremes [ 1 ] - nextExtremes [ 0 ] + 3
66
+ const focusHeight =
54
67
tween (
55
68
{
56
69
fixed : false ,
57
- // TODO use verticalInterval
58
70
interval : [ 0 , 1 ] ,
59
- extremes : [ prevCenter , nextCenter ] ,
71
+ extremes : [ prevFocusHeight , nextFocusHeight ] ,
60
72
ease : easing . easeInOutCubic ,
61
73
} ,
62
74
progress
63
75
) * lineHeight
64
76
65
- const prevFocusHeight =
66
- ( prevExtremes [ 1 ] - prevExtremes [ 0 ] + 3 ) * lineHeight
67
- const nextFocusHeight =
68
- ( nextExtremes [ 1 ] - nextExtremes [ 0 ] + 3 ) * lineHeight
69
- const focusHeight = tween (
70
- {
71
- fixed : false ,
72
- interval : [ 0 , 1 ] ,
73
- extremes : [ prevFocusHeight , nextFocusHeight ] ,
74
- } ,
75
- progress
76
- )
77
-
78
- const lw = Array . isArray ( lineWidth )
79
- ? tween (
80
- {
81
- fixed : false ,
82
- interval : [ 0 , 1 ] ,
83
- extremes : lineWidth ,
84
- } ,
85
- progress
86
- )
87
- : lineWidth
88
77
const zoom = Math . min (
89
- containerWidth / lw ,
78
+ containerWidth / focusWidth ,
90
79
containerHeight / focusHeight ,
91
80
maxZoom
92
81
)
93
82
83
+ const contentHeight =
84
+ tween (
85
+ {
86
+ fixed : false ,
87
+ interval : [ 0 , 1 ] ,
88
+ extremes : [ prevLines . length , nextLines . length ] ,
89
+ ease : easing . easeInOutCubic ,
90
+ } ,
91
+ progress
92
+ ) *
93
+ lineHeight *
94
+ zoom
95
+ const focusStart =
96
+ tween (
97
+ {
98
+ fixed : false ,
99
+ interval : [ 0 , 1 ] ,
100
+ extremes : [
101
+ prevExtremes [ 0 ] - 1 ,
102
+ nextExtremes [ 0 ] - 1 ,
103
+ ] ,
104
+ ease : easing . easeInOutCubic ,
105
+ } ,
106
+ progress
107
+ ) *
108
+ lineHeight *
109
+ zoom
110
+ const focusEnd =
111
+ tween (
112
+ {
113
+ fixed : false ,
114
+ interval : [ 0 , 1 ] ,
115
+ extremes : [
116
+ prevExtremes [ 1 ] + 2 ,
117
+ nextExtremes [ 1 ] + 2 ,
118
+ ] ,
119
+ ease : easing . easeInOutCubic ,
120
+ } ,
121
+ progress
122
+ ) *
123
+ lineHeight *
124
+ zoom
125
+
126
+ const dy = getDY (
127
+ containerHeight ,
128
+ contentHeight ,
129
+ focusStart ,
130
+ focusEnd
131
+ )
132
+
94
133
const left = center
95
- ? containerWidth / 2 - ( lw * zoom ) / 2
134
+ ? containerWidth / 2 - ( focusWidth * zoom ) / 2
96
135
: 0
97
136
98
137
const prevFocusKeys = prevFocus . map (
@@ -102,78 +141,89 @@ function SmoothLines({
102
141
index => nextLines [ index ] ?. key
103
142
)
104
143
144
+ return (
145
+ < Container
146
+ width = { containerWidth }
147
+ height = { containerHeight }
148
+ >
149
+ < Content dx = { left } dy = { dy } scale = { zoom } >
150
+ < Lines
151
+ lines = { lines }
152
+ prevFocusKeys = { prevFocusKeys }
153
+ nextFocusKeys = { nextFocusKeys }
154
+ focusWidth = { focusWidth }
155
+ lineHeight = { lineHeight }
156
+ progress = { progress }
157
+ />
158
+ </ Content >
159
+ </ Container >
160
+ )
161
+ }
162
+
163
+ function getDY (
164
+ containerHeight : number ,
165
+ contentHeight : number ,
166
+ focusStart : number ,
167
+ focusEnd : number
168
+ ) {
169
+ if ( containerHeight > contentHeight ) {
170
+ return ( containerHeight - contentHeight ) / 2
171
+ }
172
+ const focusCenter = ( focusEnd + focusStart ) / 2
173
+ return clamp (
174
+ containerHeight / 2 - focusCenter ,
175
+ containerHeight - contentHeight ,
176
+ 0
177
+ )
178
+ }
179
+
180
+ function Container ( {
181
+ width,
182
+ height,
183
+ children,
184
+ } : {
185
+ width : number
186
+ height : number
187
+ children : React . ReactNode
188
+ } ) {
105
189
return (
106
190
< div
107
191
style = { {
108
- width : containerWidth ,
109
- height : containerHeight ,
110
- // background: "salmon",
192
+ width,
193
+ height,
111
194
position : "relative" ,
112
- // outline: "1px solid pink",
113
195
} }
114
196
>
115
- < div
116
- style = { {
117
- position : "absolute" ,
118
- top : 0 ,
119
- left : 0 ,
120
- transform : `translateY(${
121
- containerHeight / 2 - yCenter * zoom
122
- } px) translateX(${ left } px) scale(${ zoom } )`,
123
- // outline: "5px solid green",
124
- } }
125
- >
126
- { lines . map (
127
- ( {
128
- element,
129
- key,
130
- tweenX,
131
- tweenY,
132
- elementWithProgress,
133
- } ) => {
134
- const dx = tween ( tweenX , progress )
135
- const dy = tween ( tweenY , progress )
136
-
137
- const opacity =
138
- tween (
139
- {
140
- fixed : false ,
141
- extremes : [
142
- prevFocusKeys . includes ( key )
143
- ? 0.99
144
- : OFF_OPACITY ,
145
- nextFocusKeys . includes ( key )
146
- ? 0.99
147
- : OFF_OPACITY ,
148
- ] ,
149
- interval : [ 0 , 1 ] ,
150
- } ,
151
- progress
152
- ) -
153
- Math . abs ( dx ) * 1
197
+ { children }
198
+ </ div >
199
+ )
200
+ }
154
201
155
- return (
156
- < div
157
- key = { key }
158
- style = { {
159
- position : "absolute" ,
160
- top : 0 ,
161
- left : 0 ,
162
- transform : `translate(${ dx * lw } px, ${
163
- dy * lineHeight
164
- } px)`,
165
- opacity,
166
- width : lw ,
167
- } }
168
- >
169
- { elementWithProgress
170
- ? elementWithProgress ( progress )
171
- : element }
172
- </ div >
173
- )
174
- }
175
- ) }
176
- </ div >
202
+ function Content ( {
203
+ dx,
204
+ dy,
205
+ scale,
206
+ children,
207
+ } : {
208
+ dx : number
209
+ dy : number
210
+ scale : number
211
+ children : React . ReactNode
212
+ } ) {
213
+ return (
214
+ < div
215
+ style = { {
216
+ position : "absolute" ,
217
+ top : 0 ,
218
+ left : 0 ,
219
+ transform : `translateX(${ dx } px) translateY(${ dy } px) scale(${ scale } )` ,
220
+ } }
221
+ >
222
+ { children }
177
223
</ div >
178
224
)
179
225
}
226
+
227
+ function clamp ( num : number , min : number , max : number ) {
228
+ return num <= min ? min : num >= max ? max : num
229
+ }
0 commit comments