Flutter中如何通过Canvas绘制带箭头的线段
Canvas线段添加箭头实现方法

实现原理
原生Canvas 2D上下文没有内置线段箭头样式,实现逻辑如下:
- 先调用原生线段绘制API,画出两点之间的主线段
- 通过三角函数计算线段的倾斜角度,推导箭头三角形另外两个顶点的坐标
- 在对应端点填充(或描边)三角形箭头,和主线段自然衔接即可
可复用封装代码
直接封装通用绘制函数,支持自定义箭头大小、颜色、单/双端箭头等配置:
/** * 绘制带箭头的线段 * @param {CanvasRenderingContext2D} ctx canvas 2D上下文 * @param {number} x1 起点X坐标 * @param {number} y1 起点Y坐标 * @param {number} x2 终点X坐标 * @param {number} y2 终点Y坐标 * @param {object} options 样式配置 */ function drawArrowLine(ctx, x1, y1, x2, y2, options = {}) { // 默认配置 const config = { lineWidth: 2, strokeStyle: '#000000', fillStyle: '#000000', arrowSize: 10, // 箭头侧边长度 arrowAngle: Math.PI / 6, // 箭头侧边与主线段夹角,默认30度 hasStartArrow: false, // 起点是否显示箭头 hasEndArrow: true // 终点是否显示箭头 } Object.assign(config, options) ctx.save() // 绘制主线段 ctx.lineWidth = config.lineWidth ctx.strokeStyle = config.strokeStyle ctx.fillStyle = config.fillStyle ctx.beginPath() ctx.moveTo(x1, y1) ctx.lineTo(x2, y2) ctx.stroke() // 计算主线段倾斜弧度 const lineSlope = Math.atan2(y2 - y1, x2 - x1) // 单箭头绘制逻辑 const drawArrow = (tipX, tipY, directionRadian) => { // 计算箭头两个底角坐标 const p1X = tipX - config.arrowSize * Math.cos(directionRadian - config.arrowAngle) const p1Y = tipY - config.arrowSize * Math.sin(directionRadian - config.arrowAngle) const p2X = tipX - config.arrowSize * Math.cos(directionRadian + config.arrowAngle) const p2Y = tipY - config.arrowSize * Math.sin(directionRadian + config.arrowAngle) ctx.beginPath() ctx.moveTo(tipX, tipY) ctx.lineTo(p1X, p1Y) ctx.lineTo(p2X, p2Y) ctx.closePath() ctx.fill() } // 按配置绘制两端箭头 if (config.hasEndArrow) drawArrow(x2, y2, lineSlope) if (config.hasStartArrow) drawArrow(x1, y1, lineSlope + Math.PI) ctx.restore() }
使用方式
获取canvas上下文后直接调用函数即可:
const canvas = document.getElementById('yourCanvas') const ctx = canvas.getContext('2d') // 绘制默认样式的单端箭头线段 drawArrowLine(ctx, 50, 50, 220, 160) // 绘制双端蓝色箭头线段,自定义箭头大小 drawArrowLine(ctx, 50, 220, 280, 220, { strokeStyle: '#2563eb', fillStyle: '#2563eb', arrowSize: 14, hasStartArrow: true })
自定义调整说明
- 调整
arrowAngle参数可以改变箭头尖锐度:数值越小箭头越尖,数值越大箭头越宽 - 如果需要空心箭头,把箭头绘制逻辑里的
ctx.fill()替换为ctx.stroke()即可,注意同步配置描边样式 - 如果画布存在缩放、平移变换,箭头大小需要同步乘上当前缩放比例,避免箭头大小失真
内容的提问来源于stack exchange,提问作者Kevin Yang
相关产品推荐
相关产品推荐

