Canvas绘制加拿大国旗枫叶时如何在直线交点处生成指定角度曲线
Canvas 绘制加拿大国旗枫叶圆角过渡方案
核心解决思路
你当前代码全部采用直线连接顶点,导致拐点为尖角,要生成夹角范围内的指定过渡圆弧,可通过计算拐点处的圆角路径实现,步骤如下:
- 确定所有需要添加过渡圆弧的拐点坐标,以及每个拐点对应的圆角半径,可按枫叶宽高比例换算得到,当前方案取宽度的1.2%适配官方构造规范,可按需调整
- 对每个需要处理的拐点,获取其前一个相邻点、拐点本身、后一个相邻点三个坐标,计算夹角方向的圆角起点、终点、圆心和角度范围
- 绘制路径时跳过原拐点的直线连接操作,替换为绘制对应范围内的圆弧
优化后完整代码
function drawMapleLeaf (ctx,x,y,width,height) { let rx = width; let ry = height; let xc = x + rx / 2; let yc = y + height; // 圆角半径,可根据实际效果调整 const cornerRadius = rx * 0.012; let points = []; points.push([xc + rx * 0.021423, yc - ry * 0.215686]); points.push([xc + rx * 0.270780, yc - ry * 0.203804]); points.push([xc + rx * 0.271820, yc - ry * 0.295752]); points.push([xc + rx * 0.482015, yc - ry * 0.411765]); points.push([xc + rx * 0.443046, yc - ry * 0.483267]); points.push([xc + rx * 0.500000, yc - ry * 0.587435]); points.push([xc + rx * 0.363353, yc - ry * 0.619576]); points.push([xc + rx * 0.342287, yc - ry * 0.693849]); points.push([xc + rx * 0.153596, yc - ry * 0.612537]); points.push([xc + rx * 0.201601, yc - ry * 0.918462]); points.push([xc + rx * 0.093001, yc - ry * 0.894514]); points.push([xc, yc - ry]); points.push([xc - rx * 0.093001, yc - ry * 0.894514]); points.push([xc - rx * 0.201601, yc - ry * 0.918462]); points.push([xc - rx * 0.153596, yc - ry * 0.612537]); points.push([xc - rx * 0.342287, yc - ry * 0.693849]); points.push([xc - rx * 0.363353, yc - ry * 0.619576]); points.push([xc - rx * 0.500000, yc - ry * 0.587435]); points.push([xc - rx * 0.443046, yc - ry * 0.483267]); points.push([xc - rx * 0.482015, yc - ry * 0.411765]); points.push([xc - rx * 0.271820, yc - ry * 0.295752]); points.push([xc - rx * 0.2707796, yc - ry * 0.203804]); points.push([xc - rx * 0.021423, yc - ry * 0.215686]); points.push([xc - rx * 0.021423, yc]); points.push([xc + rx * 0.021423, yc]); points.push([xc + rx * 0.021423, yc - ry * 0.215686]); // 计算两点距离 function dist(p1, p2) { return Math.hypot(p2[0] - p1[0], p2[1] - p1[1]); } // 计算单位向量 function unitVector(p1, p2) { let d = dist(p1, p2); return [(p2[0] - p1[0])/d, (p2[1] - p1[1])/d]; } // 计算两点连线角度 function angle(p1, p2) { return Math.atan2(p2[1] - p1[1], p2[0] - p1[0]); } ctx.beginPath(); ctx.moveTo(points[0][0], points[0][1]); for(let i=1;i<points.length-1;i++){ let p0 = points[i-1]; let p1 = points[i]; let p2 = points[i+1]; // 计算两个边的单位向量 let v1 = unitVector(p1, p0); let v2 = unitVector(p1, p2); // 计算夹角半角 let halfAngle = Math.acos((v1[0]*v2[0] + v1[1]*v2[1])/2); // 计算圆角到顶点的距离 let distToVertex = cornerRadius / Math.sin(halfAngle); // 计算圆角起点和终点 let startPoint = [p1[0] + v1[0] * distToVertex, p1[1] + v1[1] * distToVertex]; let endPoint = [p1[0] + v2[0] * distToVertex, p1[1] + v2[1] * distToVertex]; // 计算角平分线方向 let bisector = [(v1[0] + v2[0])/2, (v1[1] + v2[1])/2]; let bisectorLen = Math.hypot(bisector[0], bisector[1]); bisector = [bisector[0]/bisectorLen, bisector[1]/bisectorLen]; // 计算圆心坐标 let center = [p1[0] + bisector[0] * distToVertex * Math.cos(halfAngle), p1[1] + bisector[1] * distToVertex * Math.cos(halfAngle)]; // 计算圆弧起始角度和结束角度 let startAngle = angle(center, startPoint); let endAngle = angle(center, endPoint); // 绘制圆弧,逆时针绘制符合枫叶路径方向 ctx.arc(center[0], center[1], cornerRadius, startAngle, endAngle, true); } ctx.closePath(); ctx.fillStyle='red'; ctx.fill(); }
生成的过渡圆弧仅存在于两条直线的夹角范围内,完全符合构造图指定的角度要求。
内容的提问来源于stack exchange,提问作者michael
相关产品推荐
相关产品推荐

