2026-03-09 21:29:03 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 响应式样式工具
|
|
|
|
|
|
* 提供基于断点的样式生成功能
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
import { ViewStyle } from 'react-native';
|
2026-03-18 12:11:49 +08:00
|
|
|
|
import { BREAKPOINTS } from '../presentation/hooks/responsive';
|
2026-03-09 21:29:03 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 断点键名
|
|
|
|
|
|
*/
|
|
|
|
|
|
export type ResponsiveBreakpoint = 'mobile' | 'tablet' | 'desktop' | 'wide';
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 断点值映射
|
|
|
|
|
|
*/
|
|
|
|
|
|
export type ResponsiveValues<T> = {
|
|
|
|
|
|
mobile?: T;
|
|
|
|
|
|
tablet?: T;
|
|
|
|
|
|
desktop?: T;
|
|
|
|
|
|
wide?: T;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 断点样式映射
|
|
|
|
|
|
*/
|
|
|
|
|
|
export type ResponsiveStyles = {
|
|
|
|
|
|
mobile?: ViewStyle;
|
|
|
|
|
|
tablet?: ViewStyle;
|
|
|
|
|
|
desktop?: ViewStyle;
|
|
|
|
|
|
wide?: ViewStyle;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据当前断点返回对应的值
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param breakpoint - 当前断点
|
|
|
|
|
|
* @param values - 各断点对应的值
|
|
|
|
|
|
* @returns 对应断点的值,如果未找到则返回 undefined
|
|
|
|
|
|
*
|
|
|
|
|
|
* @example
|
|
|
|
|
|
* const fontSize = responsiveValue('tablet', {
|
|
|
|
|
|
* mobile: 14,
|
|
|
|
|
|
* tablet: 16,
|
|
|
|
|
|
* desktop: 18,
|
|
|
|
|
|
* wide: 20
|
|
|
|
|
|
* });
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function responsiveValue<T>(
|
|
|
|
|
|
breakpoint: string,
|
|
|
|
|
|
values: ResponsiveValues<T>
|
|
|
|
|
|
): T | undefined {
|
|
|
|
|
|
// 按断点优先级顺序查找
|
|
|
|
|
|
const breakpoints: ResponsiveBreakpoint[] = ['wide', 'desktop', 'tablet', 'mobile'];
|
|
|
|
|
|
|
|
|
|
|
|
for (const bp of breakpoints) {
|
|
|
|
|
|
if (breakpoint === bp && values[bp] !== undefined) {
|
|
|
|
|
|
return values[bp];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return undefined;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 创建响应式样式
|
|
|
|
|
|
* 根据当前断点返回对应的样式对象
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param breakpoint - 当前断点
|
|
|
|
|
|
* @param styles - 各断点对应的样式
|
|
|
|
|
|
* @returns 合并后的样式对象
|
|
|
|
|
|
*
|
|
|
|
|
|
* @example
|
|
|
|
|
|
* const containerStyle = createResponsiveStyles(breakpoint, {
|
|
|
|
|
|
* mobile: { padding: 12 },
|
|
|
|
|
|
* tablet: { padding: 24 },
|
|
|
|
|
|
* desktop: { padding: 32 },
|
|
|
|
|
|
* wide: { padding: 48 }
|
|
|
|
|
|
* });
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function createResponsiveStyles(
|
|
|
|
|
|
breakpoint: string,
|
|
|
|
|
|
styles: ResponsiveStyles
|
|
|
|
|
|
): ViewStyle {
|
|
|
|
|
|
const result: ViewStyle = {};
|
|
|
|
|
|
|
|
|
|
|
|
// 按断点优先级从低到高合并样式
|
|
|
|
|
|
const breakpoints: ResponsiveBreakpoint[] = ['mobile', 'tablet', 'desktop', 'wide'];
|
|
|
|
|
|
|
|
|
|
|
|
for (const bp of breakpoints) {
|
|
|
|
|
|
const style = styles[bp];
|
|
|
|
|
|
if (style) {
|
|
|
|
|
|
Object.assign(result, style);
|
|
|
|
|
|
}
|
|
|
|
|
|
// 到达当前断点后停止合并
|
|
|
|
|
|
if (breakpoint === bp) {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取响应式数值
|
|
|
|
|
|
* 根据屏幕宽度返回对应的数值
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param width - 当前屏幕宽度
|
|
|
|
|
|
* @param config - 各断点对应的数值
|
|
|
|
|
|
* @returns 对应的数值
|
|
|
|
|
|
*
|
|
|
|
|
|
* @example
|
|
|
|
|
|
* const padding = getResponsiveValue(screenWidth, {
|
|
|
|
|
|
* mobile: 12,
|
|
|
|
|
|
* tablet: 16,
|
|
|
|
|
|
* desktop: 24,
|
|
|
|
|
|
* wide: 32
|
|
|
|
|
|
* });
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function getResponsiveValue(
|
|
|
|
|
|
width: number,
|
|
|
|
|
|
config: ResponsiveValues<number>
|
|
|
|
|
|
): number | undefined {
|
|
|
|
|
|
if (width >= BREAKPOINTS.wide && config.wide !== undefined) {
|
|
|
|
|
|
return config.wide;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (width >= BREAKPOINTS.desktop && config.desktop !== undefined) {
|
|
|
|
|
|
return config.desktop;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (width >= BREAKPOINTS.tablet && config.tablet !== undefined) {
|
|
|
|
|
|
return config.tablet;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (config.mobile !== undefined) {
|
|
|
|
|
|
return config.mobile;
|
|
|
|
|
|
}
|
|
|
|
|
|
return undefined;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 判断当前是否为宽屏
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param width - 当前屏幕宽度
|
|
|
|
|
|
* @returns 是否为宽屏(tablet 及以上)
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function isWideScreen(width: number): boolean {
|
|
|
|
|
|
return width >= BREAKPOINTS.tablet;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 判断当前是否为桌面端
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param width - 当前屏幕宽度
|
|
|
|
|
|
* @returns 是否为桌面端(desktop 及以上)
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function isDesktop(width: number): boolean {
|
|
|
|
|
|
return width >= BREAKPOINTS.desktop;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 判断当前是否为超大屏
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param width - 当前屏幕宽度
|
|
|
|
|
|
* @returns 是否为超大屏(wide)
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function isWide(width: number): boolean {
|
|
|
|
|
|
return width >= BREAKPOINTS.wide;
|
|
|
|
|
|
}
|