Страницы

Поиск по вопросам

среда, 10 октября 2018 г.

Закрасить текст, слово, букву

Доброго времени суток.
WPF Разрабатываю караоке плеер. Основную часть сделал. Получаю текст и музыку всё проигрываю всё хорошо) Встала задача теперь все это дело красиво вывести, как в настоящем караоке. что бы две строчки и текст закрашивать постепенно по тайм коду.
Так вот и вопрос как сделать красиво закрасить текст. Что для этого можно использовать? Подскажите, направьте. Заранее спасибо.


Ответ

Вот вам решение с контурами букв и побуквенной постепенной заливкой.
Идея решения одолжена из комментария @S.Kost и здесь
Для начала определим наш XAML.
Определяем невидимый TextBlock с текстом. Мы стащим из него свойства, и отрендерим текст самостоятельно. Почему нельзя было использовать видимый TextBlock? Дело в том, что он не умеет отрисовывать границу глифов, а мы хотим в начале, чтобы буквы были «пустыми».
Под ним в том же Grid'е кладём Path, который и будет содержать наш текст. У него есть Stroke, задающий границу, установим его цвет в чётный и толщину в пол-пикселя.
Затем, в заполнении кладётся наша градиентная заливка. Кладём 2 контрольных точки, которые будут пробегаться по каждой букве. Слева от первой точки будет чёрная, справа — белая заливка. Между точками — переход.
Затем, анимация. Мы анимируем продвижение контрольных точек вдоль букв. Откуда и докуда происходит анимация, устанавливается в code-behind.

С XAML'ом покончено, теперь code-behind.
Для начала, нам нужна функция, которая вытащит из текста его геометрию, и измерит ширины всех букв. Заодно пропустим пробелы, их анимировать не стоит.
Для измерения геометрии текста воспользуемся удобным классом FormattedText
double extent; // общая ширина текста List boundingBoxes; // прямоугольники с каждым символом
void Create() // https://msdn.microsoft.com/en-us/library/ms745816(v=vs.110).aspx { TextBlock tb = Source; var text = tb.Text; FormattedText formattedText = new FormattedText( text, CultureInfo.GetCultureInfo("en-US"), FlowDirection.LeftToRight, new Typeface( tb.FontFamily, tb.FontStyle, tb.FontWeight, tb.FontStretch), tb.FontSize, Brushes.Black // конкретная кисть нам не важна, мы используем только геометрию );
// стащили геометрию у текста... var geo = formattedText.BuildGeometry(new Point()); // ...и отдали её Path'у Target.Data = geo;
var bb = formattedText.BuildHighlightGeometry(new Point()); extent = bb.Bounds.Width; // общая ширина
boundingBoxes = // побуквенная ширина и позиции Enumerable.Range(0, text.Length) .Where(k => !char.IsWhiteSpace(text[k])) .Select(k => formattedText.BuildHighlightGeometry(new Point(), k, 1) .Bounds) .ToList(); }
Ну и нам нужна функция, запускающая в цикле для каждой буквы анимацию. Это совсем просто:
async void Play() { var storyboard = (Storyboard)Target.Resources["AnimationStoryboard"]; var fromAnimation = (DoubleAnimation)storyboard.Children[0]; var toAnimation = (DoubleAnimation)storyboard.Children[1];
foreach (var b in boundingBoxes) { await Task.Delay(250); // перерыв между буквами fromAnimation.From = b.Left / extent; fromAnimation.To = b.Right / extent; toAnimation.From = b.Left / extent; toAnimation.To = b.Right / extent; storyboard.Begin(); await Task.Delay(250); // дождёмся конца анимации } }
Результат:

Комментариев нет:

Отправить комментарий