网格画法:原生 Canvas 画网格,可拖动、可放大缩小、并带有坐标系 0 0 位置辅助线

2 篇文章 0 订阅
1 篇文章 0 订阅

原生 Canvas 画网格

网格:一种可以在 canvas 面板上绘制图形的辅助线集合。

在这里插入图片描述
我们设定 canvas 初始化左上角顶点为 0,0 点,向右👉和向下👇是 X Y 轴正方向

一、原生 Canvas 画网格

1、使用 Canvas 的方法

原生画网格,只需要使用 ctxmoveTolineTostrokeStyle

1. moveTo

是 Canvas 2D API 将一个新的子路径的起始点移动到 (x,y) 坐标的方法。

ctx.moveTo(x, y);

2. lineTo

是 Canvas 2D API 使用直线连接子路径的终点到 x,y 坐标的方法。

ctx.lineTo(x, y);

3. strokeStyle

是 Canvas 2D API 描述画笔(绘制图形)颜色或者样式的属性。默认值是 #000

ctx.strokeStyle = '#ff0000'

2、代码

import React, { useEffect, useState } from 'react';
import { Button } from 'antd';

interface IPageSlicePosType {
    x: number;
    y: number;
}

export const CanvasGrid = () => {

    // 当前 canvas 的 0 0 坐标,我们设置 canvas 左上角顶点为 0 0,向右👉和向下👇是 X Y 轴正方向,0,0 为 pageSlicePos 初始值
    const [pageSlicePos, setPageSlicePos] = useState<IPageSlicePosType>({
        x: 0,
        y: 0,
    });
    const [ctxVal, setCtxVal] = useState<any>(null); // canvas 的 ctx
    const [scale, setScale] = useState<number>(1); // 缩放比例
    const [solidColor] = useState<string>('#CCCCCC70'); // 实线颜色
    const [dashedColor] = useState<string>('#CCCCCC25'); // 虚线颜色
    const [zeroColor] = useState<string>('#358bf3'); // 0 点坐标系颜色

    // 监听 pageSlicePos 数据,有变动则进行 canvas 的绘制
    useEffect(() => {
        if (ctxVal) {
            // 重新绘制之前清空 canvas
            ctxVal.clearRect(0, 0, ctxVal.canvas.width, ctxVal.canvas.height);
        }
        drawLineGrid();
    }, [pageSlicePos]);

    /**
     * 绘制网格
     * @param scaleVal 缩放倍数
     */
    const drawLineGrid = (scaleVal = scale) => {
        /*获取元素*/
        var myCanvas: any = document.querySelector('#myCanvas');
        /*获取绘图工具*/
        var ctx = ctxVal || myCanvas.getContext('2d');
        setCtxVal(ctx);
        // 设置网格大小
        var girdSize = 5 * scaleVal;

        // 获取Canvas的width、height
        var CanvasWidth = ctx.canvas.width;
        var CanvasHeight = ctx.canvas.height;

        // 在 pageSlicePos 的 x,y 点位画一个 10 * 10 的红色标记用来表示当前页面的 0 0 坐标
        ctx.fillRect(pageSlicePos.x, pageSlicePos.y, 10, 10); // 效果图红色小方块
        ctx.fillStyle = 'red';

        const canvasXHeight = CanvasHeight - pageSlicePos.y;
        const canvasYWidth = CanvasWidth - pageSlicePos.x;
        // 从 pageSlicePos.y 处开始往 Y 轴正方向画 X 轴网格
        const xPageSliceTotal = Math.ceil(canvasXHeight / girdSize);
        for (let i = 0; i < xPageSliceTotal; i++) {
            ctx.beginPath(); // 开启路径,设置不同的样式
            ctx.moveTo(0, pageSlicePos.y + girdSize * i);
            ctx.lineTo(CanvasWidth, pageSlicePos.y + girdSize * i);
            ctx.strokeStyle = i === 0 ? zeroColor : (i % 5 === 0 ? solidColor : dashedColor); // 如果为 0 则用蓝色标记,取余 5 为实线,其余为比较淡的线
            ctx.stroke();
        }

        // 从 pageSlicePos.y 处开始往 Y 轴负方向画 X 轴网格
        const xRemaining = pageSlicePos.y;
        const xRemainingTotal = Math.ceil(xRemaining / girdSize);
        for (let i = 0; i < xRemainingTotal; i++) {
            if (i === 0) continue;
            ctx.beginPath(); // 开启路径,设置不同的样式
            ctx.moveTo(0, pageSlicePos.y - girdSize * i); // -0.5是为了解决像素模糊问题
            ctx.lineTo(CanvasWidth, pageSlicePos.y - girdSize * i);
            ctx.strokeStyle = i === 0 ? zeroColor : (i % 5 === 0 ? solidColor : dashedColor);// 如果为 0 则用蓝色标记,取余 5 为实线,其余为比较淡的线
            ctx.stroke();
        }

        // 从 pageSlicePos.x 处开始往 X 轴正方向画 Y 轴网格
        const yPageSliceTotal = Math.ceil(canvasYWidth / girdSize); // 计算需要绘画y轴的条数
        for (let j = 0; j < yPageSliceTotal; j++) {
            ctx.beginPath(); // 开启路径,设置不同的样式
            ctx.moveTo(pageSlicePos.x + girdSize * j, 0);
            ctx.lineTo(pageSlicePos.x + girdSize * j, CanvasHeight);
            ctx.strokeStyle = j === 0 ? zeroColor : (j % 5 === 0 ? solidColor : dashedColor);// 如果为 0 则用蓝色标记,取余 5 为实线,其余为比较淡的线
            ctx.stroke();
        }

        // 从 pageSlicePos.x 处开始往 X 轴负方向画 Y 轴网格
        const yRemaining = pageSlicePos.x;
        const yRemainingTotal = Math.ceil(yRemaining / girdSize);
        for (let j = 0; j < yRemainingTotal; j++) {
            if (j === 0) continue;
            ctx.beginPath(); // 开启路径,设置不同的样式
            ctx.moveTo(pageSlicePos.x - girdSize * j, 0);
            ctx.lineTo(pageSlicePos.x - girdSize * j, CanvasHeight);
            ctx.strokeStyle = j === 0 ? zeroColor : (j % 5 === 0 ? solidColor : dashedColor);// 如果为 0 则用蓝色标记,取余 5 为实线,其余为比较淡的线
            ctx.stroke();
        }
    };
    
	/**
     * 点击缩放,设置缩放倍数
     */
    const clickScale = () => {
        const scaleVal = scale + 1 > 6 ? 1 : scale + 1;
        setScale(scaleVal);
        ctxVal.clearRect(0, 0, ctxVal.canvas.width, ctxVal.canvas.height);
        drawLineGrid(scaleVal);
    }

    /**
     * 拖动 canvas 动态渲染,拖动时,动态设置 pageSlicePos 的值
     * @param e Event
     */
    const mouseDown = (e: any) => {
        const downX = e.clientX;
        const downY = e.clientY;
        const { x, y } = pageSlicePos;
        var myCanvas: any = document.querySelector('#myCanvas');
        myCanvas.onmousemove = (ev: any) => {
            const moveX = ev.clientX;
            const moveY = ev.clientY;
            setPageSlicePos({
                x: x + (moveX - downX),
                y: y + (moveY - downY),
            });
            myCanvas.onmouseup = (en: any) => {
                myCanvas.onmousemove = null;
                myCanvas.onmouseup = null;
            };
        }
        myCanvas.onmouseup = (en: any) => {
            myCanvas.onmousemove = null;
            myCanvas.onmouseup = null;
        };
    }
    return (
        <div className='canvas'>
            <div>
                <Button onClick={clickScale}>缩放{scale}</Button>
            </div>
            <div style={{ width: '600px', margin: '0 auto'}}>
                <canvas onMouseDown={mouseDown} id="myCanvas" width="600" height="400"></canvas>
            </div>
        </div>
    )
}

3、效果展示

在这里插入图片描述

4、代码

github 链接
码云链接

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,我可以帮你回答这个问题。 首先,在QML中,我们可以使用Canvas元素来绘制2D图形。要创建一个网格布,我们需要使用Canvas的API来绘制网格线。 其次,我们需要通过使用MouseArea元素来监听鼠标滚轮事件,以实现布的放大缩小。 以下是一个简单的实现示例: ```qml import QtQuick 2.0 Item { width: 400 height: 400 property int gridSize: 20 property int scaleFactor: 100 Canvas { id: canvas anchors.fill: parent onPaint: { var ctx = getContext("2d"); var width = canvas.width; var height = canvas.height; // 清除布 ctx.clearRect(0, 0, width, height); // 绘制网格线 ctx.strokeStyle = "black"; for (var x = 0; x <= width; x += gridSize) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, height); ctx.stroke(); } for (var y = 0; y <= height; y += gridSize) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke(); } } } MouseArea { anchors.fill: parent onWheel: { // 根据滚轮事件的delta属性,计算缩放比例 scaleFactor += wheel.delta / 120; canvas.width = width * scaleFactor / 100; canvas.height = height * scaleFactor / 100; canvas.requestPaint(); } } } ``` 在这个示例中,我们创建了一个Canvas元素,并在其onPaint信号处理函数中绘制了网格线。我们还使用了MouseArea元素来监听鼠标滚轮事件,并根据事件的delta属性计算缩放比例,以实现布的放大缩小。 注意,这个示例中的缩放比例是通过scaleFactor属性来控制的,它的默认值为100。你可以根据自己的需要来调整这个值。 希望这个示例对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gqkmiss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值