На приведённом ниже изображении показана анимация "дрожания" текста, которая напоминает повреждение ночных вывесок. Целью данной статьи является воспроизведение этой анимации с помощью Flutter.
Этот эффект был реализован с использованием CSS на сайте codepen.io. Однако Flutter не имеет такой мощной системы стилей как CSS, поэтому задача заключается в том, чтобы воспроизвести этот эффект в Flutter.
Безусловно, CSS очень мощна, но воспроизведение подобного эффекта в Flutter требует некоторых усилий.
Для реализации эффекта "глюк" в Flutter нам потребуется:
Следуя этому плану, мы создадим аналогичный эффект "глюк" в Flutter.
Эта часть довольно проста. В Flutter стиль текста (TextStyle
) позволяет использовать тени (shadows
). Это позволяет быстро создать эффект "светящегося" текста.
Мы используем два объекта Shadow
, чтобы добиться эффекта "светящихся" букв. Основная идея состоит в использовании свойства blurRadius
для создания некоторого уровня размытия, которое создаёт эффект свечения. Два разных Shadow
обеспечивают различные цветовые глубины и эффекты размытия, что делает текст выглядеть "светящимся".> На следующем изображении показано, как выглядят тени без заполненного цвета текста.
Наконец, как показано ниже, добавление цвета текста через 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),
),
],
),
)
```
Здесь стоит отметить, что аналогичный подход можно применить к изображению, чтобы добиться эффекта "светового пятна". Ниже приведён пример кода, который использует вложенные `Stack`, два `Image` и `BackdropFilter` с `ImageFilter`, чтобы создать эффект размытости, создающий вид светящегося изображения.
```dart
var child = Image.asset(
'static/test_logo.png',
width: 250,
);
return Stack(
children: [
child,
Positioned.fill(
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: blurRadius,
sigmaY: blurRadius,
),
child: Container(color: Colors.transparent),
),
),
child,
],
)
Как показано на следующем рисунке, изображение может получить эффект "светового пятна" благодаря своим цветам. Однако эта часть является лишь расширенной информацией и не имеет отношения к нашему основному требованию.
Эта часть представляет собой ключевой аспект нашего требования. Здесь мы будем использовать ClipPath
и Polygon
. Мы можем случайным образом генерировать путь полигона с помощью Polygon
, а затем использовать ClipPath
для случайного разрезания содержимого текста.
Хотя используется Polygon
, но официальная библиотека Flutter не предоставляет поддержку аналогичной API для полигонов, как это делается в CSS. Но сообщество всегда готово помочь, поэтому мы можем использовать стороннюю библиотеку polygon: ^0.1.0
.
Polygon
— это просто упаковка методовmoveTo
иquadraticBezierTo
объектаPath
через шаги.
Диапазон значений Polygon
в Flutter составляет от -1 до 1, то есть положение определяется относительно размера контейнера. Например, -1 указывает на начальную точку, а 1 — на максимальную ширину или высоту. Как показано ниже, здесь используются три точки, которые вместе образуют треугольник.
List<Offset> generatePoints() {
List<Offset> points = [];
points.add(Offset(-1, -1));
points.add(Offset(-1, 0));
points.add(Offset(0, -1));
return points;
}
Если количество точек увеличивается, то они могут образовать серию нерегулярных форм. В следующем примере случайным образом добавлены 60 точек, и белый Container
на экране выглядит хаотично разрезанным.dart List<Offset> generatePoints() { List<Offset> points = [];
java
точки.add(Offset(-1.00, -0.76));
точки.add(Offset(0.06, -0.76));
точки.add(Offset(0.06, -0.48));
точки.add(Offset(-0.50, -0.48));
точки.add(Offset(-0.50, 0.72));
точки.add(Offset(-0.38, 0.72));
точки.add(Offset(-0.38, -1.00));
точки.add(Offset(0.06, -1.00));
точки.add(Offset(0.06, 0.67));
точки.add(Offset(0.84, 0.67));
точки.add(Offset(0.84, 0.63));
точки.add(Offset(0.39, 0.63));
точки.add(Offset(0.39, -0.42));
точки.add(Offset(0.56, -0.42));
точки.add(Offset(0.56, 0.30));
точки.add(Offset(0.37, 0.30));
точки.add(Offset(0.37, 0.32));
точки.add(Offset(0.54, 0.32));
точки.add(Offset(0.54, -0.09));
точки.add(Offset(0.70, -0.09));
точки.add(Offset(0.70, -0.48));
точки.add(Offset(0.94, -0.48));
точки.add(Offset(0.94, -0.43));
точки.add(Offset(0.67, -0.43));
точки.add(Offset(0.67, -0.31));
точки.add(Offset(0.08, -0.31));
точки.add(Offset(0.08, 0.78));
точки.add(Offset(-0.40, 0.78));
точки.add(Offset(-0.40, 0.15));
точки.add(Offset(0.65, 0.15));
точки.add(Offset(0.65, 0.00));
точки.add(Offset(0.36, 0.00));
точки.add(Offset(0.36, -0.28));
точки.add(Offset(0.24, -0.28));
точки.add(Offset(0.24, -0.80));
точки.add(Offset(-0.76, -0.80));
точки.add(Offset(-0.76, -0.31));
точки.add(Offset(0.19, -0.31));
точки.add(Offset(0.19, 0.13));
точки.add(Offset(0.96, 0.13));
точки.add(Offset(0.96, 0.65));
точки.add(Offset(-0.80, 0.65));
точки.add(Offset(-0.80, 0.06));
точки.add(Offset(0.82, 0.06));
точки.add(Offset(0.82, 0.67));
точки.add(Offset(0.60, 0.67));
точки.add(Offset(0.60, 0.65));
точки.add(Offset(-0.19, 0.65));
return точки;
}

Если в этот момент белый `Container` заменить на текстовое содержимое, то можно получить такой эффект, как показано ниже. Видите ли вы похожесть на случайное перемещение текста? Далее нам достаточно генерировать каждый раз один путь, чтобы реализовать требуемый эффект "разрыв" текста.

> Для того чтобы каждый раз генерировался новый `Path`, нам достаточно сделать эту реализацию случайной. Как показано ниже, мы используем метод `generatePoint`, чтобы случайным образом генерировать 60 точек каждый раз. Затем эти точки преобразуются в путь с помощью метода `computePath`. Этот путь затем передается в метод `getClip` класса `CustomClipper`.
> Обратите внимание на условие `i % 2`, которое позволяет предыдущему значению x или y оставаться тем же местоположением, обеспечивая непрерывность при соединении.
```dart
class RandomTearingClipper extends CustomClipper<Path> {
bool tear;
RandomTearingClipper(this.tear);
List<Offset> generatePoint() {
List<Offset> points = [];
var x = -1.0;
var y = -1.0;
for (var i = 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));
}
return points;
}
@override
Path getClip(Size size) {
var points = generatePoint();
var polygon = Polygon(points);
if (tear)
return polygon.computePath(rect: Offset.zero & size);
else
return Path()..addRect(Offset.zero & size);
}
@override
bool shouldReclip(RandomTearingClipper oldClipper) => true;
}
Затем нам нужно установить таймер, который будет периодически вызывать функцию tearFunction
, а также применять эффект случайной разрывчивости к тексту, используя компонент ClipPath
.
timer = Timer.periodic(Duration(milliseconds: 400), (timer) {
tearFunction();
});
```return ClipPath(
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),
);
```
> На данный момент это выглядит недостаточно реалистично.
# Деформация и мигание
Для достижения желаемого эффекта нам ещё требуется выполнить несколько специальных операций, таких как создание двух фигур с различными цветами и позициями «неоновых текстов», чтобы реализовать эффект «деформации и мигания».
Например, следующий код показывает, как можно использовать `ShaderMask`, чтобы создать текст с градиентной заливкой. Это используется для временной замены и усиления цвета во время мигания.
```dart
ShaderMask(
blendMode: BlendMode.srcATop,
shaderCallback: (bounds) {
return LinearGradient(
colors: [Colors.blue, Colors.green, Colors.red],
stops: [0.0, 0.5, 1.0],
).createShader(bounds);
},
child:
Аналогично мы можем создать текст с эффектом «деформации». На основе белого «неонового» текста добавляем эффекты «курсивного шрифта» и «пятна светлых оттенков», чтобы обеспечить эффект «деформации» при мигании.
Заключительный шаг — добавление ClipPath
к этим элементам и использование transform
для перемещения текста в произвольных направлениях. В результате получается видимый глазу эффект, близкий к нашему требованию.
transform:
Matrix4.translationValues(randomPosition(4), randomPosition(4), 0),
double randomPosition(int position) {
return Random().nextInt(position).toDouble() *
(Random().nextBool() ? -1 : 1);
}
```|  |  |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
Итоговый этап — объединение этих эффектов текста с помощью `Stack`. Мы используем таймер для постоянной смены состояний «поломки» и «нормального режима» текста, а также случайного выбора различных состояний «поломки».
```dart
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;
});
});
}
}
Результат отображается на следующем рисунке:
Конечно, здесь реализация не учитывает вопросы производительности, поэтому код довольно грубый. Однако основная цель состоит в демонстрации использования ClipPath
и Shadow
. Надеюсь, этот пример поможет вам лучше понять применение путь к рисованию и тени в Flutter.
На этом данная небольшая статья завершена. Если у вас есть какие-либо идеи или предложения, оставьте свои комментарии ниже.
Полный код доступен по адресу: https://github.com/CarGuo/gsy_flutter_demo/blob/master/lib/widget/tear_text_demo_page.dart
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )