154 lines
4.0 KiB
TypeScript
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
|