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>
相关推荐
用户298698530144 小时前
在 React 中使用 JavaScript 将 Excel 转换为 SVG
前端·javascript·react.js
labixiong4 小时前
手写Promise--微任务、静态方法、async/await 全搞懂(三)
前端·javascript
铁皮饭盒5 小时前
3行代码搞定页面截图,Bun.WebView真的简单
javascript
kyriewen19 小时前
我手写了一个 EventEmitter,面试官追问了 6 个问题——第 4 个我没答上来
前端·javascript·面试
山河木马19 小时前
矩阵专题2-怎么创建视图矩阵(uViewMatrix)
javascript·webgl·计算机图形学
tangdou36909865521 小时前
AI真好玩系列-2分钟快速了解DeepAgents | Quick Guide to DeepAgents in 2 Minutes
前端·javascript·后端
张元清21 小时前
React useIntersectionObserver Hook:懒加载与可见性检测(2026)
javascript·react.js
彭于晏爱编程21 小时前
纯 JS + Node,一个下午手搓了能读懂公司代码的 AI 助手,老板以为我转行了
前端·javascript
妙码生花1 天前
从 PHP 到 AI + Golang,程序员自救转型手记(十四):眨眼小人登录页制作
前端·javascript·ai编程
妙码生花1 天前
从 PHP 到 AI + Golang,程序员自救转型手记(十三):前端路由初始化
前端·javascript·ai编程