1、背 景
我们在之前的文章鸿蒙UI开发——实现环形文字中有讨论过如何通过 RichText组件,使用SVG方案来绘制环形文字。该例子实现的效果如下:
又有朋友提出疑问,如果我们使用Canvas去绘制一个环形文字,需要怎么实现呢?
使用Canvas实现一个环形文字其实也并不复杂。我们下面来讨论如何实现。
Canvas的基本使用以及相关的实战我们可以参考之前发表的文章:
2、使用Canvas实现环形文字
👉🏻 实现思路
由于我们需要绘制的文字是按照圆的周长进行均匀分布,因此,我们第一步就是需要确定每个文字占用的“扇形”区域弧度是多少。
另外,由于文字是围绕一个圆形做分布,因此,我们在绘制每个文字时,都先将文字按照分配的弧度进行旋转,然后进行文字绘制。
由于旋转的中心点默认是(0,0),也就是最左上角,为了保证旋转是在画布的中央,我们只需要将画布做一个平移即可,将画布移动到圆的中心点。
有了思路后,我们开始实现(文末有完整代码)。
👉🏻 step1:定义Canvas并且初始化对应的Context。
Canvas有一个onReady()事件,onReady设置的callback将会在Canvas准备完毕后调用。
代码如下(这是一个基本的Canvas开发模板):
@Entry
@Component
@Preview
export struct Index {
private settings: RenderingContextSettings = new RenderingContextSettings(true);
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
private drawCircleText(text: string) {
// todo 绘制文本逻辑
}
build() {
Row() {
Column() {
Canvas(this.context)
.width(300)
.height(300)
.borderColor('green')
.borderWidth(1)
.onReady(() => this.drawCircleText('欢迎加入Harmony自习室,微信公众号【Harmony自习室】'))
}
.width('100%')
}
.height('100%');
}
}
效果如下(由于我们还未在画布上绘制内容,因此显示是空白):
👉🏻 step2:确定一些绘制参数
我们需要确定圆心位置、圆心半径、每个文字所占用的度数。代码如下:
private drawCircleText(text: string) {
// 获取画布的尺寸信息
const width = this.context.width, height = this.context.height;
const fontSize = 18; // 字体大小
const r = width / 2 - fontSize; // 半径
const charRadius = 360 / text.length; // 计算每个文字占用的度数
// 圆形位置: (width/2, height/2)
}
👉🏻 step3:偏移画布到圆心并且开始绘制每个文字
代码如下:我们在第10行代码将画布偏移到圆心,并且接着就是一个循环,分别绘制每个文字,在每个文字绘制完毕后,对画布进行旋转。
private drawCircleText(text: string) {
const width = this.context.width, height = this.context.height;
const fontSize = 18;
const r = width / 2 - fontSize;
this.context.clearRect(0, 0, width, height); // 清空画布
this.context.save();
this.context.font = `${fontSize}vp`
this.context.textBaseline = 'top';
const charRadius = 360 / text.length; // 一个字符所占的弧度
this.context.translate(width / 2, height / 2);
for (let i = 0; i < text.length; i++) {
this.context.fillText(text.charAt(i), 0, -r);
this.context.rotate(this.getAngle(charRadius));
}
this.context.restore();
}
一个完整的Demo如下:
@Entry
@Component
@Preview
export struct Index {
private settings: RenderingContextSettings = new RenderingContextSettings(true);
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
// 转换为弧度值
private getAngle(n: number) {
return Math.PI / 180 * n;
}
private drawCircleText(text: string) {
const width = this.context.width, height = this.context.height;
const fontSize = 18;
const r = width / 2 - fontSize;
this.context.clearRect(0, 0, width, height); // 清空画布
this.context.save();
this.context.font = `${fontSize}vp`
this.context.textBaseline = 'top';
const charRadius = 360 / text.length; // 一个字符所占的弧度
this.context.translate(width / 2, height / 2);
for (let i = 0; i < text.length; i++) {
this.context.fillText(text.charAt(i), 0, -r);
this.context.rotate(this.getAngle(charRadius));
}
this.context.restore();
}
build() {
Row() {
Column() {
Canvas(this.context)
.width(300)
.height(300)
.borderColor('green')
.borderWidth(1)
.onReady(() => this.drawCircleText('欢迎加入Harmony自习室,微信公众号【Harmony自习室】'))
}
.width('100%')
}
.height('100%');
}
}
效果如下:
3、one more thing..
另外,为了方便查看,我将两个实现方案都放置到一起,效果如下:
代码地址如下:
https://gitee.com/lantingshuxu/harmony-class-room-demos/tree/feat%2FcircleText/