UniApp 中使用 Canvas 进行触摸绘制

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

需求分析

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

代码实现

1. 初始化 Canvas

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

ini 复制代码
onMounted(() => {
    uni.getSystemInfo({
        success: (res) => {
            windowWidth = res.windowWidth;
            windowHeight = res.windowHeight;
        },
    });

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

2. 触摸事件处理

(1) handleTouchStart

  • 如果是第一次绘制,允许用户在任意位置开始。
  • 如果已经绘制了至少一条线,则只能从上一个终点(蓝点)开始新的线条。
ini 复制代码
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

  • 在触摸移动时更新终点,并重绘所有线条。
ini 复制代码
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 变量,表示至少绘制了一条线,并重绘画布。
ini 复制代码
const handleTouchEnd = () => {
    if (!isDrawing.value || !ctx) return;
    isDrawing.value = false;
    hasFirstLine.value = true;
    drawAllLines();
};

3. 绘制功能

(1) drawAllLines

  • 每次绘制时,先清空画布,再绘制所有的线条。
  • 仅在最新绘制的线条终点显示小蓝点。
ini 复制代码
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

  • 绘制一个小蓝点作为连接点。
ini 复制代码
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 清除所有的绘制内容,并重置变量。
ini 复制代码
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. 提供"清空"功能,能够重新开始绘制。

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

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

相关推荐
benbenAItalk7 小时前
数字人口播视频的批量生产实践:素材规范、任务编排与质量验收
java·前端·音视频
2601_962885727 小时前
如何用 Python 扫描 A 股跳空缺口并统计缺口回补概率?
java·前端·python
西瓜太郎12348 小时前
Claude Code、Codex CLI、Gemini CLI 能否共用一枚 Key?先看协议选择矩阵
前端·api 网关·claude code·gemini cli·codex cli
陈随易10 小时前
在Finch用了62亿词元,我认为这是新一代Agent工具之神
前端·人工智能·后端
水域安全老周10 小时前
水趣钓鱼救生衣专利拆解:两级锁紧如何解决落水人衣分离
java·前端·网络
计算机魔术师10 小时前
Anthropic CEO突然喊踩刹车,OpenAI罕见力挺:AI这辆车不能只踩油门了
前端
wing9811 小时前
从codex转战workbuddy使用一周的感受
前端·人工智能·后端
EatFan11 小时前
Java接入支付宝 JSAPI 支付保姆教程(二):流程讲解与前后端代码讲解
前端·spring boot·后端·微信小程序·小程序·uni-app
梦想平凡11 小时前
百游棋牌源代码开发搭建教程(五):房间创建、座位分配与请求幂等实现
前端·javascript·数据库·源代码管理
IT_陈寒12 小时前
Redis的Set操作居然能把我的服务整挂了?
前端·人工智能·后端