实战:用 ShaderEffect 做彩虹渐变进度条

渐变效果在 UI 设计中很常见——进度条、按钮背景、标题栏等等。今天我们来做个彩虹渐变进度条,顺便深入了解 ShaderEffect 的用法。

彩虹进度条绘制流程

下面是彩虹渐变进度条的完整绘制流程:

创建Canvas上下文

清空画布

画灰色背景条

创建线性渐变createLinearGradient

添加彩虹颜色addColorStop

红橙黄绿蓝靛紫

设置fillStyle为渐变

画彩虹进度条

显示进度百分比

添加滑块交互

什么是 ShaderEffect?

简单说,ShaderEffect 就是"着色器效果"——它告诉 Canvas “用什么方式来填充颜色”。普通的 fillStyle 只能填纯色,但 ShaderEffect 可以填渐变色、图片、甚至自定义的颜色计算逻辑。

HarmonyOS 支持三种渐变:

  1. 线性渐变:颜色沿直线方向变化
  2. 径向渐变:颜色从中心向外辐射变化
  3. 扫描渐变:颜色绕中心点旋转变化

第一步:创建 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%')
}

添加一个滑块控件,拖动时更新进度并重新绘制。

动态彩虹动画流程

让彩虹进度条产生流动效果:

定义gradientOffset变量

创建线性渐变

遍历彩虹颜色数组

计算偏移后位置

pos = i/6 + offset mod 1

addColorStop添加颜色

使用渐变填充

更新gradientOffset

requestAnimationFrame

进阶:动态彩虹动画

如果你想让彩虹"流动"起来,可以用渐变的 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支持三种渐变方式,各有特点:

ShaderEffect渐变

线性渐变

径向渐变

扫描渐变

颜色沿直线变化

适合进度条/背景

颜色从中心向外辐射

适合光晕/按钮高光

颜色绕中心旋转

适合环形进度/仪表盘

径向渐变

let radialGradient = ctx.createRadialGradient(
  centerX, centerY, innerRadius,  // 内圈
  centerX, centerY, outerRadius   // 外圈
);
radialGradient.addColorStop(0, '#ffffff');  // 中心白色
radialGradient.addColorStop(1, '#000000');  // 边缘黑色

径向渐变从中心向外辐射,适合做光晕、按钮高光等效果。

小结

彩虹进度条的核心:

  1. createLinearGradient 创建线性渐变
  2. addColorStop 添加彩虹颜色
  3. 把渐变赋值给 fillStyle
  4. arcTo 画圆角矩形

渐变是 UI 设计的基础工具,掌握了线性渐变和径向渐变,你就能做出很多漂亮的视觉效果。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐