mosaicmap/components/colorbar.tsx
2025-07-28 07:25:32 +08:00

133 lines
3.8 KiB
TypeScript

'use client'
import React, { useRef, useEffect, useState } from 'react'
import { createColorMap, ColorMapType } from '@/lib/color-maps'
interface ColorbarProps {
colorMapType: ColorMapType
width?: number
height?: number
showLabels?: boolean
minValue?: number
maxValue?: number
unit?: string
}
export function Colorbar({
colorMapType,
width = 200,
height = 20,
showLabels = true,
minValue = 0,
maxValue = 100,
unit = ''
}: ColorbarProps) {
const canvasRef = useRef<HTMLCanvasElement>(null)
const [isHovered, setIsHovered] = useState(false)
const [hoverValue, setHoverValue] = useState<number | null>(null)
useEffect(() => {
const canvas = canvasRef.current
if (!canvas) return
const ctx = canvas.getContext('2d')
if (!ctx) return
// 设置canvas尺寸
canvas.width = width
canvas.height = height
// 获取色标数据
const colorMap = createColorMap(colorMapType)
// 绘制色标
const imageData = ctx.createImageData(width, height)
for (let x = 0; x < width; x++) {
const t = x / (width - 1) // 归一化到 0-1
const colorIndex = Math.floor(t * 255) * 4
const r = colorMap[colorIndex]
const g = colorMap[colorIndex + 1]
const b = colorMap[colorIndex + 2]
const a = colorMap[colorIndex + 3]
for (let y = 0; y < height; y++) {
const pixelIndex = (y * width + x) * 4
imageData.data[pixelIndex] = r
imageData.data[pixelIndex + 1] = g
imageData.data[pixelIndex + 2] = b
imageData.data[pixelIndex + 3] = a
}
}
ctx.putImageData(imageData, 0, 0)
// 添加边框
ctx.strokeStyle = '#666'
ctx.lineWidth = 1
ctx.strokeRect(0, 0, width, height)
}, [colorMapType, width, height])
const handleMouseMove = (e: React.MouseEvent<HTMLCanvasElement>) => {
const canvas = canvasRef.current
if (!canvas) return
const rect = canvas.getBoundingClientRect()
const x = e.clientX - rect.left
const t = x / width
const value = minValue + t * (maxValue - minValue)
setHoverValue(value)
setIsHovered(true)
}
const handleMouseLeave = () => {
setIsHovered(false)
setHoverValue(null)
}
const formatValue = (value: number) => {
if (unit === '%') {
return `${value.toFixed(1)}%`
} else if (unit === 'dBZ') {
return `${value.toFixed(1)} dBZ`
} else if (unit === 'mm/h') {
return `${value.toFixed(1)} mm/h`
} else {
return value.toFixed(1)
}
}
return (
<div className="relative">
<canvas
ref={canvasRef}
className="cursor-crosshair"
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
style={{ display: 'block' }}
/>
{showLabels && (
<div className="flex justify-between text-xs text-gray-600 mt-1">
<span>{formatValue(minValue)}</span>
<span>{formatValue(maxValue)}</span>
</div>
)}
{isHovered && hoverValue !== null && (
<div
className="absolute bg-black text-white text-xs px-2 py-1 rounded pointer-events-none z-10"
style={{
left: `${(hoverValue - minValue) / (maxValue - minValue) * width}px`,
top: '-30px',
transform: 'translateX(-50%)'
}}
>
{formatValue(hoverValue)}
</div>
)}
</div>
)
}