Auto Focus - 自动聚焦
一个基于滚动视图的自动聚焦Hook,支持横向和纵向滚动,可以实现列表项的自动定位和居中展示。
预览
使用示例
import useAutoFoucsScroll from '@/hooks/useAutoFoucsScroll';
import { cn } from '@/utils';
import { ScrollView, View } from '@tarojs/components';
import { getWindowInfo } from '@tarojs/taro';
import { useState } from 'react';
const data = new Array(20).fill(0).map((_, index) => `${index}`);
const { windowWidth } = getWindowInfo();
export default function Page() {
const containerWidth = windowWidth * 0.8;
const { id, scrollPosition, itemSize, handleChangeIndex } = useAutoFoucsScroll({
id: 'scroll-view',
itemNum: 5,
containerSize: containerWidth,
scrollType: 'x',
// backspace: 1,
});
const [currentIndex, setCurrentIndex] = useState(0);
const handleClickItem = (index: number) => {
setCurrentIndex(index);
handleChangeIndex(index);
};
return (
<View className="container">
<View className="rounded bg-white p-1">
<ScrollView
showScrollbar={false}
enhanced
className="whitespace-nowrap"
scrollX
id={id}
scrollWithAnimation
scrollLeft={scrollPosition}
style={{ width: containerWidth }}
>
{data.map((item, index) => (
<View
className="inline-flex items-center justify-center p-1"
style={{ width: itemSize, height: itemSize }}
key={item}
>
<View
onClick={() => handleClickItem(index)}
key={item}
className={cn(
'inline-flex size-full items-center justify-center bg-green-300 text-white',
currentIndex === index && 'bg-red-300'
)}
>
{item}
</View>
</View>
))}
</ScrollView>
</View>
</View>
);
}
下载
1
添加依赖
npm i clsx tailwind-merge
2
添加工具函数
// @/utils
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
// 合并 Tailwind CSS 类名和 clsx 类名
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
// 根据屏幕宽度计算响应式值
export const pt = (num: number) => {
const { screenWidth } = getWindowInfo();
return num * (screenWidth / 375);
};
源代码
import { createSelectorQuery, nextTick, useReady } from '@tarojs/taro';
import { useState } from 'react';
interface UseAutoFoucsScrollProps {
id: string;
// 滚动方向
scrollType?: 'x' | 'y';
// 回退的距离
backspace?: number;
// 容器的大小
containerSize: number;
// 子项的数量
itemNum: number;
}
const useAutoFoucsScroll = (props: UseAutoFoucsScrollProps) => {
const { id, scrollType = 'x', backspace = 0, containerSize, itemNum } = props;
const itemSize = containerSize / itemNum;
const [scrollViewSize, setScrollViewSize] = useState(0);
const [scrollPosition, setScrollPosition] = useState(0);
const getScrollViewRect = () => {
const query = createSelectorQuery().select(`#${id}`);
nextTick(() => {
query
.boundingClientRect(res => {
const width = Array.isArray(res) ? res[0].width : res.width;
const height = Array.isArray(res) ? res[0].height : res.height;
setScrollViewSize(scrollType === 'x' ? width : height);
})
.exec();
});
};
const getItemCenterPosition = (index: number) => {
return index * itemSize - scrollViewSize / 2 + itemSize / 2 + backspace * itemSize;
};
const handleChangeIndex = (index: number) => {
if (scrollViewSize) {
setScrollPosition(getItemCenterPosition(index));
}
};
useReady(() => {
getScrollViewRect();
});
return { id, scrollPosition, itemSize, getScrollViewRect, handleChangeIndex };
};