Skip to content

Commit

Permalink
Merge pull request #45 from code-hike/change-vertical-center
Browse files Browse the repository at this point in the history
Better centering logic
  • Loading branch information
pomber authored Mar 23, 2021
2 parents 0942082 + 6c84da7 commit d0d38f0
Show file tree
Hide file tree
Showing 5 changed files with 346 additions and 170 deletions.
285 changes: 163 additions & 122 deletions packages/smooth-lines/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Line,
useLineTransitions,
} from "./line-transitions"
import { Lines } from "./lines"
import { easing, tween } from "./tween"

export { SmoothLines }
Expand All @@ -19,11 +20,10 @@ type Props = {
nextFocus: number[]
overscroll?: boolean
center?: boolean
minZoom?: number
maxZoom?: number
}

const OFF_OPACITY = 0.33

function SmoothLines({
progress,
containerHeight,
Expand All @@ -35,65 +35,14 @@ function SmoothLines({
prevFocus,
nextFocus,
center,
minZoom = 0, // TODO use minZoom
maxZoom = 1.2,
}: Props) {
const lines = useLineTransitions(prevLines, nextLines)
const prevExtremes = [
Math.min(...prevFocus),
Math.max(...prevFocus),
]
const nextExtremes = [
Math.min(...nextFocus),
Math.max(...nextFocus),
]
const prevCenter =
(prevExtremes[0] + prevExtremes[1] + 1) / 2
const nextCenter =
(nextExtremes[0] + nextExtremes[1] + 1) / 2
const yCenter =
tween(
{
fixed: false,
// TODO use verticalInterval
interval: [0, 1],
extremes: [prevCenter, nextCenter],
ease: easing.easeInOutCubic,
},
progress
) * lineHeight

const prevFocusHeight =
(prevExtremes[1] - prevExtremes[0] + 3) * lineHeight
const nextFocusHeight =
(nextExtremes[1] - nextExtremes[0] + 3) * lineHeight
const focusHeight = tween(
{
fixed: false,
interval: [0, 1],
extremes: [prevFocusHeight, nextFocusHeight],
},
progress
)

const lw = Array.isArray(lineWidth)
? tween(
{
fixed: false,
interval: [0, 1],
extremes: lineWidth,
},
progress
)
const focusWidth = Array.isArray(lineWidth)
? tweenProp(lineWidth[0], lineWidth[1], progress)
: lineWidth
const zoom = Math.min(
containerWidth / lw,
containerHeight / focusHeight,
maxZoom
)

const left = center
? containerWidth / 2 - (lw * zoom) / 2
: 0

const prevFocusKeys = prevFocus.map(
index => prevLines[index]?.key
Expand All @@ -102,78 +51,170 @@ function SmoothLines({
index => nextLines[index]?.key
)

const [prevZoom, prevDX, prevDY] = getContentProps({
containerWidth,
containerHeight,
lineWidth: Array.isArray(lineWidth)
? lineWidth[0]
: lineWidth,
lineHeight,
maxZoom,
horizontalCenter: !!center,
focusLineIndexList: prevFocus,
originalContentHeight: prevLines.length * lineHeight,
})
const [nextZoom, nextDX, nextDY] = getContentProps({
containerWidth,
containerHeight,
lineWidth: Array.isArray(lineWidth)
? lineWidth[1]
: lineWidth,
lineHeight,
maxZoom,
horizontalCenter: !!center,
focusLineIndexList: nextFocus,
originalContentHeight: nextLines.length * lineHeight,
})

const zoom = tweenProp(prevZoom, nextZoom, progress)
const dx = tweenProp(prevDX, nextDX, progress)
const dy = tweenProp(prevDY, nextDY, progress)

return (
<Container
width={containerWidth}
height={containerHeight}
>
<Content dx={dx} dy={dy} scale={zoom}>
<Lines
lines={lines}
prevFocusKeys={prevFocusKeys}
nextFocusKeys={nextFocusKeys}
focusWidth={focusWidth}
lineHeight={lineHeight}
progress={progress}
/>
</Content>
</Container>
)
}

function getContentProps({
containerWidth,
containerHeight,
lineWidth,
lineHeight,
maxZoom,
focusLineIndexList,
originalContentHeight,
horizontalCenter,
}: {
containerWidth: number
containerHeight: number
lineWidth: number
lineHeight: number
maxZoom: number
focusLineIndexList: number[]
originalContentHeight: number
horizontalCenter: boolean
}) {
const extremes = [
Math.min(...focusLineIndexList),
Math.max(...focusLineIndexList),
]
const originalFocusHeight =
(extremes[1] - extremes[0] + 3) * lineHeight
const zoom = Math.min(
containerWidth / lineWidth,
containerHeight / originalFocusHeight,
maxZoom
)

const contentHeight = originalContentHeight * zoom

const focusStart = (extremes[0] - 1) * lineHeight * zoom
const focusEnd = (extremes[1] + 2) * lineHeight * zoom
const focusCenter = (focusEnd + focusStart) / 2

const dy =
containerHeight > contentHeight
? (containerHeight - contentHeight) / 2
: clamp(
containerHeight / 2 - focusCenter,
containerHeight - contentHeight,
0
)

const dx = horizontalCenter
? containerWidth / 2 - (lineWidth * zoom) / 2
: 0

return [zoom, dx, dy] as const
}

function Container({
width,
height,
children,
}: {
width: number
height: number
children: React.ReactNode
}) {
return (
<div
style={{
width: containerWidth,
height: containerHeight,
// background: "salmon",
width,
height,
position: "relative",
// outline: "1px solid pink",
}}
>
<div
style={{
position: "absolute",
top: 0,
left: 0,
transform: `translateY(${
containerHeight / 2 - yCenter * zoom
}px) translateX(${left}px) scale(${zoom})`,
// outline: "5px solid green",
}}
>
{lines.map(
({
element,
key,
tweenX,
tweenY,
elementWithProgress,
}) => {
const dx = tween(tweenX, progress)
const dy = tween(tweenY, progress)

const opacity =
tween(
{
fixed: false,
extremes: [
prevFocusKeys.includes(key)
? 0.99
: OFF_OPACITY,
nextFocusKeys.includes(key)
? 0.99
: OFF_OPACITY,
],
interval: [0, 1],
},
progress
) -
Math.abs(dx) * 1

return (
<div
key={key}
style={{
position: "absolute",
top: 0,
left: 0,
transform: `translate(${dx * lw}px, ${
dy * lineHeight
}px)`,
opacity,
width: lw,
}}
>
{elementWithProgress
? elementWithProgress(progress)
: element}
</div>
)
}
)}
</div>
{children}
</div>
)
}

function Content({
dx,
dy,
scale,
children,
}: {
dx: number
dy: number
scale: number
children: React.ReactNode
}) {
return (
<div
style={{
position: "absolute",
top: 0,
left: 0,
transform: `translateX(${dx}px) translateY(${dy}px) scale(${scale})`,
}}
>
{children}
</div>
)
}

function tweenProp(
start: number,
end: number,
progress: number
) {
return tween(
{
fixed: false,
interval: [0, 1],
extremes: [start, end],
ease: easing.easeInOutCubic,
},
progress
)
}

function clamp(num: number, min: number, max: number) {
return num <= min ? min : num >= max ? max : num
}
2 changes: 1 addition & 1 deletion packages/smooth-lines/src/line-transitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type LineData = {
exitIndex: number | null
}

type LineTransition = {
export type LineTransition = {
element: React.ReactNode
elementWithProgress?: (progress: number) => Element
key: number
Expand Down
Loading

0 comments on commit d0d38f0

Please sign in to comment.