优化右侧姓氏索引栏超高时的自适应缩放

This commit is contained in:
JSR
2026-03-18 19:49:38 +08:00
parent 00fc797d5a
commit 51545dd0df
+68 -19
View File
@@ -517,10 +517,54 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
const isNavDragging = useRef(false) const isNavDragging = useRef(false)
const bodyUserSelectRef = useRef("") const bodyUserSelectRef = useRef("")
const bodyWebkitUserSelectRef = useRef("") const bodyWebkitUserSelectRef = useRef("")
const [viewportHeight, setViewportHeight] = useState(() =>
typeof window === "undefined" ? 900 : window.innerHeight
)
const [navActiveKey, setNavActiveKey] = useState<string | null>(null) const [navActiveKey, setNavActiveKey] = useState<string | null>(null)
const [navIndicatorY, setNavIndicatorY] = useState(0) const [navIndicatorY, setNavIndicatorY] = useState(0)
const [isNavDraggingState, setIsNavDraggingState] = useState(false) const [isNavDraggingState, setIsNavDraggingState] = useState(false)
const quickNavLayout = useMemo(() => {
const baseItemSize = 24
const baseFontSize = 11
const basePaddingY = 8
const basePaddingX = 4
const baseBorderRadius = 20
const baseBubbleSize = 34
const baseBubbleFontSize = 13
const minScale = 0.58
const maxHeight = Math.max(120, Math.floor(viewportHeight * 0.8))
const count = groupedStudents.length
if (count <= 0) {
return {
maxHeight,
itemSize: baseItemSize,
fontSize: baseFontSize,
paddingY: basePaddingY,
paddingX: basePaddingX,
borderRadius: baseBorderRadius,
bubbleSize: baseBubbleSize,
bubbleFontSize: baseBubbleFontSize,
}
}
const baseContentHeight = count * baseItemSize + basePaddingY * 2
const rawScale = Math.min(1, maxHeight / baseContentHeight)
const scale = Math.max(minScale, rawScale)
return {
maxHeight,
itemSize: Math.max(14, baseItemSize * scale),
fontSize: Math.max(8, baseFontSize * scale),
paddingY: Math.max(4, basePaddingY * scale),
paddingX: Math.max(3, basePaddingX * scale),
borderRadius: Math.max(12, baseBorderRadius * scale),
bubbleSize: Math.max(22, baseBubbleSize * scale),
bubbleFontSize: Math.max(10, baseBubbleFontSize * scale),
}
}, [groupedStudents.length, viewportHeight])
const setNavDraggingState = useCallback((dragging: boolean) => { const setNavDraggingState = useCallback((dragging: boolean) => {
isNavDragging.current = dragging isNavDragging.current = dragging
setIsNavDraggingState(dragging) setIsNavDraggingState(dragging)
@@ -541,22 +585,23 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
if (!navContainerRef.current) return if (!navContainerRef.current) return
const rect = navContainerRef.current.getBoundingClientRect() const rect = navContainerRef.current.getBoundingClientRect()
const y = clientY - rect.top const y = clientY - rect.top
const items = navContainerRef.current.children const itemCount = groupedStudents.length
const itemCount = items.length
if (itemCount === 0) return if (itemCount === 0) return
const itemHeight = rect.height / itemCount const innerTop = quickNavLayout.paddingY
const index = Math.floor(y / itemHeight) const innerBottom = rect.height - quickNavLayout.paddingY
const clampedY = Math.max(innerTop, Math.min(innerBottom - 0.001, y))
const index = Math.floor((clampedY - innerTop) / quickNavLayout.itemSize)
const safeIndex = Math.max(0, Math.min(itemCount - 1, index)) const safeIndex = Math.max(0, Math.min(itemCount - 1, index))
const targetGroup = groupedStudents[safeIndex] const targetGroup = groupedStudents[safeIndex]
if (targetGroup) { if (targetGroup) {
setNavActiveKey(targetGroup.key) setNavActiveKey(targetGroup.key)
setNavIndicatorY((safeIndex + 0.5) * itemHeight) setNavIndicatorY(quickNavLayout.paddingY + (safeIndex + 0.5) * quickNavLayout.itemSize)
scrollToGroup(targetGroup.key) scrollToGroup(targetGroup.key)
} }
}, },
[groupedStudents] [groupedStudents, quickNavLayout.itemSize, quickNavLayout.paddingY]
) )
const onNavMouseDown = (e: React.MouseEvent) => { const onNavMouseDown = (e: React.MouseEvent) => {
@@ -599,6 +644,12 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
setNavDraggingState(false) setNavDraggingState(false)
} }
useEffect(() => {
const onResize = () => setViewportHeight(window.innerHeight)
window.addEventListener("resize", onResize)
return () => window.removeEventListener("resize", onResize)
}, [])
useEffect(() => { useEffect(() => {
return () => { return () => {
document.removeEventListener("mousemove", onGlobalMouseMove) document.removeEventListener("mousemove", onGlobalMouseMove)
@@ -631,9 +682,7 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
setNavActiveKey(currentKey) setNavActiveKey(currentKey)
const idx = groupedStudents.findIndex((g) => g.key === currentKey) const idx = groupedStudents.findIndex((g) => g.key === currentKey)
if (idx >= 0 && navContainerRef.current) { if (idx >= 0 && navContainerRef.current) {
const rect = navContainerRef.current.getBoundingClientRect() setNavIndicatorY(quickNavLayout.paddingY + (idx + 0.5) * quickNavLayout.itemSize)
const itemHeight = rect.height / groupedStudents.length
setNavIndicatorY((idx + 0.5) * itemHeight)
} }
} }
} }
@@ -645,7 +694,7 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
window.removeEventListener("scroll", refreshActiveByScroll) window.removeEventListener("scroll", refreshActiveByScroll)
window.removeEventListener("resize", refreshActiveByScroll) window.removeEventListener("resize", refreshActiveByScroll)
} }
}, [groupedStudents]) }, [groupedStudents, quickNavLayout.itemSize, quickNavLayout.paddingY])
const renderQuickNav = () => { const renderQuickNav = () => {
if ( if (
@@ -671,11 +720,11 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
backgroundColor: "var(--ss-card-bg)", backgroundColor: "var(--ss-card-bg)",
padding: "8px 4px", padding: `${quickNavLayout.paddingY}px ${quickNavLayout.paddingX}px`,
borderRadius: "20px", borderRadius: `${quickNavLayout.borderRadius}px`,
boxShadow: "0 4px 12px rgba(0,0,0,0.1)", boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
zIndex: 100, zIndex: 100,
maxHeight: "80vh", maxHeight: `${quickNavLayout.maxHeight}px`,
border: "1px solid var(--ss-border-color)", border: "1px solid var(--ss-border-color)",
cursor: "pointer", cursor: "pointer",
userSelect: "none", userSelect: "none",
@@ -688,8 +737,8 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
position: "absolute", position: "absolute",
left: "-10px", left: "-10px",
top: `${navIndicatorY}px`, top: `${navIndicatorY}px`,
width: "34px", width: `${quickNavLayout.bubbleSize}px`,
height: "34px", height: `${quickNavLayout.bubbleSize}px`,
transform: "translate(-100%, -50%)", transform: "translate(-100%, -50%)",
borderRadius: "50%", borderRadius: "50%",
backgroundColor: isNavDraggingState backgroundColor: isNavDraggingState
@@ -726,7 +775,7 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
<span <span
style={{ style={{
color: isNavDraggingState ? "#fff" : "var(--ant-color-primary, #1890ff)", color: isNavDraggingState ? "#fff" : "var(--ant-color-primary, #1890ff)",
fontSize: "13px", fontSize: `${quickNavLayout.bubbleFontSize}px`,
fontWeight: 700, fontWeight: 700,
lineHeight: 1, lineHeight: 1,
}} }}
@@ -739,12 +788,12 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
<div <div
key={group.key} key={group.key}
style={{ style={{
width: "24px", width: `${quickNavLayout.itemSize}px`,
height: "24px", height: `${quickNavLayout.itemSize}px`,
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
fontSize: "11px", fontSize: `${quickNavLayout.fontSize}px`,
fontWeight: "bold", fontWeight: "bold",
color: color:
navActiveKey === group.key navActiveKey === group.key