鸿蒙开发-实战:用PathEffect画虚线标注线
摘要 本文介绍了如何使用PathEffect绘制地图标注虚线,包含以下核心内容: 虚线绘制流程:通过创建Path路径、定义坐标点、设置画笔属性、应用虚线效果等步骤完成绘制。 PathEffect功能:可改变路径绘制方式,支持虚线、路径重复和变形等效果。 关键实现: 使用createDashPathEffect创建虚线效果,参数控制线段和间隔 绘制地图背景网格和主路线 通过"几"字形虚线标注连接地点
实战:用 PathEffect 画虚线——地图标注线
在地图 APP 里,你经常能看到虚线标注——比如从 A 点到 B 点的路线、某个区域的边界线、或者 POI(兴趣点)的标注线。今天我们用 PathEffect 来实现这种效果。
虚线绘制流程
下面是用PathEffect绘制虚线的完整流程:
PathEffect 是什么?
PathEffect 是路径特效,它可以改变路径的绘制方式。比如:
- 虚线:把实线变成虚线
- 路径重复:沿路径重复绘制图案
- 路径变形:让路径扭曲、变形
今天我们主要用虚线效果。
第一步:创建虚线效果
import { PathEffect } from '@kit.ArkGraphics2D';
// 创建虚线效果
// 参数:[线段长度, 间隔长度]
let dashEffect = PathEffect.createDashPathEffect([10, 5], 0);
createDashPathEffect(intervals, phase) 的参数:
intervals:一个数组,交替表示"画"和"不画"的长度。[10, 5]表示画 10 像素,空 5 像素,再画 10 像素,再空 5 像素…phase:偏移量。设为 0 就从头开始,设为其他值可以调整虚线的起始位置。
第二步:设置地图背景
import { common } from '@kit.AbilityKit';
import { CanvasRenderingContext2D, Path, PathEffect, Pen } from '@kit.ArkGraphics2D';
导入需要的模块。
drawMapAnnotation() {
const ctx = this.context;
const width = 350;
const height = 400;
ctx.clearRect(0, 0, width, height);
清空画布。
// 模拟地图背景(灰色网格)
ctx.strokeStyle = '#e8e8e8';
ctx.lineWidth = 1;
for (let x = 0; x < width; x += 30) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, height);
ctx.stroke();
}
for (let y = 0; y < height; y += 30) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(width, y);
ctx.stroke();
}
画一个灰色网格模拟地图背景。每 30 像素画一条线。
第三步:定义地点和画路线
// 定义两个地点
const pointA = { x: 80, y: 300, name: '起点' };
const pointB = { x: 270, y: 100, name: '终点' };
定义起点和终点的坐标。
// 画实线路线(主路线)
let mainPath = new Path();
mainPath.moveTo(pointA.x, pointA.y);
mainPath.cubicTo(
pointA.x + 50, pointA.y - 100, // 控制点1
pointB.x - 50, pointB.y + 100, // 控制点2
pointB.x, pointB.y // 终点
);
用三次贝塞尔曲线画主路线,从 A 点到 B 点是一条平滑的曲线。
// 设置画笔
let mainPen = new Pen();
mainPen.setColor({ alpha: 255, red: 33, green: 150, blue: 243 }); // 蓝色
mainPen.setStrokeWidth(4);
ctx.attachPen(mainPen);
ctx.strokePath(mainPath);
ctx.detachPen();
用蓝色画笔画主路线,宽度 4 像素。
第四步:画虚线标注
// 画虚线标注(距离标注线)
let annotationPath = new Path();
annotationPath.moveTo(pointA.x, pointA.y);
annotationPath.lineTo(pointA.x, pointA.y - 60);
annotationPath.lineTo(pointB.x, pointB.y - 60);
annotationPath.lineTo(pointB.x, pointB.y);
画一个"几"字形的标注线:从 A 点向上,水平延伸到 B 点上方,再向下到 B 点。
let dashPen = new Pen();
dashPen.setColor({ alpha: 180, red: 150, green: 150, blue: 150 }); // 灰色半透明
dashPen.setStrokeWidth(1);
dashPen.setPathEffect(PathEffect.createDashPathEffect([8, 4], 0));
创建虚线画笔。setPathEffect 应用虚线效果,[8, 4] 表示画 8 像素、空 4 像素。
ctx.attachPen(dashPen);
ctx.strokePath(annotationPath);
ctx.detachPen();
用虚线画笔画标注线。
第五步:画标记点
// 画起点标记
this.drawMarker(ctx, pointA.x, pointA.y, '#4CAF50', 'A');
// 画终点标记
this.drawMarker(ctx, pointB.x, pointB.y, '#f44336', 'B');
调用辅助方法画起点和终点的标记。
drawMarker(ctx: CanvasRenderingContext2D, x: number, y: number, color: string, label: string) {
// 外圈
ctx.beginPath();
ctx.arc(x, y, 15, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.fill();
// 内圈
ctx.beginPath();
ctx.arc(x, y, 8, 0, Math.PI * 2);
ctx.fillStyle = '#ffffff';
ctx.fill();
// 标签
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 12px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(label, x, y);
}
标记由外圈(彩色)、内圈(白色)、标签文字组成。
第六步:画 POI 标注线
// 显示距离信息
ctx.fillStyle = '#333333';
ctx.font = '14px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('2.5 公里', (pointA.x + pointB.x) / 2, pointA.y - 65);
在标注线上方显示距离。
// 画 POI 标注线
this.drawPOIAnnotation(ctx, 200, 200, '餐厅', '#ff9800');
this.drawPOIAnnotation(ctx, 150, 150, '商场', '#9c27b0');
}
画两个 POI(兴趣点)的标注。
drawPOIAnnotation(ctx: CanvasRenderingContext2D, x: number, y: number, name: string, color: string) {
// POI 点
ctx.beginPath();
ctx.arc(x, y, 6, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.fill();
画 POI 的小圆点。
// 标注线(虚线)
let labelX = x + 40;
let labelY = y - 30;
let annotationPath = new Path();
annotationPath.moveTo(x, y);
annotationPath.lineTo(labelX, labelY);
let pen = new Pen();
pen.setColor({ alpha: 200, red: 100, green: 100, blue: 100 });
pen.setStrokeWidth(1);
pen.setPathEffect(PathEffect.createDashPathEffect([4, 3], 0));
ctx.attachPen(pen);
ctx.strokePath(annotationPath);
ctx.detachPen();
用虚线连接 POI 点和标签。
// 标签背景
ctx.fillStyle = color;
this.roundRect(ctx, labelX, labelY - 12, 50, 20, 4);
ctx.fill();
// 标签文字
ctx.fillStyle = '#ffffff';
ctx.font = '12px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(name, labelX + 25, labelY - 2);
}
画标签的背景和文字。
不同虚线样式对比
通过调整intervals数组可以实现多种虚线效果:
不同的虚线样式
你可以通过调整 intervals 数组来实现不同的虚线效果:
// 标准虚线
PathEffect.createDashPathEffect([10, 5], 0);
// 点线交替
PathEffect.createDashPathEffect([15, 5, 5, 5], 0);
// 长虚线
PathEffect.createDashPathEffect([20, 10], 0);
// 点状
PathEffect.createDashPathEffect([2, 6], 0);
// 长短交替
PathEffect.createDashPathEffect([20, 5, 10, 5], 0);
intervals 数组可以有任意多个值,Canvas 会循环使用。比如 [15, 5, 5, 5] 表示:画 15 像素,空 5 像素,画 5 像素,空 5 像素,然后重复。
phase 参数的妙用
phase 参数可以偏移虚线的起始位置。这在做动画时很有用——通过不断改变 phase,可以让虚线"流动"起来:
@State dashPhase: number = 0;
drawAnimatedDash() {
let effect = PathEffect.createDashPathEffect([10, 5], this.dashPhase);
// ...
}
地图标注线组成
一个完整的地图标注线包含多个元素:
小结
地图标注线的核心:
- 用
Path画路线和标注线 - 用
PathEffect.createDashPathEffect创建虚线效果 - 用
Pen.setPathEffect应用到画笔 - 调整
intervals数组控制虚线样式
虚线是 UI 设计的基础元素,地图标注只是其中一个应用场景。你还可以用它画表格边框、分隔线、选区边框等等。
更多推荐


所有评论(0)