feat(freqchart): 优化Y轴刻度范围选择交互体验
- 添加Y轴刻度范围的下拉菜单按钮,代替原先的按钮组展示 - 实现菜单外点击关闭下拉功能,提升操作便捷性 - 调整刻度范围按钮样式,使其更美观且易于点击 - 修正dB刻度网格标签显示,避免顶部标签与范围按钮重叠 - 优化相关样式和布局,提升整体界面一致性与视觉效果
This commit is contained in:
@@ -1842,7 +1842,9 @@ export default function EQPage() {
|
|||||||
{freqLabel(b.freq)}
|
{freqLabel(b.freq)}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-[9px] mt-0.5 opacity-70 leading-tight lg:text-[8px] lg:mt-0">
|
<span className="text-[9px] mt-0.5 opacity-70 leading-tight lg:text-[8px] lg:mt-0">
|
||||||
{getFilterShortName(b.type)}
|
{peqBandBypassSupported && b.bypassed
|
||||||
|
? "BYPASS"
|
||||||
|
: getFilterShortName(b.type)}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
@@ -1871,27 +1873,21 @@ export default function EQPage() {
|
|||||||
style={{
|
style={{
|
||||||
background:
|
background:
|
||||||
"linear-gradient(180deg, rgba(36,38,42,0.92) 0%, rgba(24,26,30,0.92) 100%)",
|
"linear-gradient(180deg, rgba(36,38,42,0.92) 0%, rgba(24,26,30,0.92) 100%)",
|
||||||
border: bandBypassed
|
border: "1px solid rgba(0,255,246,0.52)",
|
||||||
? "1px solid rgba(255,255,255,0.18)"
|
boxShadow:
|
||||||
: "1px solid rgba(0,255,246,0.52)",
|
"0 0 14px rgba(0,255,246,0.12), inset 0 1px 0 rgba(255,255,255,0.08)",
|
||||||
boxShadow: bandBypassed
|
|
||||||
? "none"
|
|
||||||
: "0 0 14px rgba(0,255,246,0.12), inset 0 1px 0 rgba(255,255,255,0.08)",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={cn(
|
className="w-full pl-3 pr-8 py-2 text-left text-[13px] font-semibold tracking-wide bg-transparent outline-none cursor-pointer active:opacity-80 transition-opacity rounded-[12px] text-[#E9FBFF]"
|
||||||
"w-full pl-3 pr-8 py-2 text-left text-[13px] font-semibold tracking-wide bg-transparent outline-none cursor-pointer active:opacity-80 transition-opacity rounded-[12px]",
|
|
||||||
bandBypassed ? "text-white/50" : "text-[#E9FBFF]"
|
|
||||||
)}
|
|
||||||
onClick={() => setIsFilterMenuOpen(v => !v)}
|
onClick={() => setIsFilterMenuOpen(v => !v)}
|
||||||
>
|
>
|
||||||
{bandBypassed ? "BYPASS" : normalizeFilterType(band.type)}
|
{bandBypassed ? "BYPASS" : normalizeFilterType(band.type)}
|
||||||
</button>
|
</button>
|
||||||
<ChevronDown
|
<ChevronDown
|
||||||
size={13}
|
size={13}
|
||||||
className={`absolute right-2.5 top-1/2 -translate-y-1/2 pointer-events-none transition-transform ${bandBypassed ? "text-white/40" : "text-[#00FFF6]"} ${isFilterMenuOpen ? "rotate-180" : ""}`}
|
className={`absolute right-2.5 top-1/2 -translate-y-1/2 pointer-events-none transition-transform text-[#00FFF6] ${isFilterMenuOpen ? "rotate-180" : ""}`}
|
||||||
/>
|
/>
|
||||||
{isFilterMenuOpen && (
|
{isFilterMenuOpen && (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -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" />
|
||||||
<text x="3" y={gainToY(g) - 2} fontSize="7" fill="rgba(255,255,255,0.2)">
|
{/* 顶格标签位置被 Y 轴范围按钮占用,避免文字重叠 */}
|
||||||
{formatGainLabel(g)}
|
{gi > 0 && (
|
||||||
</text>
|
<text x="3" y={gainToY(g) - 2} fontSize="7" fill="rgba(255,255,255,0.2)">
|
||||||
|
{formatGainLabel(g)}
|
||||||
|
</text>
|
||||||
|
)}
|
||||||
</g>
|
</g>
|
||||||
))}
|
))}
|
||||||
{/* Freq grid */}
|
{/* Freq grid */}
|
||||||
|
|||||||
Reference in New Issue
Block a user