Flutter绘制-05-shader专项

2,314 阅读4分钟

查看目录-->

是什么

Paint有个属性叫shader,也就是着色器,用于:

  • 画 或 填充一个图形时增加特殊效果,如drawCircle时,添加颜色渐变效果。
  • 优先级:colorFilter > shader > color,当共同存在时,优先级高的生效,优先级低的无效。
  • Gradient,绘制颜色渐变的着色器。
  • ImageShader,对图片做处理的着色器。

直白来讲,shader就是设定一个纹理图案,然后贴到要画的内容上。

Gradient

  • linear,线性渐变
  • radial,径向渐变,指的是圆圈,由内而外
  • sweep,扫描渐变,雷达扫描效果

linear 线性渐变

构造函数:

Gradient.linear(
    Offset from,
    Offset to,
    List<Color> colors, [    List<double>? colorStops,    TileMode tileMode = TileMode.clamp,    Float64List? matrix4,  ])
  • from,to, 确定Gradient影响的范围。
  • colors,指定线性渐变的颜色值数组.colors 与 colorStops的长度要相同。
  • colorStops,数组中每个值是0.0-1.0之间。colorStops[i]确定colors[i]从哪儿开始,指的是从from to中哪儿开始。
  • TileMode,colors的渲染模式。针对的是超出from to后怎么处理。
    • clamp,超出from to边界后用最近的颜色渲染,直到最后。如果不显示设置,此是默认值。
    • repeated,超出from to边界后重复
    • mirror, 超出from to边界后镜像显示
TileMode.clamp

image.png

void _testGradientLinear(Canvas canvas, Size size) {
    Paint _paint = Paint();
    _paint.isAntiAlias = true;
    _paint.style = PaintingStyle.stroke;
    _paint.strokeWidth = 30;
    _paint.shader = ui.Gradient.linear(
        Offset(-150, 0),
        Offset(100, 0),
        [Colors.red, Colors.blue, Colors.green],
        [0.2, 0.4, 0.6],
        TileMode.clamp,
        Float64List.fromList([
          1, 0, 0, 0,
          0, 1, 0, 0,
          0, 0, 1, 0,
          0, 0, 0, 1,
        ])
//      TileMode.repeated,
//        TileMode.mirror
    );
    canvas.drawLine(Offset(-150, 0), Offset(200, 0), _paint);
  }
TileMode.repeated

image.png

效果一直重复往前叠加 image.png

void _testGradientLinear(Canvas canvas, Size size) {
    Paint _paint = Paint();
    _paint.isAntiAlias = true;
    _paint.style = PaintingStyle.stroke;
    _paint.strokeWidth = 30;
    _paint.shader = ui.Gradient.linear(
        Offset(-100, 0),
        Offset(100, 0),
        [Colors.red, Colors.blue, Colors.green],
        [0.2, 0.4, 0.6],
//        TileMode.clamp,
      TileMode.repeated,
//        TileMode.mirror,
        Float64List.fromList([
          1, 0, 0, 0,
          0, 1, 0, 0,
          0, 0, 1, 0,
          0, 0, 0, 1,
        ]),

//
    );
    canvas.drawLine(Offset(-150, 0), Offset(150, 0), _paint);
  }
TileMode.mirror

image.png

void _testGradientLinear(Canvas canvas, Size size) {
    Paint _paint = Paint();
    _paint.isAntiAlias = true;
    _paint.style = PaintingStyle.stroke;
    _paint.strokeWidth = 30;
    _paint.shader = ui.Gradient.linear(
        Offset(-100, 0),
        Offset(100, 0),
        [Colors.red, Colors.blue, Colors.green],
        [0.2, 0.4, 0.6],
//        TileMode.clamp,
//      TileMode.repeated,
        TileMode.mirror,
        Float64List.fromList([
          1, 0, 0, 0,
          0, 1, 0, 0,
          0, 0, 1, 0,
          0, 0, 0, 1,
        ]),
    );
    canvas.drawLine(Offset(-150, 0), Offset(150, 0), _paint);
  }

radial 圆圈由内而外渐变

构造函数:

Gradient.radial(
    Offset center,
    double radius,
    List<Color> colors, [
    List<double>? colorStops,
    TileMode tileMode = TileMode.clamp,
    Float64List? matrix4,
    Offset? focal,
    double focalRadius = 0.0
  ])
  • center 中心点
  • radius 半径,类似于linear的from to
  • colors,指定线性渐变的颜色值数组.colors 与 colorStops的长度要相同。
  • colorStops,数组中每个值是0.0-1.0之间。colorStops[i]确定colors[i]从哪儿开始,指的是从center 到 radius中哪儿开始。
  • TileMode,colors的渲染模式。针对的是超出radius后怎么处理。
    • clamp,超出radius边界后用最近的颜色渲染,直到最后。如果不显示设置,此是默认值。
    • repeated,超出radius边界后重复
    • mirror, 超出radius边界后镜像显示
  • focal 设定shader圆心的偏移量,该圆心向focal的xy方向挤压效果
  • focalRadius 半径 下面这个例子,是画了一条线,然后设置上shader:

image.png

void _testGradientRadial(Canvas canvas, Size size) {
    Paint _paint = Paint();
    _paint.isAntiAlias = true;
    _paint.style = PaintingStyle.stroke;
    _paint.strokeWidth = 200;
    _paint.shader = ui.Gradient.radial(
      Offset(0, 0),
      150,
      [Colors.red, Colors.blue, Colors.green],
      [0.2, 0.4, 0.6],
        TileMode.clamp,
//      TileMode.repeated,
//      TileMode.mirror,
      Float64List.fromList([
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1,
      ]),
    );
    canvas.drawLine(Offset(-100, 0), Offset(100, 0), _paint);
  }
TileMode.clamp

image.png

void _testGradientRadial(Canvas canvas, Size size) {
    Paint _paint = Paint();
    _paint.isAntiAlias = true;
    _paint.style = PaintingStyle.fill;
    _paint.strokeWidth = 1;
    _paint.shader = ui.Gradient.radial(
      Offset(-80, -100),
      100,
      [Colors.red, Colors.blue, Colors.green],
      [0.2, 0.4, 0.8],
        TileMode.clamp,
//      TileMode.repeated,
//      TileMode.mirror,
    );
    canvas.drawCircle(Offset(-80, -100), 70, _paint);

    _paint.shader = ui.Gradient.radial(
      Offset(-80, 100),
      100,
      [Colors.red, Colors.blue, Colors.green],
      [0.2, 0.4, 0.8],
      TileMode.clamp,
//      TileMode.repeated,
//      TileMode.mirror,
    );
    canvas.drawCircle(Offset(-80, 100), 120, _paint);
  }

image.png

void _testGradientRadial(Canvas canvas, Size size) {
    Paint _paint = Paint();
    _paint.isAntiAlias = true;
    _paint.style = PaintingStyle.fill;
    _paint.strokeWidth = 1;
    _paint.shader = ui.Gradient.radial(
      Offset(-60, -100),
      100,
      [Colors.red, Colors.blue, Colors.green],
      [0.2, 0.4, 0.8],
        TileMode.clamp,
//      TileMode.repeated,
//      TileMode.mirror,
    );
    canvas.drawCircle(Offset(-80, -100), 70, _paint);

    _paint.shader = ui.Gradient.radial(
      Offset(-80, 100),
      100,
      [Colors.red, Colors.blue, Colors.green],
      [0.2, 0.4, 0.8],
      TileMode.clamp,
//      TileMode.repeated,
//      TileMode.mirror,
    );
    canvas.drawCircle(Offset(-80, 100), 120, _paint);
  }
TileMode.repeated

超过shader的radius后重复

image.png

void _testGradientRadial(Canvas canvas, Size size) {
    Paint _paint = Paint();
    _paint.isAntiAlias = true;
    _paint.style = PaintingStyle.fill;
    _paint.strokeWidth = 1;

    _paint.shader = ui.Gradient.radial(
      Offset(0, 0),
      80,
      [Colors.red, Colors.blue, Colors.green],
      [0.2, 0.4, 0.8],
//      TileMode.clamp,
      TileMode.repeated,
//      TileMode.mirror,
    );
    canvas.drawCircle(Offset(0, 0), 200, _paint);
  }
TileMode.mirror

超过shader的radius后镜像显示 image.png

void _testGradientRadial(Canvas canvas, Size size) {
    Paint _paint = Paint();
    _paint.isAntiAlias = true;
    _paint.style = PaintingStyle.fill;
    _paint.strokeWidth = 1;

    _paint.shader = ui.Gradient.radial(
      Offset(0, 0),
      80,
      [Colors.red, Colors.blue, Colors.green],
      [0.2, 0.4, 0.8],
//      TileMode.clamp,
//      TileMode.repeated,
      TileMode.mirror,
    );
    canvas.drawCircle(Offset(0, 0), 200, _paint);
  }
focal

focal偏向哪里就向哪里挤压,换个描述就是,从shader的center到focal延长线的挤压 image.png

void _testGradientRadialFocol(Canvas canvas, Size size) {
    Paint _paint = Paint();
    _paint.isAntiAlias = true;
    _paint.style = PaintingStyle.fill;
    _paint.strokeWidth = 1;

    _paint.shader = ui.Gradient.radial(
      Offset(0, 0),
      80,
      [Colors.red, Colors.blue, Colors.green],
      [0.2, 0.4, 0.8],
//      TileMode.clamp,
      TileMode.repeated,
//      TileMode.mirror,
      Float64List.fromList([
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1,
      ]),
      Offset(40,1),
      5
    );
    canvas.drawCircle(Offset(0, 0), 200, _paint);
  }

sweep 扫描渐变

构造函数:

Gradient.sweep(
    Offset center,
    List<Color> colors, [
    List<double>? colorStops,
    TileMode tileMode = TileMode.clamp,
    double startAngle = 0.0,
    double endAngle = math.pi * 2,
    Float64List? matrix4,
  ])
  • center 圆心
  • colors 渐变色
  • colorStops colors[i]渐变起点
  • tileMode 模式
  • startAngle 起始角度,类似于linear的from
  • endAngle 结束角度,类似于linear的to, 设定的是渐变范围
TileMode.clamp

image.png

void _testGradientSweep(Canvas canvas, Size size) {
    Paint _paint = Paint();
    _paint.isAntiAlias = true;
    _paint.style = PaintingStyle.fill;
    _paint.strokeWidth = 1;

    _paint.shader = ui.Gradient.sweep(
      Offset(0, 0),
      [Colors.red, Colors.blue, Colors.green],
      [0.3, 0.5, 1],
      TileMode.clamp,
//      TileMode.repeated,
//      TileMode.mirror,
      0,
      pi/2
    );
    canvas.drawCircle(Offset(0, 0), 200, _paint);
  }
TileMode.repeated

image.png


void _testGradientSweep(Canvas canvas, Size size) {
    Paint _paint = Paint();
    _paint.isAntiAlias = true;
    _paint.style = PaintingStyle.fill;
    _paint.strokeWidth = 1;

    _paint.shader = ui.Gradient.sweep(
      Offset(0, 0),
      [Colors.red, Colors.blue, Colors.green],
      [0.3, 0.5, 1],
//      TileMode.clamp,
      TileMode.repeated,
//      TileMode.mirror,
      0,
      pi/2
    );
    canvas.drawCircle(Offset(0, 0), 200, _paint);
  }
TileMode.mirror

image.png

void _testGradientSweep(Canvas canvas, Size size) {
    Paint _paint = Paint();
    _paint.isAntiAlias = true;
    _paint.style = PaintingStyle.fill;
    _paint.strokeWidth = 1;

    _paint.shader = ui.Gradient.sweep(
      Offset(0, 0),
      [Colors.red, Colors.blue, Colors.green],
      [0.3, 0.5, 1],
//      TileMode.clamp,
//      TileMode.repeated,
      TileMode.mirror,
      0,
      pi/2
    );
    canvas.drawCircle(Offset(0, 0), 200, _paint);
  }

ImageShader

用图片当做纹理。

构造函数:

ImageShader(Image image, TileMode tmx, TileMode tmy, Float64List matrix4) 
  • image 当做纹理的图片
  • tmx 在x方向的渲染方式 TimeMode 效果跟前面一样,不再细说
  • tmy 在y方向的渲染方式
TileMode.clamp

image.png

void _testImageShader(Canvas canvas, Size size) {
    Paint _paint = Paint();
    _paint.isAntiAlias = true;
    _paint.shader = ImageShader(_src, TileMode.clamp,
        TileMode.clamp,
//      TileMode.repeated,
//        TileMode.mirror,
        Float64List.fromList([
      1, 0, 0, 0,
      0, 1, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, 1,
    ]));

    canvas.drawCircle(Offset.zero, 200, _paint);
  }
TileMode.repeated

image.png

void _testImageShader(Canvas canvas, Size size) {
    Paint _paint = Paint();
    _paint.isAntiAlias = true;
    _paint.shader = ImageShader(_src, TileMode.repeated, TileMode.repeated, Float64List.fromList([
      1, 0, 0, 0,
      0, 1, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, 1,
    ]));

    canvas.drawCircle(Offset.zero, 200, _paint);
  }
TileMode.mirror

image.png

void _testImageShader(Canvas canvas, Size size) {
    Paint _paint = Paint();
    _paint.isAntiAlias = true;
    _paint.shader = ImageShader(_src, TileMode.mirror,
        TileMode.mirror,
//      TileMode.repeated,
//        TileMode.mirror,
        Float64List.fromList([
      1, 0, 0, 0,
      0, 1, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, 1,
    ]));

    canvas.drawCircle(Offset.zero, 200, _paint);
  }