Files
labelmain/components/label/components/ScaleComponent.tsx
2026-02-03 18:05:47 +08:00

154 lines
4.0 KiB
TypeScript

"use client"
import React, { useCallback, useEffect, useRef } from "react"
interface ScaleComponentProps {
options: {
offsetX: number
offsetY: number
scale: number
}
mode: "horizontal" | "vertical"
}
const ScaleComponent: React.FC<ScaleComponentProps> = (props) => {
const { options, mode } = props
const canvasRef = useRef<HTMLCanvasElement>(null)
const getFixed = (sparsity: number) => {
const pointIdx = String(sparsity).indexOf(".")
const len = String(sparsity).length
return pointIdx < 0 ? 0 : len - pointIdx - 1
}
const isCloseToInteger = (num: number) => {
return Math.abs(num - Math.round(num)) < 0.0000001
}
const getSparsity = (scale: number) => {
if (scale <= 1) {
return Math.round(100 / scale)
} else if (scale <= 3) {
return 50
} else if (scale <= 4) {
return 20
} else if (scale <= 5) {
return 10
}
return 5
}
const renderWidthWays = useCallback(() => {
const canvas = canvasRef.current
if (!canvas) {
return
}
const { width, height } = canvas.getBoundingClientRect()
const dpi = 2
canvas.width = width * dpi
canvas.height = height * dpi
// canvas.style.width = width + "px";
// canvas.style.height = height + "px";
let { width: w, height: h } = canvas
w /= dpi
h /= dpi
const ctx = canvas.getContext("2d")!
ctx.scale(dpi, dpi)
ctx.clearRect(0, 0, w, h)
ctx.save()
ctx.lineWidth = 1
// ctx.strokeStyle = "#d9d9d9";
ctx.strokeStyle = "#808080"
// ctx.fillStyle = "#232323";
ctx.fillStyle = "#808080"
ctx.font = "12px serif"
ctx.beginPath()
const { offsetX, offsetY, scale } = options
const offset = mode === "horizontal" ? offsetX : offsetY
// 间隔
const sparsity = getSparsity(scale)
// 间隔内有多少条线
const part = 10
const pixelPerUnit = scale * sparsity
const gap = pixelPerUnit / part
const fixed = getFixed(sparsity)
let index = offset % gap > 0 ? gap - (offset % gap) : -offset % gap
if (mode === "horizontal") {
ctx.translate(31.5, 0)
do {
const num = ((offset + index) / pixelPerUnit) * sparsity
if (isCloseToInteger(num / sparsity)) {
ctx.moveTo(index, h * 0.5)
ctx.lineTo(index, h)
const text = num.toFixed(fixed)
const textWidth = ctx.measureText(text).width
ctx.fillText(text, index - textWidth / 2, 10)
} else {
ctx.moveTo(index, h * 0.7)
ctx.lineTo(index, h)
}
index += gap
} while (index < w)
} else {
ctx.translate(0, 0.5)
do {
const num = ((offset + index) / pixelPerUnit) * sparsity
if (isCloseToInteger(num / sparsity)) {
ctx.moveTo(w * 0.5, index)
ctx.lineTo(w, index)
const text = num.toFixed(fixed)
ctx.save()
ctx.rotate((-90 * Math.PI) / 180)
const textWidth =
text === "0"
? ctx.measureText(text).width * 2
: ctx.measureText(text).width
ctx.fillText(text, -(index + textWidth / 2), 12)
ctx.rotate((0 * Math.PI) / 180)
ctx.restore()
} else {
ctx.moveTo(w * 0.7, index)
ctx.lineTo(w, index)
}
index += gap
} while (index < h)
}
ctx.closePath()
ctx.stroke()
ctx.restore()
}, [mode, options])
const getStyles = (): React.CSSProperties => {
const baseStyles: React.CSSProperties = {
flex: "none",
display: "block",
backgroundColor: "transparent",
pointerEvents: "none",
}
if (mode === "horizontal") {
return {
...baseStyles,
width: "100vw",
height: "32px",
cursor: "col-resize",
}
}
return {
...baseStyles,
width: "32px",
height: "100%",
cursor: "row-resize",
}
}
useEffect(() => {
renderWidthWays()
}, [renderWidthWays])
return <canvas ref={canvasRef} style={getStyles()}></canvas>
}
export default ScaleComponent