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>
相关推荐
郑州光合科技余经理5 分钟前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
2601_962071576 分钟前
类变量和全局变量的生命周期有什么区别?
java·开发语言·jvm
Wang's Blog21 分钟前
Java 项目实战: 外卖平台优化-前后端分离开发模式与工程拆分
java·开发语言
凤城老人1 小时前
从 PyQt6 到 Electron:给 Edge TTS 做一个“多角色配音机“的踩坑手记
javascript·typescript·electron
Dovis(誓平步青云)1 小时前
浇水提醒刚弹出又消失,植物状态别只存一个百分比
开发语言·前端·javascript·pdf·ecmascript·电脑
sunshine22 girl1 小时前
Java学习六 Java常见API-2--String-3-字符串的截取替换和其他方法
java·开发语言·学习
LOVE️YOU2 小时前
Python 函数名、函数对象与“把函数作为参数传递”
开发语言·python
码艺-Alimjan2 小时前
Vben Admin 新增维吾尔语 Vben-Modal的关键坑之一
前端·javascript·vue.js
sunshine22 girl2 小时前
Java学习六 Java常见API-1-Radom随机数
java·开发语言·学习
可乐鸡翅yeah_2 小时前
hls.js 手动自定义 http 请求 loader,修改请求头实战
开发语言·前端·javascript·网络协议·http·ecmascript·m3u8在线