feat(freqchart): 优化Y轴刻度范围选择交互体验

- 添加Y轴刻度范围的下拉菜单按钮,代替原先的按钮组展示
- 实现菜单外点击关闭下拉功能,提升操作便捷性
- 调整刻度范围按钮样式,使其更美观且易于点击
- 修正dB刻度网格标签显示,避免顶部标签与范围按钮重叠
- 优化相关样式和布局,提升整体界面一致性与视觉效果
This commit is contained in:
eafonyang
2026-11-05 10:07:15 +08:00
parent fde04879c6
commit 3442807918
+56 -23
View File
@@ -1,4 +1,5 @@
import { useState, useEffect, useRef, useMemo } from "react"; import { useState, useEffect, useRef, useMemo } from "react";
import { ChevronDown, ZoomIn } from "lucide-react";
import { toast } from "sonner"; import { toast } from "sonner";
import { useStrokeDrawAnimation } from "@/lib/useStrokeDrawAnimation"; import { useStrokeDrawAnimation } from "@/lib/useStrokeDrawAnimation";
import { import {
@@ -113,6 +114,22 @@ export function FreqChart({
} }
}; };
const [isYRangeMenuOpen, setIsYRangeMenuOpen] = useState(false);
const yRangeMenuRef = useRef<HTMLDivElement | null>(null);
// 点击菜单外部收起下拉
useEffect(() => {
if (!isYRangeMenuOpen) return;
const onPointerDown = (event: PointerEvent) => {
const target = event.target as Node;
if (!(yRangeMenuRef.current?.contains(target) ?? false)) {
setIsYRangeMenuOpen(false);
}
};
document.addEventListener("pointerdown", onPointerDown);
return () => document.removeEventListener("pointerdown", onPointerDown);
}, [isYRangeMenuOpen]);
useEffect(() => { useEffect(() => {
const node = chartContainerRef.current; const node = chartContainerRef.current;
if (!node) return; if (!node) return;
@@ -317,27 +334,6 @@ export function FreqChart({
Equalized Equalized
</span> </span>
</button> </button>
{/* Y 轴刻度范围切换 */}
<div
className="ml-auto flex shrink-0 items-center overflow-hidden rounded-[8px]"
style={{ background: "rgba(44,44,46,0.7)", border: "1px solid rgba(255,255,255,0.08)" }}
>
{Y_RANGE_OPTIONS.map((r) => (
<button
key={r}
type="button"
className="px-2 py-1 text-[10px] transition-all duration-150"
style={
yDbMax === r
? { background: "#00FFF6", color: "#000", fontWeight: 600 }
: { color: "rgba(255,255,255,0.4)" }
}
onClick={() => handleYRangeChange(r)}
>
±{r}
</button>
))}
</div>
</div> </div>
{/* A/B + DIFF controls */} {/* A/B + DIFF controls */}
@@ -399,9 +395,43 @@ export function FreqChart({
{/* SVG chart */} {/* SVG chart */}
<div <div
ref={chartContainerRef} ref={chartContainerRef}
className="rounded-[10px] overflow-hidden" className="relative rounded-[10px] overflow-hidden"
style={{ background: "rgba(10,12,10,0.7)" }} style={{ background: "rgba(10,12,10,0.7)" }}
> >
{/* Y 轴刻度范围:Y 轴顶端单按钮,点击展开下拉选项 */}
<div ref={yRangeMenuRef} className="absolute left-1 top-1 z-20">
<button
type="button"
className="flex items-center gap-1 rounded-[8px] px-2 py-1 text-[12px] font-semibold text-[#00FFF6] active:opacity-80 transition-opacity"
style={{ background: "rgba(44,44,46,0.85)", border: "1px solid rgba(255,255,255,0.12)" }}
onClick={() => setIsYRangeMenuOpen(v => !v)}
>
<ZoomIn size={12} />
{yDbMax}
<ChevronDown size={12} className={`transition-transform ${isYRangeMenuOpen ? "rotate-180" : ""}`} />
</button>
{isYRangeMenuOpen && (
<div
className="absolute left-0 top-full mt-1 w-[64px] overflow-hidden rounded-[8px]"
style={{ background: "rgba(10,12,16,0.98)", border: "1px solid rgba(0,255,246,0.35)", boxShadow: "0 8px 20px rgba(0,0,0,0.45)" }}
>
{Y_RANGE_OPTIONS.map((r) => (
<button
key={r}
type="button"
className={`w-full px-2 py-1.5 text-left text-[11px] transition-colors ${yDbMax === r ? "text-black font-semibold" : "text-white/90 hover:bg-white/10"}`}
style={yDbMax === r ? { background: "#00FFF6" } : undefined}
onClick={() => {
handleYRangeChange(r);
setIsYRangeMenuOpen(false);
}}
>
{r}
</button>
))}
</div>
)}
</div>
<svg <svg
ref={svgRef} ref={svgRef}
viewBox={`0 0 ${W} ${H}`} viewBox={`0 0 ${W} ${H}`}
@@ -419,13 +449,16 @@ export function FreqChart({
</clipPath> </clipPath>
</defs> </defs>
{/* dB grid */} {/* dB grid */}
{gainLabels.map((g) => ( {gainLabels.map((g, gi) => (
<g key={`gain-${g}`}> <g key={`gain-${g}`}>
<line x1="0" y1={gainToY(g)} x2={W} y2={gainToY(g)} <line x1="0" y1={gainToY(g)} x2={W} y2={gainToY(g)}
stroke="rgba(255,255,255,0.05)" strokeWidth="1" /> stroke="rgba(255,255,255,0.05)" strokeWidth="1" />
{/* 顶格标签位置被 Y 轴范围按钮占用,避免文字重叠 */}
{gi > 0 && (
<text x="3" y={gainToY(g) - 2} fontSize="7" fill="rgba(255,255,255,0.2)"> <text x="3" y={gainToY(g) - 2} fontSize="7" fill="rgba(255,255,255,0.2)">
{formatGainLabel(g)} {formatGainLabel(g)}
</text> </text>
)}
</g> </g>
))} ))}
{/* Freq grid */} {/* Freq grid */}