Доброго времени суток.
WPF Разрабатываю караоке плеер. Основную часть сделал. Получаю текст и музыку всё проигрываю всё хорошо) Встала задача теперь все это дело красиво вывести, как в настоящем караоке. что бы две строчки и текст закрашивать постепенно по тайм коду.
Так вот и вопрос как сделать красиво закрасить текст. Что для этого можно использовать?
Подскажите, направьте. Заранее спасибо.
Ответ
Вот вам решение с контурами букв и побуквенной постепенной заливкой.
Идея решения одолжена из комментария @S.Kost и здесь
Для начала определим наш XAML.
Определяем невидимый TextBlock с текстом. Мы стащим из него свойства, и отрендерим текст самостоятельно. Почему нельзя было использовать видимый TextBlock? Дело в том, что он не умеет отрисовывать границу глифов, а мы хотим в начале, чтобы буквы были «пустыми».
Под ним в том же Grid'е кладём Path, который и будет содержать наш текст. У него есть Stroke, задающий границу, установим его цвет в чётный и толщину в пол-пикселя.
Затем, в заполнении кладётся наша градиентная заливка. Кладём 2 контрольных точки, которые будут пробегаться по каждой букве. Слева от первой точки будет чёрная, справа — белая заливка. Между точками — переход.
Затем, анимация. Мы анимируем продвижение контрольных точек вдоль букв. Откуда и докуда происходит анимация, устанавливается в code-behind.
С XAML'ом покончено, теперь code-behind.
Для начала, нам нужна функция, которая вытащит из текста его геометрию, и измерит ширины всех букв. Заодно пропустим пробелы, их анимировать не стоит.
Для измерения геометрии текста воспользуемся удобным классом FormattedText
double extent; // общая ширина текста
List
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); // дождёмся конца анимации
}
}
Результат:
Комментариев нет:
Отправить комментарий