Flutter繪製-05-shader專項

天色將變發表於2021-03-11

是什麼

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);
  }
複製程式碼

相關文章