clxx 2.1.4 → 2.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +766 -422
  2. package/build/Alert/style.js +41 -58
  3. package/build/AutoGrid/index.js +32 -43
  4. package/build/AutoGrid/style.d.ts +3 -4
  5. package/build/AutoGrid/style.js +7 -19
  6. package/build/CarouselNotice/style.js +6 -11
  7. package/build/Clickable/index.d.ts +1 -0
  8. package/build/Clickable/index.js +77 -41
  9. package/build/Container/index.js +46 -80
  10. package/build/Dialog/style.js +2 -2
  11. package/build/Fixed/index.d.ts +5 -0
  12. package/build/Fixed/index.js +37 -0
  13. package/build/Indicator/index.js +5 -5
  14. package/build/Loading/style.js +16 -17
  15. package/build/Overlay/index.js +4 -32
  16. package/build/ScrollView/index.js +65 -47
  17. package/build/ScrollView/style.js +12 -13
  18. package/build/Toast/index.js +5 -3
  19. package/build/Toast/style.js +11 -14
  20. package/build/context.d.ts +2 -3
  21. package/build/context.js +1 -4
  22. package/build/index.d.ts +1 -1
  23. package/build/index.js +1 -1
  24. package/build/utils/Countdown.js +11 -4
  25. package/build/utils/ago.js +39 -52
  26. package/build/utils/createApp.d.ts +1 -2
  27. package/build/utils/createApp.js +23 -12
  28. package/build/utils/cssUtil.d.ts +0 -9
  29. package/build/utils/cssUtil.js +0 -38
  30. package/build/utils/defaultScroll.d.ts +4 -4
  31. package/build/utils/defaultScroll.js +6 -15
  32. package/build/utils/dom.js +5 -8
  33. package/build/utils/jsonp.js +7 -4
  34. package/build/utils/request.js +23 -33
  35. package/build/utils/tick.js +7 -14
  36. package/build/utils/uniqKey.js +7 -7
  37. package/build/utils/wait.js +1 -1
  38. package/package.json +10 -10
  39. package/test/eslint.config.js +5 -2
  40. package/test/package.json +11 -11
  41. package/test/src/ago/index.jsx +3 -1
  42. package/test/src/dialog/index.module.css +1 -1
  43. package/test/src/index.jsx +3 -2
@@ -1,17 +1,12 @@
1
- import { adaptive } from "../utils/cssUtil";
2
1
  export const style = {
3
- container: [
4
- {
5
- position: "relative",
6
- overflow: "hidden",
7
- backgroundColor: "#fff",
8
- boxShadow: "0 0 2px 0 #00000055",
9
- },
10
- adaptive({
11
- borderRadius: 16,
12
- width: 750 * 0.84,
13
- }),
14
- ],
2
+ container: {
3
+ position: "relative",
4
+ overflow: "hidden",
5
+ backgroundColor: "#fff",
6
+ boxShadow: "0 0 2px 0 #00000055",
7
+ borderRadius: '.16rem',
8
+ width: 750 * 0.84 / 100 + 'rem',
9
+ },
15
10
  content: {
16
11
  position: "relative",
17
12
  "&:after,&::after": {
@@ -25,38 +20,30 @@ export const style = {
25
20
  transform: `scale(1, ${1 / window.devicePixelRatio})`,
26
21
  },
27
22
  },
28
- title: [
29
- {
30
- textAlign: "center",
31
- lineHeight: 1.4,
32
- color: "#000",
33
- },
34
- adaptive({
35
- paddingTop: 50,
36
- paddingLeft: 40,
37
- paddingRight: 40,
38
- paddingBottom: 50,
39
- fontSize: 33,
40
- }),
41
- ],
42
- desc: [
43
- {
44
- textAlign: "center",
45
- lineHeight: 1.4,
46
- color: "#666",
47
- },
48
- adaptive({
49
- paddingTop: 20,
50
- paddingLeft: 40,
51
- paddingRight: 40,
52
- paddingBottom: 50,
53
- fontSize: 29,
54
- }),
55
- ],
56
- btnBox: adaptive({
23
+ title: {
24
+ textAlign: "center",
25
+ lineHeight: 1.4,
26
+ color: "#000",
27
+ paddingTop: '.5rem',
28
+ paddingLeft: '.4rem',
29
+ paddingRight: '.4rem',
30
+ paddingBottom: '.5rem',
31
+ fontSize: '.33rem',
32
+ },
33
+ desc: {
34
+ textAlign: "center",
35
+ lineHeight: 1.4,
36
+ color: "#666",
37
+ paddingTop: '.2rem',
38
+ paddingLeft: '.4rem',
39
+ paddingRight: '.4rem',
40
+ paddingBottom: '.5rem',
41
+ fontSize: '.29rem',
42
+ },
43
+ btnBox: {
57
44
  position: "relative",
58
- height: 90,
59
- }),
45
+ height: '.9rem',
46
+ },
60
47
  btnBoxWithCancel: {
61
48
  "&:after,&::after": {
62
49
  content: "''",
@@ -67,20 +54,16 @@ export const style = {
67
54
  marginLeft: "-.5px",
68
55
  width: "1px",
69
56
  backgroundColor: "#c0c0c0",
70
- transform: `scale(${1 / devicePixelRatio}, 1)`,
57
+ transform: `scale(${1 / window.devicePixelRatio}, 1)`,
71
58
  },
72
59
  },
73
- btn: [
74
- {
75
- flex: 1,
76
- display: "flex",
77
- alignItems: "center",
78
- justifyContent: "center",
79
- userSelect: "none",
80
- letterSpacing: "1px",
81
- },
82
- adaptive({
83
- fontSize: 33,
84
- }),
85
- ],
60
+ btn: {
61
+ flex: 1,
62
+ display: "flex",
63
+ alignItems: "center",
64
+ justifyContent: "center",
65
+ userSelect: "none",
66
+ letterSpacing: "1px",
67
+ fontSize: '.33rem',
68
+ },
86
69
  };
@@ -10,7 +10,7 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  return t;
11
11
  };
12
12
  import { jsx as _jsx } from "@emotion/react/jsx-runtime";
13
- import React, { useCallback } from 'react';
13
+ import React, { useMemo } from 'react';
14
14
  import { style } from './style';
15
15
  import { normalizeUnit } from '../utils/cssUtil';
16
16
  /**
@@ -19,55 +19,44 @@ import { normalizeUnit } from '../utils/cssUtil';
19
19
  */
20
20
  export function AutoGrid(props) {
21
21
  const { children, cols: rawCols = 1, gap: rawGap = 0, isSquare = false, itemStyle, containerStyle } = props, extra = __rest(props, ["children", "cols", "gap", "isSquare", "itemStyle", "containerStyle"]);
22
- // 规范化数字单位
23
- const cols = +rawCols;
22
+ // 规范化,确保 cols >= 1
23
+ const cols = Math.max(1, Math.floor(+rawCols) || 1);
24
24
  const gap = normalizeUnit(rawGap);
25
- // 获取表格数据
26
- const getGridData = useCallback(() => {
27
- // 生成一个能创建表格的二维数组
28
- let list = [];
25
+ // 将 children 分组为二维数组
26
+ const gridData = useMemo(() => {
27
+ const list = [];
29
28
  React.Children.forEach(children, (child) => {
30
- if (child !== null) {
29
+ if (child !== null && child !== undefined) {
31
30
  if (list.length === 0 || list[list.length - 1].length >= cols) {
32
31
  list.push([]);
33
32
  }
34
33
  list[list.length - 1].push(child);
35
34
  }
36
35
  });
36
+ // 用空占位符补齐最后一行,避免最后一行元素宽度不一致
37
+ if (list.length > 0) {
38
+ const lastRow = list[list.length - 1];
39
+ while (lastRow.length < cols) {
40
+ lastRow.push(null);
41
+ }
42
+ }
37
43
  return list;
38
- }, [children]);
39
- // 元素的最终样式
40
- const finalItemBoxStyle = [
41
- style.itemBoxStyle,
42
- {
43
- marginRight: gap,
44
- width: `calc((100% - ${cols - 1} * ${gap}) / ${cols})`,
45
- },
46
- ];
47
- /**
48
- * 显示内容
49
- */
50
- const showContent = () => {
51
- const gridData = getGridData();
52
- return gridData.map((row, rowIndex) => {
53
- // 每行的槽样式,最后一行没有
54
- let finalRowStyle = [
55
- style.rowStyle,
56
- rowIndex !== gridData.length - 1 ? { marginBottom: gap } : {}
57
- ];
58
- return (_jsx("div", { css: finalRowStyle, children: row.map((item, colIndex) => {
59
- let finalCss = [
60
- ...finalItemBoxStyle,
61
- itemStyle
62
- ];
63
- if (isSquare) {
64
- return (_jsx("div", { css: [...finalCss, style.itemBoxSquare], children: _jsx("div", { css: style.itemInnerStyle, children: item }) }, colIndex));
65
- }
66
- else {
67
- return (_jsx("div", { css: finalCss, children: item }, colIndex));
68
- }
69
- }) }, rowIndex));
70
- });
71
- };
72
- return (_jsx("div", Object.assign({}, extra, { css: [containerStyle], children: showContent() })));
44
+ }, [children, cols]);
45
+ // 缓存 item 宽度计算
46
+ const itemWidth = useMemo(() => `calc((100% - ${cols - 1} * ${gap}) / ${cols})`, [cols, gap]);
47
+ return (_jsx("div", Object.assign({}, extra, { css: containerStyle, children: gridData.map((row, rowIndex) => (_jsx("div", { css: [
48
+ style.row,
49
+ { gap },
50
+ rowIndex !== gridData.length - 1 ? { marginBottom: gap } : undefined,
51
+ ], children: row.map((item, colIndex) => {
52
+ const isPlaceholder = item === null;
53
+ const boxCss = [
54
+ style.itemBox,
55
+ { width: itemWidth },
56
+ isPlaceholder ? { visibility: 'hidden' } : undefined,
57
+ isSquare ? style.itemBoxSquare : undefined,
58
+ itemStyle,
59
+ ];
60
+ return isSquare ? (_jsx("div", { css: boxCss, children: _jsx("div", { css: style.itemInner, children: item }) }, colIndex)) : (_jsx("div", { css: boxCss, children: item }, colIndex));
61
+ }) }, rowIndex))) })));
73
62
  }
@@ -1,7 +1,6 @@
1
1
  export declare const style: {
2
- itemBoxStyle: import("@emotion/react").SerializedStyles;
2
+ row: import("@emotion/react").SerializedStyles;
3
+ itemBox: import("@emotion/react").SerializedStyles;
3
4
  itemBoxSquare: import("@emotion/react").SerializedStyles;
4
- itemNull: import("@emotion/react").SerializedStyles;
5
- itemInnerStyle: import("@emotion/react").SerializedStyles;
6
- rowStyle: import("@emotion/react").SerializedStyles;
5
+ itemInner: import("@emotion/react").SerializedStyles;
7
6
  };
@@ -1,11 +1,12 @@
1
1
  import { css } from "@emotion/react";
2
2
  export const style = {
3
- itemBoxStyle: css({
4
- float: "left",
3
+ row: css({
4
+ display: "flex",
5
+ width: "100%",
6
+ }),
7
+ itemBox: css({
5
8
  position: "relative",
6
- "&:last-child": {
7
- marginRight: 0,
8
- },
9
+ minWidth: 0,
9
10
  }),
10
11
  itemBoxSquare: css({
11
12
  "&:after,&::after": {
@@ -16,24 +17,11 @@ export const style = {
16
17
  paddingBottom: "100%",
17
18
  },
18
19
  }),
19
- itemNull: css({
20
- visibility: "hidden",
21
- }),
22
- itemInnerStyle: css({
20
+ itemInner: css({
23
21
  position: "absolute",
24
22
  left: 0,
25
23
  right: 0,
26
24
  bottom: 0,
27
25
  top: 0,
28
26
  }),
29
- // 行样式
30
- rowStyle: css({
31
- width: "100%",
32
- "&:after,&::after": {
33
- content: "''",
34
- display: "table",
35
- height: 0,
36
- clear: "both"
37
- }
38
- })
39
27
  };
@@ -1,5 +1,4 @@
1
1
  import { keyframes } from "@emotion/react";
2
- import { adaptive } from "../utils/cssUtil";
3
2
  export const Bubble = keyframes `
4
3
  from {
5
4
  transform: translateY(0);
@@ -9,16 +8,12 @@ export const Bubble = keyframes `
9
8
  }
10
9
  `;
11
10
  export const style = {
12
- box: [
13
- {
14
- position: "relative",
15
- overflow: "hidden",
16
- transition: "all 200ms",
17
- },
18
- adaptive({
19
- height: 80,
20
- }),
21
- ],
11
+ box: {
12
+ position: "relative",
13
+ overflow: "hidden",
14
+ transition: "all 200ms",
15
+ height: '.8rem',
16
+ },
22
17
  wrapper: {
23
18
  position: "absolute",
24
19
  left: 0,
@@ -8,5 +8,6 @@ export interface ClickableProps extends React.HTMLProps<HTMLDivElement> {
8
8
  activeClassName?: string;
9
9
  activeStyle?: React.CSSProperties;
10
10
  disable?: boolean;
11
+ moveThreshold?: number;
11
12
  }
12
13
  export declare function Clickable(props: Partial<ClickableProps>): import("@emotion/react/jsx-runtime").JSX.Element;
@@ -13,74 +13,110 @@ import { jsx as _jsx } from "@emotion/react/jsx-runtime";
13
13
  import React, { useCallback, useRef, useState, useEffect, } from 'react';
14
14
  import { is } from '../utils/is';
15
15
  export function Clickable(props) {
16
- let { children, bubble = true, className, activeClassName, style, activeStyle, disable = false } = props, attrs = __rest(props, ["children", "bubble", "className", "activeClassName", "style", "activeStyle", "disable"]);
16
+ let { children, bubble = true, className, activeClassName, style, activeStyle, disable = false, moveThreshold = 10 } = props, attrs = __rest(props, ["children", "bubble", "className", "activeClassName", "style", "activeStyle", "disable", "moveThreshold"]);
17
17
  // 如果激活样式和激活类都不存在,则设置激活默认样式
18
- // 使用 useMemo 避免每次渲染都创建新对象
19
- const defaultActiveStyle = React.useMemo(() => {
18
+ const finalActiveStyle = React.useMemo(() => {
20
19
  if (!activeClassName && !activeStyle) {
21
20
  return { opacity: 0.6 };
22
21
  }
23
22
  return activeStyle;
24
23
  }, [activeClassName, activeStyle]);
25
- const finalActiveStyle = defaultActiveStyle || activeStyle;
26
24
  const touchable = is('touchable');
27
- const [boxClass, setBoxClass] = useState(className);
28
- const [boxStyle, setBoxStyle] = useState(style);
29
- // 监控属性的更新
30
- useEffect(() => {
31
- setBoxClass(className);
32
- setBoxStyle(style);
33
- }, [className, style]);
34
- // 标记是否正处于触摸状态
25
+ const [isActive, setIsActive] = useState(false);
26
+ // 标记是否正处于触摸/按下状态
35
27
  const touchRef = useRef(false);
36
- const onStart = (event) => {
28
+ // 是否已挂载(防止卸载后更新 state)
29
+ const mountedRef = useRef(true);
30
+ // 记录触摸起始坐标,用于 touchmove 取消
31
+ const startPos = useRef({ x: 0, y: 0 });
32
+ useEffect(() => {
33
+ mountedRef.current = true;
34
+ return () => {
35
+ mountedRef.current = false;
36
+ };
37
+ }, []);
38
+ const activate = useCallback(() => {
39
+ if (mountedRef.current)
40
+ setIsActive(true);
41
+ }, []);
42
+ const deactivate = useCallback(() => {
43
+ if (mountedRef.current)
44
+ setIsActive(false);
45
+ }, []);
46
+ const onTouchStart = useCallback((event) => {
37
47
  if (!touchRef.current) {
38
48
  touchRef.current = true;
39
- // 阻止冒泡
40
- if (!bubble) {
49
+ if (!bubble)
41
50
  event.stopPropagation();
51
+ const touch = event.touches[0];
52
+ startPos.current = { x: touch.clientX, y: touch.clientY };
53
+ activate();
54
+ }
55
+ }, [bubble, activate]);
56
+ const onTouchMove = useCallback((event) => {
57
+ if (touchRef.current) {
58
+ const touch = event.touches[0];
59
+ const dx = touch.clientX - startPos.current.x;
60
+ const dy = touch.clientY - startPos.current.y;
61
+ if (dx * dx + dy * dy > moveThreshold * moveThreshold) {
62
+ touchRef.current = false;
63
+ deactivate();
42
64
  }
43
- // 激活目标样式
44
- if (typeof activeClassName === 'string') {
45
- setBoxClass(typeof boxClass === 'string'
46
- ? `${boxClass} ${activeClassName}`
47
- : activeClassName);
48
- }
49
- if (typeof finalActiveStyle === 'object') {
50
- setBoxStyle(typeof boxStyle === 'object'
51
- ? Object.assign(Object.assign({}, boxStyle), finalActiveStyle) : finalActiveStyle);
52
- }
53
65
  }
54
- };
55
- // onEnd返回记忆的版本,防止下一个effect中无意义重复执行
56
- const onEnd = useCallback(() => {
66
+ }, [moveThreshold, deactivate]);
67
+ const onTouchEnd = useCallback((event) => {
68
+ if (touchRef.current) {
69
+ touchRef.current = false;
70
+ if (!bubble)
71
+ event.stopPropagation();
72
+ deactivate();
73
+ }
74
+ }, [bubble, deactivate]);
75
+ const onMouseDown = useCallback((event) => {
76
+ // 只响应鼠标左键
77
+ if (event.button !== 0)
78
+ return;
79
+ if (!touchRef.current) {
80
+ touchRef.current = true;
81
+ if (!bubble)
82
+ event.stopPropagation();
83
+ activate();
84
+ }
85
+ }, [bubble, activate]);
86
+ const onMouseUp = useCallback(() => {
57
87
  if (touchRef.current) {
58
88
  touchRef.current = false;
59
- setBoxClass(className);
60
- setBoxStyle(style);
89
+ deactivate();
61
90
  }
62
- }, [className, style]);
63
- // PC环境释放逻辑
91
+ }, [deactivate]);
92
+ // PC 环境:在 document 上监听 mouseup,处理鼠标移出元素后释放的场景
64
93
  useEffect(() => {
65
94
  if (!disable && !touchable) {
66
95
  const doc = document.documentElement;
67
- doc.addEventListener('mouseup', onEnd);
96
+ doc.addEventListener('mouseup', onMouseUp);
68
97
  return () => {
69
- doc.removeEventListener('mouseup', onEnd);
98
+ doc.removeEventListener('mouseup', onMouseUp);
70
99
  };
71
100
  }
72
- }, [disable, touchable, onEnd]);
73
- const fullAttrs = Object.assign(Object.assign({}, attrs), { className: boxClass, style: boxStyle });
101
+ }, [disable, touchable, onMouseUp]);
102
+ // 根据激活状态计算最终的 className style
103
+ const finalClassName = isActive && typeof activeClassName === 'string'
104
+ ? (typeof className === 'string' ? `${className} ${activeClassName}` : activeClassName)
105
+ : className;
106
+ const finalStyle = isActive && typeof finalActiveStyle === 'object'
107
+ ? (typeof style === 'object' ? Object.assign(Object.assign({}, style), finalActiveStyle) : finalActiveStyle)
108
+ : style;
109
+ const fullAttrs = Object.assign(Object.assign({}, attrs), { className: finalClassName, style: finalStyle });
74
110
  // 非禁用状态有点击态行为
75
111
  if (!disable) {
76
112
  if (touchable) {
77
- // 当前如果是触摸环境
78
- fullAttrs.onTouchStart = onStart;
79
- fullAttrs.onTouchEnd = onEnd;
80
- fullAttrs.onTouchCancel = onEnd;
113
+ fullAttrs.onTouchStart = onTouchStart;
114
+ fullAttrs.onTouchMove = onTouchMove;
115
+ fullAttrs.onTouchEnd = onTouchEnd;
116
+ fullAttrs.onTouchCancel = onTouchEnd;
81
117
  }
82
118
  else {
83
- fullAttrs.onMouseDown = onStart;
119
+ fullAttrs.onMouseDown = onMouseDown;
84
120
  }
85
121
  }
86
122
  return _jsx("div", Object.assign({}, fullAttrs, { children: children }));
@@ -1,7 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
2
  import { Global } from "@emotion/react";
3
- import React, { useCallback, useEffect, useMemo, useState } from "react";
4
- import { getContextValue } from "../context";
3
+ import React, { useCallback, useEffect, useLayoutEffect, useMemo, useState, } from "react";
5
4
  import { useWindowResize } from "../Effect/useWindowResize";
6
5
  import { useViewport } from "../Effect/useViewport";
7
6
  /**
@@ -9,104 +8,71 @@ import { useViewport } from "../Effect/useViewport";
9
8
  * @param props
10
9
  */
11
10
  export function Container(props) {
12
- // 来自全局的环境变量
13
- const { minDocWidth, maxDocWidth } = getContextValue();
14
- // 获取环境变量
15
11
  const { designWidth = 750, globalStyle, children } = props;
16
- // 计算根字体尺寸的函数(使用 useCallback 避免重复创建)
12
+ // 计算理论根字体大小(未经浏览器缩放修正)
17
13
  const calculateFontSize = useCallback((width) => {
18
- let targetWidth = width;
19
- if (width >= maxDocWidth) {
20
- targetWidth = maxDocWidth;
21
- }
22
- else if (width <= minDocWidth) {
23
- targetWidth = minDocWidth;
24
- }
25
- return (targetWidth * 100) / designWidth;
26
- }, [designWidth, minDocWidth, maxDocWidth]);
27
- // 基准字体尺寸(初始化时计算一次)
28
- const [baseFontSize, setBaseFontSize] = useState(() => calculateFontSize(window.innerWidth));
29
- // 是否已完成初始化(包括字体缩放修正)
14
+ return (width * 100) / designWidth;
15
+ }, [designWidth]);
16
+ // 理论基准字体大小(跟随窗口尺寸变化)
17
+ const [rawFontSize, setRawFontSize] = useState(() => calculateFontSize(window.innerWidth));
18
+ // 浏览器字体缩放因子(>1 表示用户放大了系统字体,<1 表示缩小)
19
+ // 独立存储,使得 resize 后缩放修正依然生效
20
+ const [scaleFactor, setScaleFactor] = useState(1);
21
+ // 是否已完成字体缩放检测
30
22
  const [isInitialized, setIsInitialized] = useState(false);
31
- // 字体缩放修正逻辑(处理浏览器字体设置影响)
32
- // 使用 useLayoutEffect DOM 更新后立即同步执行,避免闪烁
33
- useEffect(() => {
34
- // 只在未初始化时检查一次
23
+ // 修正后的字体大小:统一对所有字体计算应用缩放修正
24
+ const correctedFontSize = useMemo(() => scaleFactor === 1
25
+ ? rawFontSize
26
+ : Math.round((rawFontSize / scaleFactor) * 10) / 10, [rawFontSize, scaleFactor]);
27
+ // 字体缩放检测
28
+ // Emotion 的 <Global> 通过 useInsertionEffect 注入样式,早于 useLayoutEffect
29
+ // 因此 useLayoutEffect 内 getComputedStyle 可正确读取已注入的字体大小
30
+ // 检测和修正均在浏览器绘制前同步完成,避免闪烁
31
+ useLayoutEffect(() => {
32
+ // 缩放因子在页面生命周期内不变,只需检测一次
35
33
  if (isInitialized)
36
34
  return;
37
- // 延迟到下一帧检查,确保 DOM 已经应用了 baseFontSize
38
- requestAnimationFrame(() => {
39
- const computedSize = parseFloat(window.getComputedStyle(document.documentElement).fontSize);
40
- // 如果计算出的字体大小与期望不符(说明被浏览器字体设置影响了)
41
- // 使用较大的容差值,避免浮点数精度问题导致的无限循环
42
- if (typeof computedSize === "number" &&
43
- computedSize > 0 &&
44
- Math.abs(computedSize - baseFontSize) > 1 // 容差 1px,避免过度敏感
45
- ) {
46
- // 计算浏览器的字体缩放比例
47
- const scaleFactor = computedSize / baseFontSize;
48
- // 通过反向缩放修正字体大小
49
- // 例如:期望 50px,实际 60px(1.2倍),则设置 50/1.2 ≈ 41.67px
50
- const correctedSize = Math.round((baseFontSize / scaleFactor) * 10) / 10;
51
- // 只修正一次,然后标记为已初始化
52
- setBaseFontSize(correctedSize);
53
- setIsInitialized(true);
54
- }
55
- else {
56
- // 字体大小正确,直接标记为已初始化
57
- setIsInitialized(true);
58
- }
59
- });
60
- }, [baseFontSize, isInitialized]);
61
- // 页面大小变化时,基准字体同步变化
62
- // 使用 requestAnimationFrame 批量处理,避免频繁更新
35
+ const computedSize = parseFloat(window.getComputedStyle(document.documentElement).fontSize);
36
+ // 如果计算出的字体大小与期望不符(说明被浏览器字体设置影响了)
37
+ // 容差 1px,避免浮点精度导致误判
38
+ if (computedSize > 0 && Math.abs(computedSize - rawFontSize) > 1) {
39
+ // 记录缩放因子,后续所有字体计算(包括 resize)都会自动应用
40
+ setScaleFactor(computedSize / rawFontSize);
41
+ }
42
+ setIsInitialized(true);
43
+ }, [rawFontSize, isInitialized]);
44
+ // 窗口大小变化时更新理论字体大小
45
+ // correctedFontSize 通过 useMemo 自动应用 scaleFactor 修正
63
46
  useWindowResize(() => {
64
- requestAnimationFrame(() => {
65
- const newFontSize = calculateFontSize(window.innerWidth);
66
- if (newFontSize !== baseFontSize) {
67
- setBaseFontSize(newFontSize);
68
- }
69
- });
47
+ setRawFontSize(calculateFontSize(window.innerWidth));
70
48
  });
71
- // 设置meta, 确保viewport的合法逻辑
49
+ // 设置 viewport meta
72
50
  useViewport();
73
- // 页面初始化逻辑
51
+ // 激活 iOS 上的 :active 伪类
74
52
  useEffect(() => {
75
- // 激活iOS上的:active伪类
76
- const activable = () => { };
77
- document.body.addEventListener("touchstart", activable, { passive: true });
53
+ const noop = () => { };
54
+ document.body.addEventListener("touchstart", noop, { passive: true });
78
55
  return () => {
79
- document.body.removeEventListener("touchstart", activable);
56
+ document.body.removeEventListener("touchstart", noop);
80
57
  };
81
58
  }, []);
82
- // 使用 useMemo 缓存媒体查询样式,避免每次渲染都重新计算
83
- const mediaQueryStyles = useMemo(() => ({
84
- [`@media (min-width: ${maxDocWidth}px)`]: {
85
- html: {
86
- fontSize: `${(100 * maxDocWidth) / designWidth}px`,
87
- },
88
- },
89
- [`@media (max-width: ${minDocWidth}px)`]: {
90
- html: {
91
- fontSize: `${(100 * minDocWidth) / designWidth}px`,
92
- },
93
- },
94
- }), [designWidth, minDocWidth, maxDocWidth]);
95
59
  return (_jsxs(React.Fragment, { children: [_jsx(Global, { styles: [
96
- Object.assign({ "*": {
60
+ {
61
+ "*": {
97
62
  boxSizing: "border-box",
98
- }, html: {
63
+ },
64
+ html: {
99
65
  WebkitTapHighlightColor: "transparent",
100
66
  WebkitOverflowScrolling: "touch",
101
67
  WebkitTextSizeAdjust: "100%",
102
- fontSize: `${baseFontSize}px`,
68
+ fontSize: `${correctedFontSize}px`,
103
69
  touchAction: "manipulation",
104
- }, body: {
70
+ },
71
+ body: {
105
72
  fontSize: "16px",
106
73
  margin: "0 auto",
107
- maxWidth: `${maxDocWidth}px`,
108
- minWidth: `${minDocWidth}px`,
109
- } }, mediaQueryStyles),
74
+ },
75
+ },
110
76
  globalStyle,
111
77
  ] }), isInitialized ? children : null] }));
112
78
  }
@@ -117,7 +117,7 @@ export function getAnimation(type, status) {
117
117
  return {
118
118
  keyframes,
119
119
  animation: css({
120
- animation: `${keyframes} 300ms ease`,
120
+ animation: `${keyframes} 300ms ease forwards`,
121
121
  }),
122
122
  };
123
123
  }
@@ -126,7 +126,7 @@ export const style = {
126
126
  animation: `${maskShow} 300ms ease`,
127
127
  }),
128
128
  maskHide: css({
129
- animation: `${maskHide} 300ms ease`,
129
+ animation: `${maskHide} 300ms ease forwards`,
130
130
  }),
131
131
  mask: css({
132
132
  zIndex: 1,
@@ -0,0 +1,5 @@
1
+ export interface FixedProps extends React.HTMLProps<HTMLDivElement> {
2
+ children?: React.ReactNode;
3
+ position?: "top" | "bottom" | "left" | "right";
4
+ }
5
+ export declare function Fixed(props: FixedProps): import("@emotion/react/jsx-runtime").JSX.Element;