Skip to content

Commit f48f42c

Browse files
committed
Better vertical center
1 parent 0942082 commit f48f42c

File tree

4 files changed

+331
-146
lines changed

4 files changed

+331
-146
lines changed

packages/smooth-lines/src/index.tsx

Lines changed: 149 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
Line,
44
useLineTransitions,
55
} from "./line-transitions"
6+
import { Lines } from "./lines"
67
import { easing, tween } from "./tween"
78

89
export { SmoothLines }
@@ -19,11 +20,10 @@ type Props = {
1920
nextFocus: number[]
2021
overscroll?: boolean
2122
center?: boolean
23+
minZoom?: number
2224
maxZoom?: number
2325
}
2426

25-
const OFF_OPACITY = 0.33
26-
2727
function SmoothLines({
2828
progress,
2929
containerHeight,
@@ -35,9 +35,22 @@ function SmoothLines({
3535
prevFocus,
3636
nextFocus,
3737
center,
38+
minZoom = 0,
3839
maxZoom = 1.2,
3940
}: Props) {
4041
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+
4154
const prevExtremes = [
4255
Math.min(...prevFocus),
4356
Math.max(...prevFocus),
@@ -46,53 +59,79 @@ function SmoothLines({
4659
Math.min(...nextFocus),
4760
Math.max(...nextFocus),
4861
]
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 =
5467
tween(
5568
{
5669
fixed: false,
57-
// TODO use verticalInterval
5870
interval: [0, 1],
59-
extremes: [prevCenter, nextCenter],
71+
extremes: [prevFocusHeight, nextFocusHeight],
6072
ease: easing.easeInOutCubic,
6173
},
6274
progress
6375
) * lineHeight
6476

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
8877
const zoom = Math.min(
89-
containerWidth / lw,
78+
containerWidth / focusWidth,
9079
containerHeight / focusHeight,
9180
maxZoom
9281
)
9382

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+
94133
const left = center
95-
? containerWidth / 2 - (lw * zoom) / 2
134+
? containerWidth / 2 - (focusWidth * zoom) / 2
96135
: 0
97136

98137
const prevFocusKeys = prevFocus.map(
@@ -102,78 +141,89 @@ function SmoothLines({
102141
index => nextLines[index]?.key
103142
)
104143

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+
}) {
105189
return (
106190
<div
107191
style={{
108-
width: containerWidth,
109-
height: containerHeight,
110-
// background: "salmon",
192+
width,
193+
height,
111194
position: "relative",
112-
// outline: "1px solid pink",
113195
}}
114196
>
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+
}
154201

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}
177223
</div>
178224
)
179225
}
226+
227+
function clamp(num: number, min: number, max: number) {
228+
return num <= min ? min : num >= max ? max : num
229+
}

packages/smooth-lines/src/line-transitions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type LineData = {
2424
exitIndex: number | null
2525
}
2626

27-
type LineTransition = {
27+
export type LineTransition = {
2828
element: React.ReactNode
2929
elementWithProgress?: (progress: number) => Element
3030
key: number

0 commit comments

Comments
 (0)