基于vue3的刻度尺组件

刻度尺组件,可支持横向、纵向,同时支持通过缩放比调节缩放比例。在一些可视化设计组件中使用。

截图

横向刻度尺

在这里插入图片描述

纵向刻度尺

在这里插入图片描述

参数说明

  • layout?: ‘horizontal’ | ‘vertical’,标尺方向,默认为:horizontal;

  • scale: number,缩放比;

  • rulerColor?: string,标尺颜色默认为:#000000;

  • labelColor?: string,标尺刻度数字颜色默认为:#000000

  • mousemove(position: number): void,鼠标移动时触发;

  • click(position: number): void,鼠标点击时触发;

组件源码

<!--
 * @Description: 刻度尺组件
 * @Author: wang keju
 * @Email: git config user.email
 * @Date: 2025-03-04 07:12:35
 * @LastEditTime: 2025-03-04 07:35:06
 * @LastEditors: wang keju
-->
<script lang="ts" setup>
import { onMounted, ref, watch } from 'vue'

type Props = {
    layout?: 'horizontal' | 'vertical';
    scale: number;
    rulerColor?: string;
    labelColor?: string;
}

const props = withDefaults(defineProps<Props>(), {
    layout: 'horizontal',
    rulerColor: '#000000',
    labelColor: '#000000',
})

const emits = defineEmits<{
    (event: 'mousemove', position: number): void
    (event: 'click', position: number): void
}>()

const canvasRef = ref<HTMLCanvasElement>()

onMounted(draw)
watch(props, draw)

/**
 * @description: 根据布局方向,绘制标尺
 * @return {*}
 */
function draw () {
    drawSketchRuler()
}
 
 /**
  * @description: 绘制刻度
  * @return {void}
  */
function drawSketchRuler() {
    const canvas = canvasRef.value
    if (!canvas) return
    const containerRect = canvas.getBoundingClientRect()
    canvas.width = containerRect.width
    canvas.height = containerRect.height

    const ctx = canvas.getContext('2d')
    if (!ctx) return
    // 清空画布
    ctx.clearRect(0, 0, containerRect.width, containerRect.height)

    // 设置线条样式
    ctx.strokeStyle = props.rulerColor
    ctx.lineWidth = 1

    // 绘制标尺刻度数字
    ctx.font = '12px Arial'
    ctx.textAlign = 'center'
    ctx.textBaseline = 'top'
    ctx.fillStyle = props.labelColor
    
    const scale = props.scale
    const rulerConfig = [
        { min: 8, max: 10, minSketch: 0.5, middleSketch: 2.5, maxSketch: 5 },
        { min: 4, max: 8, minSketch: 1, middleSketch: 5, maxSketch: 10 },
        { min: 2, max: 4, minSketch: 2, middleSketch: 10, maxSketch: 20 },
        { min: 1, max: 2, minSketch: 5, middleSketch: 25, maxSketch: 50 },
        { min: 0.5, max: 1, minSketch: 10, middleSketch: 500, maxSketch: 100 },
        { min: 0.3, max: 0.5, minSketch: 20, middleSketch: 100, maxSketch: 200 },
        { min: 0.1, max: 0.3, minSketch: 50, middleSketch: 250, maxSketch: 500 },
        { min: 0.01, max: 0.1, minSketch: 100, middleSketch: 500, maxSketch: 1000 },
    ]
    let info = scale >= 10 ? rulerConfig[0] : rulerConfig.find((item) => item.min <= scale && scale < item.max)
    if (!info) return
    
    const { minSketch, middleSketch, maxSketch } = info
    if (props.layout === 'horizontal') {
        for (let i = 0; i <= containerRect.width / scale; i += 1) {
            const point = i * props.scale
            if (i % maxSketch === 0) {
                ctx.beginPath()
                ctx.moveTo(point, 0)
                ctx.lineTo(point, 14)
                ctx.stroke()
                i !== 0 && ctx.fillText(String(i), point, 16);
            } else if (i % middleSketch === 0) {
                ctx.beginPath()
                ctx.moveTo(point, 0)
                ctx.lineTo(point, 10)
                ctx.stroke()
            } else if (i % minSketch === 0) {
                ctx.beginPath();
                ctx.moveTo(point, 0)
                ctx.lineTo(point, 6)
                ctx.stroke()
            }
        }
    } else {
        for (let i = 0; i <= containerRect.height / scale; i += minSketch) {
            const point = i * props.scale
            if (i % maxSketch === 0) {
                ctx.beginPath()
                ctx.moveTo(0, point)
                ctx.lineTo(14, point)
                ctx.stroke()
            } else if (i % middleSketch === 0) {
                ctx.beginPath()
                ctx.moveTo(0, point)
                ctx.lineTo(10, point)
                ctx.stroke()
            } else if (i % minSketch === 0) {
                ctx.beginPath()
                ctx.moveTo(6, point)
                ctx.lineTo(0, point)
                ctx.stroke()
            }
        }

        // 纵向标尺,绘制纵向文本
        ctx.rotate(-Math.PI / 2);
        for (let i = 0; i <= containerRect.height / Math.min(scale, 1); i += minSketch) {
            const point = i * props.scale
            if (i % maxSketch === 0) {
                if (i !== 0) {
                    ctx.fillText(String(i), -point, 16)
                }
            }
        }
        ctx.restore()
    }
}

/**
 * @description: 点击标尺触发
 * @param {MouseEvent} e
 * @return {void}
 */
const onClick =  (e: MouseEvent) => {
    const position = props.layout === 'horizontal'
        ? (e.offsetX) / props.scale
        : (e.offsetY) / props.scale
    emits('click', Math.ceil(position * 10) / 10)
}

/**
 * @description: 
 * @param {MouseEvent} e
 * @return {void}
 */
const onMousemove = (e: MouseEvent) => {
    const current = props.layout === 'horizontal' ? e.layerX / props.scale : e.layerY / props.scale
    emits('mousemove', Math.ceil(current * 10) / 10)
}

</script>

<template>
    <canvas style="width: 100%; height: 100%;" ref="canvasRef" @click="onClick" @mousemove="onMousemove" />
</template>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值