UniApp 中使用 Canvas 进行触摸绘制

0 阅读2分钟

在移动端开发中,手势交互是一个常见的需求,尤其是在图形应用中,用户希望能够自由绘制线条并与之交互。本文将介绍如何在 UniApp 中使用 Canvas 进行触摸绘制,实现用户通过触摸屏幕绘制线条,并在最后一个绘制点显示小蓝点,以便继续连接新线条。

需求分析

  1. 用户可以通过手指触摸屏幕绘制线条。
  2. 绘制完成后,终点会出现一个蓝色小点。
  3. 继续绘制时,必须从上一个终点的小蓝点开始,不能随意在画布上绘制。
  4. “清空”按钮可清除所有绘制内容。

代码实现

1. 初始化 Canvas

我们在 Vue 组件 onMounted 生命周期中初始化 Canvas 画布,确保在 DOM 加载完成后正确获取 canvas 上下文。

onMounted(() => {
    uni.getSystemInfo({
        success: (res) => {
            windowWidth = res.windowWidth;
            windowHeight = res.windowHeight;
        },
    });

    setTimeout(() => {
        ctx = uni.createCanvasContext('myCanvas');
        clearCanvas();
    }, 200);
});

2. 触摸事件处理

(1) handleTouchStart

  • 如果是第一次绘制,允许用户在任意位置开始。
  • 如果已经绘制了至少一条线,则只能从上一个终点(蓝点)开始新的线条。
const handleTouchStart = (e) => {
    if (!ctx) return;
    const touch = e.touches[0];
    
    if (hasFirstLine.value) {
        const lastLine = lines.value[lines.value.length - 1];
        const dx = touch.x - lastLine.x2;
        const dy = touch.y - lastLine.y2;
        const distance = Math.sqrt(dx * dx + dy * dy);
        if (distance > pointRadius) {
            isDrawing.value = false;
            return;
        }
    }

    lines.value.push({ x1: touch.x, y1: touch.y, x2: touch.x, y2: touch.y });
    isDrawing.value = true;
};

(2) handleTouchMove

  • 在触摸移动时更新终点,并重绘所有线条。
const handleTouchMove = (e) => {
    if (!isDrawing.value || !ctx) return;
    const touch = e.touches[0];
    const lastLine = lines.value[lines.value.length - 1];
    lastLine.x2 = touch.x;
    lastLine.y2 = touch.y;
    drawAllLines();
};

(3) handleTouchEnd

  • 触摸结束时,标记 hasFirstLine 变量,表示至少绘制了一条线,并重绘画布。
const handleTouchEnd = () => {
    if (!isDrawing.value || !ctx) return;
    isDrawing.value = false;
    hasFirstLine.value = true;
    drawAllLines();
};

3. 绘制功能

(1) drawAllLines

  • 每次绘制时,先清空画布,再绘制所有的线条。
  • 仅在最新绘制的线条终点显示小蓝点。
const drawAllLines = () => {
    if (!ctx) return;
    ctx.setFillStyle('#f8f8f8');
    ctx.fillRect(0, 0, windowWidth, windowHeight);
    ctx.setStrokeStyle('#FF0000');
    ctx.setLineWidth(5);
    
    lines.value.forEach((line, index) => {
        ctx.beginPath();
        ctx.moveTo(line.x1, line.y1);
        ctx.lineTo(line.x2, line.y2);
        ctx.stroke();

        if (index === lines.value.length - 1) {
            drawIcon(line.x2, line.y2);
        }
    });
    ctx.draw(true);
};

(2) drawIcon

  • 绘制一个小蓝点作为连接点。
const drawIcon = (x, y) => {
    if (!ctx) return;
    ctx.setFillStyle('#0000FF');
    ctx.beginPath();
    ctx.arc(x, y, 5, 0, 2 * Math.PI);
    ctx.fill();
    ctx.draw(true);
};

4. 清空画布

  • 通过 clearCanvas 清除所有的绘制内容,并重置变量。
const clearCanvas = () => {
    if (!ctx) return;
    ctx.setFillStyle('#f8f8f8');
    ctx.fillRect(0, 0, windowWidth, windowHeight);
    ctx.draw();
    lines.value = [];
    hasFirstLine.value = false;
};

总结

本次开发的功能点:

  1. 使用 UniApp 的 canvas 实现触摸绘制。
  2. 绘制完成后,在线条终点显示小蓝点。
  3. 仅允许从小蓝点继续绘制新的线条。
  4. 提供“清空”功能,能够重新开始绘制。

这套方案适用于手势绘制、路径规划等场景,并且可扩展性强,比如增加不同颜色的线条、保存绘制结果等。如果你有进一步的需求,可以基于这套逻辑进行优化和拓展。

希望这篇文章对你有所帮助!🚀