33 * Canvas + React 混合渲染的币安风格市场深度图
44 */
55
6- import { memo , useRef , useEffect , useCallback , useState } from 'react' ;
6+ import { memo , useRef , useEffect , useCallback , useState , useMemo } from 'react' ;
77import { ZoomIn , ZoomOut } from 'lucide-react' ;
88import { drawDepthChart , fmtVol } from './depthCanvas' ;
99import { useDepthData } from './hooks/useDepthData' ;
@@ -18,6 +18,41 @@ interface DepthChartProps {
1818 price ?: number ;
1919}
2020
21+ /* ============================================
22+ 工具函数
23+ ============================================ */
24+
25+ /**
26+ * 根据当前价格自动计算合适的深度图聚合精度
27+ * 目标:让深度图可视范围内有约 30~60 个价格档位
28+ */
29+ function computeAutoPrecision ( price : number | undefined ) : number {
30+ if ( ! price || price <= 0 ) return 0.1 ;
31+ // 取价格量级,然后选择约为价格 0.01% ~ 0.02% 的精度
32+ const magnitude = Math . pow ( 10 , Math . floor ( Math . log10 ( price ) ) ) ;
33+ // 产生的最小步长约为价格的 0.01%
34+ const rawStep = magnitude * 0.0001 ;
35+ // 对齐到整数量级: 0.01, 0.05, 0.1, 0.5, 1, 5, 10, 50, 100...
36+ const niceSteps = [ 0.01 , 0.02 , 0.05 , 0.1 , 0.2 , 0.5 , 1 , 2 , 5 , 10 , 20 , 50 , 100 , 200 , 500 , 1000 ] ;
37+ return niceSteps . find ( s => s >= rawStep ) ?? rawStep ;
38+ }
39+
40+ /**
41+ * 根据当前价格生成合理的精度选项列表
42+ */
43+ function computePrecisionOptions ( price : number | undefined ) : number [ ] {
44+ if ( ! price || price <= 0 ) return [ 0.01 , 0.05 , 0.1 , 0.5 , 1 , 5 , 10 , 50 , 100 ] ;
45+ const auto = computeAutoPrecision ( price ) ;
46+ // 生成 auto 为基准的 5 个档位: auto/10, auto/5, auto, auto*5, auto*10
47+ const candidates = [ auto / 10 , auto / 5 , auto / 2 , auto , auto * 2 , auto * 5 , auto * 10 , auto * 50 ] ;
48+ // 过滤掉小于 0.01 和重复值,保留合理范围
49+ return [ ...new Set ( candidates . filter ( v => v >= 0.01 ) . map ( v => {
50+ // 美化数字:避免浮点精度问题
51+ if ( v >= 1 ) return Math . round ( v ) ;
52+ return parseFloat ( v . toPrecision ( 2 ) ) ;
53+ } ) ) ] . sort ( ( a , b ) => a - b ) ;
54+ }
55+
2156/* ============================================
2257 组件
2358 ============================================ */
@@ -27,7 +62,14 @@ function DepthChart({ bids, asks, price }: DepthChartProps) {
2762 const containerRef = useRef < HTMLDivElement > ( null ) ;
2863 const [ size , setSize ] = useState ( { w : 0 , h : 0 } ) ;
2964 const [ mouse , setMouse ] = useState < { x : number ; y : number } | null > ( null ) ;
30- const [ precision , setPrecision ] = useState ( 0.1 ) ;
65+
66+ // 自动精度:基于价格动态计算
67+ const autoPrecision = useMemo ( ( ) => computeAutoPrecision ( price ) , [ price ] ) ;
68+ const [ userPrecision , setUserPrecision ] = useState < number | null > ( null ) ;
69+ const precision = userPrecision ?? autoPrecision ;
70+
71+ // 动态精度选项
72+ const precisionOptions = useMemo ( ( ) => computePrecisionOptions ( price ) , [ price ] ) ;
3173
3274 // 数据处理 Hook
3375 const {
@@ -169,15 +211,15 @@ function DepthChart({ bids, asks, price }: DepthChartProps) {
169211 </ button >
170212 </ div >
171213
172- { /* 精度选择 (新增) */ }
214+ { /* 精度选择 */ }
173215 < div className = "absolute top-2 right-3 z-10" >
174216 < select
175217 value = { precision }
176- onChange = { ( e ) => setPrecision ( Number ( e . target . value ) ) }
218+ onChange = { ( e ) => setUserPrecision ( Number ( e . target . value ) ) }
177219 className = "bg-bg-surface/80 backdrop-blur-sm border border-border-dark text-[10px] text-gray-400 rounded px-1.5 py-0.5 outline-none hover:text-white hover:bg-bg-surface-alt transition-colors cursor-pointer appearance-none text-right min-w-[60px]"
178220 title = "调整聚合精度"
179221 >
180- { [ 0.01 , 0.05 , 0.1 , 0.5 , 1 , 5 , 10 , 50 , 100 ] . map ( ( p ) => (
222+ { precisionOptions . map ( ( p ) => (
181223 < option key = { p } value = { p } >
182224 Step: { p }
183225 </ option >
@@ -189,3 +231,4 @@ function DepthChart({ bids, asks, price }: DepthChartProps) {
189231}
190232
191233export default memo ( DepthChart ) ;
234+
0 commit comments