SEC_Warehouse/src/components/vital-wave/index.tsx

111 lines
3.2 KiB
TypeScript

import { useEffect, useState } from 'react'
import { Canvas, View, Text } from '@tarojs/components'
import Taro from '@tarojs/taro'
const V_PADDING = 8
const HEIGHT = 110
interface Props {
points: number[]
canvasId?: string
}
export function VitalWave({ points, canvasId = 'vitalWave' }: Props) {
const [width, setWidth] = useState(200)
const empty = !points || points.length < 2
// 测量容器宽度
useEffect(() => {
const query = Taro.createSelectorQuery()
query.select(`#${canvasId}-wrap`).boundingClientRect((rect: any) => {
if (rect && rect.width > 0) {
setWidth(Math.floor(rect.width))
}
}).exec()
}, [canvasId])
// 数据变化重绘
useEffect(() => {
if (empty || width <= 0) return
drawWave(canvasId, width, HEIGHT, points)
}, [points, width, empty, canvasId])
return (
<View id={`${canvasId}-wrap`} style={{ position: 'relative', width: '100%', height: `${HEIGHT * 2}rpx` }}>
<Canvas
canvasId={canvasId}
style={{ width: `${width}px`, height: `${HEIGHT}px` }}
/>
{!empty && (
<>
<View style={{
position: 'absolute', top: '33.3%', left: 0, right: 0,
borderTop: '1px dashed rgba(245, 34, 45, 0.25)'
}} />
<View style={{
position: 'absolute', top: '66.6%', left: 0, right: 0,
borderTop: '1px dashed rgba(245, 34, 45, 0.25)'
}} />
</>
)}
{empty && (
<View style={{
position: 'absolute', top: 0, left: 0, right: 0, bottom: 0,
display: 'flex', alignItems: 'center', justifyContent: 'center'
}}>
<Text style={{ fontSize: '24rpx', color: '#b5b9c0' }}>暂无数据</Text>
</View>
)}
</View>
)
}
function drawWave(canvasId: string, w: number, h: number, pts: number[]) {
const ctx = Taro.createCanvasContext(canvasId)
const min = Math.min(...pts)
const max = Math.max(...pts)
const span = max - min || 1
const xAt = (i: number) => (pts.length <= 1 ? 0 : (i / (pts.length - 1)) * w)
const yAt = (v: number) => h - V_PADDING - ((v - min) / span) * (h - V_PADDING * 2)
const pathTo = (closeBottom: boolean) => {
ctx.beginPath()
ctx.moveTo(xAt(0), yAt(pts[0]))
for (let i = 0; i < pts.length - 1; i++) {
const x0 = xAt(Math.max(0, i - 1))
const y0 = yAt(pts[Math.max(0, i - 1)])
const x1 = xAt(i)
const y1 = yAt(pts[i])
const x2 = xAt(i + 1)
const y2 = yAt(pts[i + 1])
const x3 = xAt(Math.min(pts.length - 1, i + 2))
const y3 = yAt(pts[Math.min(pts.length - 1, i + 2)])
const c1x = x1 + (x2 - x0) / 6
const c1y = y1 + (y2 - y0) / 6
const c2x = x2 - (x3 - x1) / 6
const c2y = y2 - (y3 - y1) / 6
ctx.bezierCurveTo(c1x, c1y, c2x, c2y, x2, y2)
}
if (closeBottom) {
ctx.lineTo(w, h)
ctx.lineTo(0, h)
ctx.closePath()
}
}
pathTo(true)
const grad = ctx.createLinearGradient(0, 0, 0, h)
grad.addColorStop(0, 'rgba(245, 34, 45, 0.25)')
grad.addColorStop(1, 'rgba(245, 34, 45, 0)')
ctx.setFillStyle(grad)
ctx.fill()
pathTo(false)
ctx.setStrokeStyle('#f5222d')
ctx.setLineWidth(2)
ctx.stroke()
ctx.draw()
}