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>
相关推荐
岳哥i6 小时前
vue鼠标单机复制文本
javascript
哈哈不让取名字7 小时前
基于C++的爬虫框架
开发语言·c++·算法
幻云20107 小时前
Python深度学习:从筑基到登仙
前端·javascript·vue.js·人工智能·python
花间相见7 小时前
【JAVA开发】—— Nginx服务器
java·开发语言·nginx
扶苏-su7 小时前
Java---Properties 类
java·开发语言
一条咸鱼_SaltyFish8 小时前
远程鉴权中心设计:HTTP 与 gRPC 的技术决策与实践
开发语言·网络·网络协议·程序人生·http·开源软件·个人开发
我即将远走丶或许也能高飞9 小时前
vuex 和 pinia 的学习使用
开发语言·前端·javascript
沐知全栈开发9 小时前
SQL LEN() 函数详解
开发语言
钟离墨笺9 小时前
Go语言--2go基础-->基本数据类型
开发语言·前端·后端·golang
爱吃泡芙的小白白9 小时前
Vue 3 核心原理与实战:从响应式到企业级应用
前端·javascript·vue.js