跑马灯被运用在很多领域, 例如商场的电子条幅、大楼的宣传广告位、地铁的广告位.不过毫无疑问的是它们都是为了解决文字过长的问题而应景给出的一种解决方案.

今天我们来说一下flutter 怎么通过绘制来实现跑马灯效果!
等不及想看源码的兄弟们,请走这里:github.com/weniner/flutter_demo

分析实现

我们都知道跑马灯是不停的往前跑. 在给定的区域里,如果是最小的绘制区域,我们也至少绘制出它左边刚出头,右边刚出尾的所有子项.同时我们也需要一个东西来驱动它去前行.
1669277501146.jpg

同时,文字也会有很多情况.
1669277689122.jpg
它可能很短,也可能和给定的的区域相等,也可能远超我们给定的区域.
在这种情况下,我们应该首先获取文字的宽度.然后确定每秒它会移动多少.并且还需要一个驱动器来驱动它去做这些变化.

测量文字

如果大家写过Flutter, 一定用过Text Widget. 既然Text Widget 可以根据文字大小来展示,说明Flutter中是有一个api可以测量文字长宽的.这里直接给出答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 我们最终绘制的文字
final String kText = 'Flutter 文字特效之跑马灯';
{
// 需要被测量的文字 Widget
TextSpan measureText =
TextSpan(text: kText, style: const TextStyle(color: Colors.black));
TextPainter _painter = TextPainter(
text: measureText,
textDirection: TextDirection.ltr,
// 这里定义最大为1行
maxLines: 1,
// 这里给定文字绘制的约束最小为0.0, 最大不限制.从而获取真实的文字宽高
)..layout(minWidth: 0.0, maxWidth: double.infinity);
}

其中,TextPainter便包含了我们需要的文字宽高.我们创建一个对象去保存这个信息,方便
后面去绘制.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 这里继承里ChangeNotifier, 可以让我们以最小的代价刷新界面
class _TextInfo extends ChangeNotifier {
// 绘制时最前面的文字偏移
double head = 0.0;

double width;

double margin;

_TextInfo(this.width, this.margin);

// speed是我们定义的偏移速度,
// 这里与我们的偏移量息息相关
void update(double speed) {
// 首先先让偏移量自减
head -= speed;
// 如果超过了文字大小和间距的总长度,说明当前的文字已经超出了屏幕.
// 我们应该用紧接着下一条数据的偏移量赋值.
if (head < -width - margin) {
head += width + margin;
}
notifyListeners();
}
}

动画为媒

文字测量完后, 我们还需要一个可以驱动文字移动的驱动器. 这里你可以通过ticker自己实现一个驱动. 我们为了简单化考虑, 没错~ 我们又要请出我们的好兄弟AnimationController重出江湖了
image

实际上这里运用不了太多AnimationController的参数,我们唯一需要在乎的就是Duration, 也就是时间越短.我们将跑的越快.时间越长,跑的越慢.可以说是反比关系.

1
2
3
4
{
AnimationController _scrollController =
AnimationController(vsync: this, duration: const Duration(seconds:1));
}

最终绘制

万事俱备,只欠东风. 像一个方程, 我们所有的未知数都有了解. 只剩下最后求y了.
这里我们通过CustomPainter来绘制文字. 我们之前通过创建自己的类保存了文字信息,同时让它继承了ChangeNotifier就是为了今朝.

1
2
3
4
_RevolvingLanternPainter(
this.painter,
this.textInfo,
) : super(repaint: textInfo);

我们将CustomPainter的repaint参数赋值成我们自己定义的类. 每次我们update() 都会通知界面来刷新,从而不会影响别的界面, 只影响当前的绘制画布.
然后, 我们就可以去真正的绘制啦~ 这里我们唯一需要做的,就是给一个值x记录下所有文字的偏移量. 如果偏移量大于当前的界面. 说明它不在界面上, 也就不用绘制啦!

1
2
3
4
5
6
7
8
9
10

@override
void paint(Canvas canvas, Size size) {
double x = textInfo.head;
while (x < size.width) {
painter.paint(canvas, Offset(x, 0.0));
x += textInfo.width + textInfo.margin;
}
}

通过这种方法, 我们实现了跑马灯文字的绘制~~
gif

源码

github.com/weniner/flutter_demo

结语

这里是WeninerIo, 希望这次的跑马灯教程可以给你带来一丝丝感悟. 如果你想看到更有意思的文字效果或者其他动画效果, 不妨评论区留言 + 关注.期待下一次更好的相遇.