canvas绘制拖拽箭头

canvas绘制拖拽箭头

完整代码

复制代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>跟随鼠标移动的拖拽箭头</title>
<style>
    body { margin: 0; background: #f0f0f0; }
    canvas { background: #fff; border: 1px solid #ccc; }
</style>
</head>
<body>
<canvas id="canvas"></canvas>

<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

let startPoint = null;
let currentMouse = null;
let arrow = null;
let clickCount = 0;

// 箭头头部参数
const headLength = 15; // 箭头头部长度
const headWidth = 25;  // 箭头头部宽度

function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // 第一次点击后,鼠标移动时就显示箭头
    if (startPoint && currentMouse && clickCount === 1) {
        const angle = angleBetweenPoints(startPoint.x, startPoint.y, currentMouse.x, currentMouse.y);

        // 箭身终点往回退 headLength
        const bodyEnd = shortenLine(currentMouse.x, currentMouse.y, angle, headLength);

        // 1. 绘制箭身(渐变线)
        drawGradientLine(startPoint.x, startPoint.y, bodyEnd.x, bodyEnd.y, 2, 10);

        // 2. 绘制箭头头部(三角形)
        drawArrowHead(currentMouse.x, currentMouse.y, angle, headWidth, headLength);
    }

    // 绘制最终箭头
    if (arrow) {
        const { x1, y1, x2, y2 } = arrow;
        const angle = angleBetweenPoints(x1, y1, x2, y2);

        // 箭身终点往回退 headLength
        const bodyEnd = shortenLine(x2, y2, angle, headLength);

        // 1. 绘制箭身(渐变线)
        drawGradientLine(x1, y1, bodyEnd.x, bodyEnd.y, 2, 10);

        // 2. 绘制箭头头部(三角形)
        drawArrowHead(x2, y2, angle, headWidth, headLength);
    }
}

// 绘制渐变线
function drawGradientLine(x1, y1, x2, y2, startWidth, endWidth) {
    const dx = x2 - x1;
    const dy = y2 - y1;
    const len = Math.sqrt(dx*dx + dy*dy);
    const steps = 50;
    const widthStep = (endWidth - startWidth) / steps;

    for (let i = 0; i < steps; i++) {
        const t = i / steps;
        const x = x1 + dx * t;
        const y = y1 + dy * t;
        const nextX = x1 + dx * (t + 1/steps);
        const nextY = y1 + dy * (t + 1/steps);

        ctx.beginPath();
        ctx.moveTo(x, y);
        ctx.lineTo(nextX, nextY);
        ctx.lineWidth = startWidth + widthStep * i;
        ctx.strokeStyle = '#333';
        ctx.stroke();
    }
}

// 绘制箭头头部
function drawArrowHead(x, y, angle, headWidth, headLength) {
    ctx.save();
    ctx.translate(x, y);
    ctx.rotate(angle);
    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.lineTo(-headLength, -headWidth/2);
    ctx.lineTo(-headLength, headWidth/2);
    ctx.closePath();
    ctx.fillStyle = '#333';
    ctx.fill();
    ctx.restore();
}

// 计算两点之间的角度
function angleBetweenPoints(x1, y1, x2, y2) {
    return Math.atan2(y2 - y1, x2 - x1);
}

// 计算缩短后的终点
function shortenLine(x, y, angle, length) {
    return {
        x: x - Math.cos(angle) * length,
        y: y - Math.sin(angle) * length
    };
}

canvas.addEventListener('mousedown', e => {
    const rect = canvas.getBoundingClientRect();
    const mx = e.clientX - rect.left;
    const my = e.clientY - rect.top;

    if (clickCount === 0) {
        startPoint = { x: mx, y: my };
        clickCount = 1;
    } else if (clickCount === 1) {
        arrow = { x1: startPoint.x, y1: startPoint.y, x2: mx, y2: my };
        startPoint = null;
        clickCount = 0;
    }
});

canvas.addEventListener('mousemove', e => {
    const rect = canvas.getBoundingClientRect();
    currentMouse = { x: e.clientX - rect.left, y: e.clientY - rect.top };
    draw();
});

draw();
</script>
</body>
</html>
相关推荐
全栈项目管理程序猿4 分钟前
ArcGIS JS 基础教程(13):屏幕坐标与地理坐标互转
javascript
名字还没想好☜22 分钟前
Next.js 用 cookies()/headers() 读请求信息:动态渲染触发、缓存失效与在 Server Action 里读写 cookie
前端·javascript·缓存·react·next.js·app router
智嵌研习社30 分钟前
Ollama 本地大模型完全配置指南:Modelfile 参数与系统环境变量深度解析
java·开发语言
XZ-07000143 分钟前
1-1-可视化-练习
开发语言·python
xiaominlaopodaren1 小时前
three.js最小地图运行时(六): 地图运行时
javascript·gis·three.js
小白勇闯网安圈1 小时前
Vue 工程化开发:组件复用、插槽、项目结构与 ES6 模块化
javascript·vue.js·es6
橙橙笔记1 小时前
Python学习第三部分
开发语言·python·学习
huainingning1 小时前
个人版WorkBuddy编写设备巡检并导出设备配置python脚本
开发语言·python
Java后端的Ai之路1 小时前
02、Python普通工厂模式
开发语言·人工智能·python·设计模式·普通工厂模式
韶博雅2 小时前
开启补充日志
java·开发语言·sql