鸿蒙开发-实战:用渐变做一个彩虹进度条
实战:用 ShaderEffect 做彩虹渐变进度条
渐变效果在 UI 设计中很常见——进度条、按钮背景、标题栏等等。今天我们来做个彩虹渐变进度条,顺便深入了解 ShaderEffect 的用法。
彩虹进度条绘制流程
下面是彩虹渐变进度条的完整绘制流程:
什么是 ShaderEffect?
简单说,ShaderEffect 就是"着色器效果"——它告诉 Canvas “用什么方式来填充颜色”。普通的 fillStyle 只能填纯色,但 ShaderEffect 可以填渐变色、图片、甚至自定义的颜色计算逻辑。
HarmonyOS 支持三种渐变:
- 线性渐变:颜色沿直线方向变化
- 径向渐变:颜色从中心向外辐射变化
- 扫描渐变:颜色绕中心点旋转变化
第一步:创建 Canvas 和数据
import { common } from '@kit.AbilityKit';
import { CanvasRenderingContext2D } from '@kit.ArkGraphics2D';
@Entry
@Component
struct RainbowProgress {
@State progress: number = 0.3; // 进度 0-1
定义进度值,默认 30%。
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(
new RenderingContextSettings(true)
);
创建 Canvas 渲染上下文。
第二步:画背景条
drawProgress() {
const ctx = this.context;
const width = 300;
const height = 80;
const barHeight = 20;
const barY = (height - barHeight) / 2;
const borderRadius = 10;
// 清空画布
ctx.clearRect(0, 0, width, height);
清空画布,计算进度条的位置(垂直居中)。
// 画背景条(灰色)
ctx.fillStyle = '#e0e0e0';
this.roundRect(ctx, 0, barY, width, barHeight, borderRadius);
ctx.fill();
用灰色填充一个圆角矩形作为背景。
第三步:创建彩虹渐变
// 创建彩虹渐变
let gradient = ctx.createLinearGradient(0, 0, width, 0);
createLinearGradient(x1, y1, x2, y2) 创建一个线性渐变。这里从 (0, 0) 到 (width, 0),是水平方向的渐变。
如果你想做垂直渐变,改成 (0, 0, 0, height)。对角线渐变用 (0, 0, width, height)。
gradient.addColorStop(0, '#ff0000'); // 红
gradient.addColorStop(0.17, '#ff8c00'); // 橙
gradient.addColorStop(0.33, '#ffff00'); // 黄
gradient.addColorStop(0.5, '#00ff00'); // 绿
gradient.addColorStop(0.67, '#0000ff'); // 蓝
gradient.addColorStop(0.83, '#4b0082'); // 靛
gradient.addColorStop(1, '#9400d3'); // 紫
addColorStop(position, color) 在渐变的指定位置添加颜色。position 是 0-1 之间的值,0 是起点,1 是终点。
彩虹有 7 种颜色,所以每个颜色间隔约 0.17(1/6)。
第四步:用渐变填充进度条
// 画进度条(彩虹渐变)
ctx.fillStyle = gradient;
this.roundRect(ctx, 0, barY, width * this.progress, barHeight, borderRadius);
ctx.fill();
把 fillStyle 设为渐变对象,然后正常绘制形状。Canvas 会自动用渐变来填充。
width * this.progress 让进度条的宽度随进度变化。
第五步:显示进度文字
// 显示进度文字
ctx.fillStyle = '#333333';
ctx.font = 'bold 16px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(`${Math.round(this.progress * 100)}%`, width / 2, barY + barHeight + 20);
}
在进度条下方显示百分比文字。Math.round 把小数转成整数。
第六步:画圆角矩形的辅助方法
roundRect(ctx: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, r: number) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.arcTo(x + w, y, x + w, y + r, r);
ctx.lineTo(x + w, y + h - r);
ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
ctx.lineTo(x + r, y + h);
ctx.arcTo(x, y + h, x, y + h - r, r);
ctx.lineTo(x, y + r);
ctx.arcTo(x, y, x + r, y, r);
ctx.closePath();
}
Canvas 没有直接画圆角矩形的方法,我们用 arcTo 来画四个圆角。arcTo(x1, y1, x2, y2, radius) 画一个从当前点到 (x2, y2) 的弧线,圆角半径是 radius。
第七步:添加交互
build() {
Column() {
Text('彩虹进度条')
.fontSize(20)
.margin({ top: 20 })
Canvas(this.context)
.width(300)
.height(80)
.onReady(() => {
this.drawProgress();
})
// 模拟进度变化
Slider({ value: this.progress * 100, min: 0, max: 100, step: 1 })
.width(280)
.margin({ top: 20 })
.onChange((value: number) => {
this.progress = value / 100;
this.drawProgress();
})
}
.width('100%')
.height('100%')
}
添加一个滑块控件,拖动时更新进度并重新绘制。
动态彩虹动画流程
让彩虹进度条产生流动效果:
进阶:动态彩虹动画
如果你想让彩虹"流动"起来,可以用渐变的 addColorStop 动态调整颜色位置:
@State gradientOffset: number = 0;
drawAnimatedProgress() {
let gradient = ctx.createLinearGradient(0, 0, width, 0);
// 偏移颜色位置
const colors = ['#ff0000', '#ff8c00', '#ffff00', '#00ff00', '#0000ff', '#4b0082', '#9400d3'];
for (let i = 0; i < colors.length; i++) {
let pos = (i / (colors.length - 1) + this.gradientOffset) % 1;
gradient.addColorStop(pos, colors[i]);
}
ctx.fillStyle = gradient;
// ...
}
通过不断改变 gradientOffset,渐变的颜色位置会偏移,看起来就像彩虹在流动。
三种渐变类型对比
HarmonyOS支持三种渐变方式,各有特点:
径向渐变
let radialGradient = ctx.createRadialGradient(
centerX, centerY, innerRadius, // 内圈
centerX, centerY, outerRadius // 外圈
);
radialGradient.addColorStop(0, '#ffffff'); // 中心白色
radialGradient.addColorStop(1, '#000000'); // 边缘黑色
径向渐变从中心向外辐射,适合做光晕、按钮高光等效果。
小结
彩虹进度条的核心:
createLinearGradient创建线性渐变addColorStop添加彩虹颜色- 把渐变赋值给
fillStyle - 用
arcTo画圆角矩形
渐变是 UI 设计的基础工具,掌握了线性渐变和径向渐变,你就能做出很多漂亮的视觉效果。
更多推荐



所有评论(0)