如下图所示,最近经过群友的问题在 codepen.io 上看到了一个文本「抽动」的动画完成,看起来就像是生活中常见的「霓虹灯招牌」毛病时的「抽动」作用,而本篇的目标经过「抄袭」这个完成,协助咱们理解 Flutter 里的一些完成小技巧。

Flutter 小技巧之霓虹灯文本的「故障」效果的实现

这个作用在 codepen 上是经过 CSS 完成的,完成思路 codepen 上的 Glitch Walkthrough 大致有提示,可是 Flutter 没有强壮的 CSS,那么如何将它「复刻」到 Flutter 上便是本篇的中心要点。

不得不说 CSS 很强壮,要在 Flutter 上完成相似的作用仍是比较「折腾」。

而要在 Flutter 上完成相似 Glitch Walkthrough 的作用,大致上咱们需求处理:

  • 相似霓虹灯作用的文本
  • 文本内容撕裂的作用
  • 文本变形闪烁的作用

那么接下来咱们就依照这个流程来完成一个 Flutter 上的 Glitch Walkthrough 。

霓虹灯文本

这一步其实相对简略,Flutter 的 TextStyle 供给了 shadows 装备,经过它能够快速完成一个「会发光」的文本。

咱们这儿经过两个 Shadow 来完成「发光」的视觉作用,中心便是运用 ShadowblurRadius 来让布景出现必定程度的含糊发散,然后两个 Shadow 构成不一样的色彩深度和发散作用,然后达到看起来「发亮」的作用。

如下图是没有填充文本色彩时 Shadow 的作用。

Flutter 小技巧之霓虹灯文本的「故障」效果的实现

最终,如下代码所示,咱们只需求经过 foreground 给文本弥补下色彩,就能够看到如下图所示的相似「霓虹灯」作用的文本。

当然这儿你不想用 foreground ,只用简略的 color 也能够。

Text(
widget.text,
style:TextStyle(
 fontSize:48,
 fontWeight:FontWeight.bold,
 foreground:Paint()
  ..style=PaintingStyle.fill
  ..strokeWidth=5
  ..color=Colors.white,
 shadows: [
  Shadow(
   blurRadius:10,
   color:Colors.white,
   offset:Offset(0,0),
  ),
  Shadow(
   blurRadius:20,
   color:Colors.white30,
   offset:Offset(0,0),
  ),
 ],
),
)

Flutter 小技巧之霓虹灯文本的「故障」效果的实现

这儿提个题外话,其实相似的思路用在图片上也能够完成「发光」的作用,如下代码所示,经过 Stack 嵌套两个 Image ,然后中心经过 BackdropFilterImageFilter 做一层含糊,让底下的图片含糊后发散发生相似「发光」的作用。

varchild=Image.asset(
'static/test_logo.png',
width:250,
);
returnStack(
  children: [
   child,
   Positioned.fill(
    child:BackdropFilter(
     filter:ImageFilter.blur(
      sigmaX:blurRadius,
      sigmaY:blurRadius,
     ),
     child:Container(color:Colors.transparent),
    ),
   ),
   child,
  ],
 )
);

如下图所示,图片终究能够经过自己的色彩发生相似「发光」的作用,当然这部分仅仅额定的拓宽内容,和咱们要完成的作用无关。

Flutter 小技巧之霓虹灯文本的「故障」效果的实现

文本撕裂

这部分能够说是需求作用的中心,这儿咱们需求用到 ClipPathPolygon ,经过 Polygon 来完成随机的多边形途径,然后运用 ClipPath 对文本内容进行随机的途径裁剪。

尽管说用 Polygon , 可是 Flutter 官方并没有直接供给相似前端 CSS 的 Polygon 多边形 API 支撑,可是社区总有「好心人」,咱们能够直接运用 Flutter 上相似的第三方库: polygon: ^0.1.0

简略说 Polygon 便是依照 step 对 PathmoveToquadraticBezierTo 等 API 进行了封装。

Flutter 上的 Polygon 取值规模是 -1 ~ 1 ,也便是依照比例决议方位,比方 – 1 便是起始点, 1 便是最大宽高, 更具体如下面的代码所示,这儿运用 Polygon 添加了三个点,终究这三个点构成的 Path 会制作出一个三角形。

List<Offset>generatePoint() {
List<Offset>points=[];
points.add(Offset(-1,-1));
points.add(Offset(-1,0));
points.add(Offset(0,-1));
returnpoints;
}

Flutter 小技巧之霓虹灯文本的「故障」效果的实现

如下代码所示,那如果如果 point 的数量多了,就能够构成一系列不规则的形状,比方下面代码随机添加了 60 个点的方位,能够看到此时屏幕上的白色 Container 被裁剪成「杂乱」的形状。

List<Offset>generatePoint() {
List<Offset>points=[];
​
points.add(Offset(-1.00,-0.76));
points.add(Offset(0.06,-0.76));
points.add(Offset(0.06,-0.48));
points.add(Offset(-0.50,-0.48));
points.add(Offset(-0.50,0.72));
points.add(Offset(-0.38,0.72));
points.add(Offset(-0.38,-1.00));
points.add(Offset(0.06,-1.00));
points.add(Offset(0.06,0.67));
points.add(Offset(0.84,0.67));
points.add(Offset(0.84,0.63));
points.add(Offset(0.39,0.63));
points.add(Offset(0.39,-0.42));
points.add(Offset(0.56,-0.42));
points.add(Offset(0.56,0.30));
points.add(Offset(0.37,0.30));
points.add(Offset(0.37,0.32));
points.add(Offset(0.54,0.32));
points.add(Offset(0.54,-0.09));
points.add(Offset(0.70,-0.09));
points.add(Offset(0.70,-0.48));
points.add(Offset(0.94,-0.48));
points.add(Offset(0.94,-0.43));
points.add(Offset(0.67,-0.43));
points.add(Offset(0.67,-0.31));
points.add(Offset(0.08,-0.31));
points.add(Offset(0.08,0.78));
points.add(Offset(-0.40,0.78));
points.add(Offset(-0.40,0.15));
points.add(Offset(0.65,0.15));
points.add(Offset(0.65,0.00));
points.add(Offset(0.36,0.00));
points.add(Offset(0.36,-0.28));
points.add(Offset(0.24,-0.28));
points.add(Offset(0.24,-0.80));
points.add(Offset(-0.76,-0.80));
points.add(Offset(-0.76,-0.31));
points.add(Offset(0.19,-0.31));
points.add(Offset(0.19,0.13));
points.add(Offset(0.96,0.13));
points.add(Offset(0.96,0.65));
points.add(Offset(-0.80,0.65));
points.add(Offset(-0.80,0.06));
points.add(Offset(0.82,0.06));
points.add(Offset(0.82,0.67));
points.add(Offset(0.60,0.67));
points.add(Offset(0.60,0.65));
points.add(Offset(-0.19,0.65));
returnpoints;
}

Flutter 小技巧之霓虹灯文本的「故障」效果的实现

如果这时候把白色 Container 换成文本内容,那么咱们就能够如下图所示的作用,看起来像不像一帧状态下文本的「紊乱」作用?后边咱们只需求每次生成一帧这样的 Path ,就能够完成文本动态「撕裂」的需求。

Flutter 小技巧之霓虹灯文本的「故障」效果的实现

咱们只需求把这个完成做成随机输出,然后每次生成一个 Path 就能够了。

如下代码所示,咱们经过 generatePoint 办法,每次随机生成 60 个点,然后将这些点经过 computePath 转化为 Path,然后继承 CustomClipper 装备到 getClip 办法里,在需求的时候(tear )对 child 按 Path 进行裁剪。

注意这儿的 i % 2 ,为的是让上次的 x 或者 y 能够是同一个方位,在连接上能接连。

classRandomTearingClipperextendsCustomClipper<Path>{
booltear;
​
RandomTearingClipper(this.tear);
​
List<Offset>generatePoint() {
 List<Offset>points=[];
 varx=-1.0;
 vary=-1.0;
 for(vari=0;i<60;i++) {
  if(i%2!=0) {
   x=Random().nextDouble()*(Random().nextBool()?-1:1);
  }else{
   y=Random().nextDouble()*(Random().nextBool()?-1:1);
  }
  points.add(Offset(x,y));
 }
 returnpoints;
}
​
@override
PathgetClip(Sizesize) {
 varpoints=generatePoint();
 varpolygon=Polygon(points);
 if(tear)
  returnpolygon.computePath(rect:Offset.zero&size);
 else
  returnPath()..addRect(Offset.zero&size);
}
​
@override
boolshouldReclip(RandomTearingClipperoldClipper)=>true;
}

接着,咱们只需求设置一个定期器,然后将前面的「霓虹灯文本」和「毛病裁剪作用」装备到 ClipPath 上,如下图所示,咱们就能够看到文本的随机撕裂作用。

timer=Timer.periodic(Duration(milliseconds:400), (timer) {
tearFunction();
});
​
returnClipPath(
child:Center(
 child:Text(
  widget.text,
  style:TextStyle(
   fontSize:48,
   fontWeight:FontWeight.bold,
   foreground:Paint()
     ..style=PaintingStyle.fill
     ..strokeWidth=1
     ..color=Colors.white,
   shadows: [
    Shadow(
     blurRadius:10,
     color:Colors.white,
     offset:Offset(0,0),
     ),
    Shadow(
     blurRadius:20,
     color:Colors.white30,
     offset:Offset(0,0),
     ),
    ],
   ),
  ),
 ),
clipper:RandomTearingClipper(tear),
);

Flutter 小技巧之霓虹灯文本的「故障」效果的实现

此时看起来还不行形象。

变形闪烁

为了达到咱们预期的作用,最终咱们还需求做一些特别处理,比方再完成两个形状、色彩和方位不一样「霓虹灯文本」,为的便是完成「变形和闪烁」的作用替换。

比方如下代码所示,经过 ShaderMask 能够完成一个突变作用的的文本,这是用来在闪烁的时候,供给一个短暂替换和色彩加深的作用。

ShaderMask(
blendMode:BlendMode.srcATop,
shaderCallback: (bounds) {
 returnLinearGradient(
  colors: [Colors.blue,Colors.green,Colors.red],
  stops: [0.0,0.5,1.0],
 ).createShader(bounds);
},
child:

Flutter 小技巧之霓虹灯文本的「故障」效果的实现

相似的咱们还能够完成一个「变形」的文本,在之前的白色「霓虹灯」文本基础上添加「斜体」和「色彩变淡」等处理,用来闪烁的时候供给「变形」的作用。

Flutter 小技巧之霓虹灯文本的「故障」效果的实现

最终咱们再将之前的 ClipPath添加到它们上面,并添加一个 transform 完成文本四周随意移动的作用支撑,如下图所示,此时的作用已经肉眼可见的接近咱们的需求。

transform:
 Matrix4.translationValues(randomPosition(4),randomPosition(4),0),
​
doublerandomPosition(position) {
returnRandom().nextInt(position).toDouble()*
  (Random().nextBool()?-1:1);
}
Flutter 小技巧之霓虹灯文本的「故障」效果的实现
Flutter 小技巧之霓虹灯文本的「故障」效果的实现

最终咱们将这几个文本作用用 Stack 组合起来,然后再在定时器里不停去切换「毛病」和「正常」的文本状态,并且随机挑选展现不同的 「毛病」状态。

timer = Timer.periodic(Duration(milliseconds: 400), (timer) {
  tearFunction();
});
timer2 = Timer.periodic(Duration(milliseconds: 600), (timer) {
  tearFunction();
});
tearFunction() {
  count++;
  tear = count % 2 == 0;
  if (tear == true) {
    setState(() {});
    Future.delayed(Duration(milliseconds: 150), () {
      setState(() {
        tear = false;
      });
    });
  }
}
@override
Widget build(BuildContext context) {
  var status = Random().nextInt(3);
  return Stack(
    children: [
      if (tear && (status == 1)) renderTearText1(RandomTearingClipper(tear)),
      if (!tear || (tear && status != 2))
        renderMainText(RandomTearingClipper(tear)),
      if (tear && status == 2) renderTearText2(RandomTearingClipper(tear)),
    ],
  );
}

终究作用如下图所示,这儿还额定对后边两个文本做了一个 ClipRect 处理,闪烁切换的时候只展现部分内容,这样在「毛病」时的切换不会显得过分僵硬,能够看到简略的 CSS 作用在 Flutter 上的完成成本其实并不低。

Flutter 小技巧之霓虹灯文本的「故障」效果的实现

当然,这儿的完成没考虑功能问题,所以代码也比较糙,不过这儿主要是为了展现了 ClipPathShadow 的运用技巧,相信经过这个例子,能够协助咱们更好地发掘 Flutter 里对于途径制作和阴影的运用场景,这才是本篇的主要目的。

那么本篇小技巧到这儿就结束了,如果你还有什么想说的,欢迎留言评论。

完整代码可见:github.com/CarGuo/gsy_…