Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Создавайте приложения WinUI с плавной анимацией, высокой частотой кадров и высокопроизводительными захватами и воспроизведением мультимедиа.
Сделать анимацию гладкой
Ключевым аспектом приложений WinUI является плавное взаимодействие. Это включает в себя сенсорные манипуляции, которые "прилипают к пальцу", гладкие переходы и анимации, а также небольшие движения, которые обеспечивают обратную связь с входными данными. В платформе XAML существует поток, называемый потоком композиции, предназначенным для композиции и анимации визуальных элементов приложения. Так как поток композиции отделен от потока пользовательского интерфейса (потока, выполняющего платформу и код разработчика), приложения могут достичь согласованной частоты кадров и плавной анимации независимо от сложных проходов макета или расширенных вычислений. В этом разделе показано, как использовать поток композиции для обеспечения плавности анимации приложения. Дополнительные сведения об анимациях см. в обзоре анимаций. Дополнительные сведения о повышении скорости реагирования приложения при выполнении интенсивных вычислений см. в статье "Сохранение отклика потока пользовательского интерфейса".
Использование независимых анимаций вместо зависимых анимаций
Независимые анимации можно рассчитать с начала до конца во время создания, потому что изменения свойства, которое анимируется, не влияют на остальные объекты в сцене. Поэтому независимые анимации могут выполняться в потоке композиции вместо потока пользовательского интерфейса. Это гарантирует, что они остаются гладкими, так как поток композиции обновляется по согласованной частоте.
Все эти типы анимаций гарантированно являются независимыми:
Анимация объектов с помощью ключевых кадров
Анимации нулевой длительности
Анимация свойств Canvas.Left и Canvas.Top
Анимации для свойства UIElement.Opacity
Анимация свойств типа Brush при нацеливании на подсвойство SolidColorBrush.Color
Анимация в следующих свойствах UIElement при целевом подпроверлении этих типов возвращаемых значений:
Зависимые анимации влияют на макет, поэтому не может быть вычислен без дополнительных данных из потока UI. Зависимые анимации включают изменения свойств, таких как ширина и высота. По умолчанию зависимые анимации не выполняются и требуют согласия от разработчика приложения. При включении они выполняются плавно, если поток пользовательского интерфейса остается разблокирован, но они начинают заикаться, если платформа или приложение выполняет много других действий в потоке пользовательского интерфейса.
Почти все анимации в платформе XAML независимы по умолчанию, но есть некоторые действия, которые можно предпринять для отключения этой оптимизации. Особенно остерегайтесь этих сценариев:
- Установите свойство EnableDependentAnimation, чтобы запустить зависимую анимацию на потоке пользовательского интерфейса. Преобразуйте эти анимации в независимую версию. Например, анимация ScaleTransform.ScaleX и ScaleTransform.ScaleY вместо ширины и высоты объекта. Не бойтесь масштабировать объекты, такие как изображения и текст. Платформа применяет двулинейное масштабирование только в то время как ScaleTransform анимируется. Изображение или текст будет перерастрировано в конечном размере, чтобы обеспечить его четкость.
- Создание обновлений для каждого кадра, которые эффективно зависят от анимации. Примером этого является применение преобразований в обработчике события CompositionTarget.Rendering .
- Выполнение любой анимации, считающейся независимой, в элементе со свойством CacheMode, равным BitmapCache. Это считается зависимым, так как кэш должен быть повторно растеризован для каждого кадра.
Не анимируйте webView2 или MediaPlayerElement
Веб-содержимое, размещенное в элементе управления WebView2, не отображается напрямую платформой XAML, поэтому для компоновки с остальной сценой требуется дополнительная работа. Эта нагрузка увеличивается при анимации элемента управления по экрану и может привести к проблемам синхронизации, например, если веб-содержимое оказывается несинхронизированным с окружающими объектами XAML. Если вам нужно добавить анимацию к веб-содержимому, анимируйте интерфейс WinUI вокруг, а не саму поверхность WebView2. Более ранние рекомендации могут упомянуть WebViewBrush; в современных приложениях WinUI WebView2 является поддерживаемым элементом управления веб-хостингом и не существует прямой замены WebViewBrush.
Анимация элемента MediaPlayerElement тоже плохая идея. Помимо ущерба производительности, он может вызвать разрывы или другие артефакты в воспроизводимом видеоконтенте.
Используйте бесконечные анимации экономно.
Большинство анимаций выполняются в течение указанного периода времени, но при задании свойства Timeline.Duration значение Forever позволяет анимации выполняться неограниченное время. Мы рекомендуем свести к минимуму использование бесконечных анимаций, так как они постоянно расходуют ресурсы ЦП и могут предотвратить переход ЦП в состояние низкого энергопотребления или простоя, что приводит к более быстрому разряду.
Добавление обработчика для CompositionTarget.Rendering аналогично выполнению бесконечной анимации. Обычно поток пользовательского интерфейса активен только при наличии работы, но добавление обработчика для этого события принуждает его запускаться при каждом кадре. Удалите обработчик, когда нет работы для выполнения, и повторно зарегистрируйте его, когда это потребуется снова.
Использование библиотеки анимации
Пространство имен Microsoft.UI.Xaml.Media.Animation включает в себя библиотеку высокопроизводительных, гладких анимаций, которые имеют внешний вид и чувствуют себя в соответствии с другими анимациями Windows. Соответствующие классы имеют "Theme" в их имени и описаны в обзоре анимаций. Эта библиотека поддерживает множество распространенных сценариев анимации, таких как анимация первого представления приложения и создание переходов состояния и содержимого. Мы рекомендуем использовать эту библиотеку анимации, если это возможно, чтобы повысить производительность и согласованность для приложений WinUI.
Примечание Библиотека анимации не может анимировать все возможные свойства. Сценарии XAML, в которых библиотека анимации не применяется, см. в разделе "Раскадровки анимаций".
Анимировать свойства CompositeTransform3D независимо
Вы можете анимировать каждое свойство CompositeTransform3D независимо, применяйте только необходимые анимации. Примеры и дополнительные сведения см. в разделе UIElement.Transform3D. Дополнительные сведения о анимации преобразований см. в разделах раскадровки анимаций и анимации ключевых кадров и анимаций функций.
Оптимизация ресурсов мультимедиа
Аудио, видео и изображения являются убедительными формами содержимого, которые используют большинство приложений. По мере увеличения частоты захвата мультимедиа и перехода от стандартного определения к высокому определению объема ресурсов, необходимых для хранения, декодировки и воспроизведения этого содержимого, увеличивается. Платформа XAML основана на современной инфраструктуре мультимедиа Windows, поэтому приложения WinUI наследуют многие из этих улучшений автоматически. Ниже перечислены некоторые дополнительные хитрости, которые помогут вам максимально использовать мультимедиа в приложении WinUI.
Выпуск потоков мультимедиа
Файлы мультимедиа являются одними из наиболее распространенных и дорогостоящих ресурсов, которые обычно используются в приложениях. Так как ресурсы файлов мультимедиа могут значительно увеличить объем памяти вашего приложения, нужно помнить о том, что необходимо освободить дескриптор ресурсов мультимедиа, как только приложение завершит работу с ним.
Например, если приложение работает с объектом RandomAccessStream или объектом IInputStream , обязательно вызовите метод закрытия объекта, когда приложение завершит работу с ним, чтобы освободить базовый объект.
Отображение полноэкранного воспроизведения видео по возможности
В приложениях WinUI всегда используйте свойство IsFullWindow в MediaPlayerElement для включения и отключения полной отрисовки окон. Это гарантирует, что при воспроизведении мультимедиа используются оптимизации на уровне системы.
Платформа XAML может оптимизировать отображение видеосодержимого, если это единственный элемент отображения, что приводит к использованию меньшего количества энергии и увеличению частоты кадров. Для наиболее эффективного воспроизведения мультимедиа задайте размер MediaPlayerElement по ширине и высоте экрана и не отображайте другие элементы XAML.
Существуют законные причины наложения элементов XAML на MediaPlayerElement , которые занимают полную ширину и высоту экрана, например закрытые заголовки или моментальные элементы управления транспортом. Не забудьте скрыть эти элементы (задать Visibility="Collapsed"), если они не нужны, чтобы вернуть воспроизведение мультимедиа в наиболее эффективное состояние.
Отключение дисплея и экономия энергии
Чтобы предотвратить деактивацию дисплея, если действие пользователя больше не обнаружено, например, когда приложение воспроизводит видео, можно вызвать DisplayRequest.RequestActive.
Чтобы сохранить питание и время работы батареи, необходимо вызвать DisplayRequest.RequestRelease , чтобы освободить запрос на отображение, как только он больше не требуется.
Ниже приведены некоторые ситуации, когда следует отменить запрос на отображение:
- Воспроизведение видео приостановлено, например с помощью действия пользователя, буферизации или корректировки из-за ограниченной пропускной способности.
- Воспроизведение останавливается. Например, видео закончилось или презентация завершилась.
- Произошла ошибка воспроизведения. Например, проблемы с сетевым подключением или поврежденный файл.
Поместите другие элементы рядом с внедренным видео
Часто приложения предлагают внедренное представление, в котором воспроизводится видео на странице. Теперь вы, очевидно, потеряли полноэкранную оптимизацию, так как MediaPlayerElement не занимает всю страницу, и на ней нарисованы другие объекты XAML. Остерегайтесь случайного входа в этот режим, очерчивая границу вокруг MediaPlayerElement.
Не рисуйте элементы XAML поверх видео, когда оно находится во встроенном режиме. Если вы так поступаете, фреймворк вынужден сделать немного дополнительной работы, чтобы составить сцену. Размещение элементов управления транспортом под внедренным элементом мультимедиа вместо видео является хорошим примером оптимизации для этой ситуации. На этом изображении красная полоса обозначает набор элементов управления транспортировкой (воспроизведение, приостановка, остановка и т. д.).
Не размещайте эти элементы управления поверх медиа, которое не в полноэкранном режиме. Вместо этого поместите элементы управления воспроизведением вне области, в которой воспроизводится медиа. На следующем изображении элементы управления размещены под медиа.
Задержка установки источника для MediaPlayerElement
Медиа движки — это дорогостоящие объекты, и платформа XAML откладывает загрузку DLL и создание больших объектов до последнего момента. MediaPlayerElement принудительно выполняет эту работу после установки источника с помощью свойства Source. Когда это устанавливается, когда пользователь действительно готов к воспроизведению мультимедиа, это максимально задерживает большую часть затрат, связанных с MediaPlayerElement.
Настройка MediaPlayerElement.PosterSource
Настройка MediaPlayerElement.PosterSource позволяет XAML выпускать некоторые ресурсы GPU, которые в противном случае использовались бы. Этот API позволяет приложению использовать как можно меньше памяти.
Улучшение очистки мультимедиа
Очистка всегда является сложной задачей для медиа-платформ, чтобы сделать действительно адаптивным. Обычно люди добиваются этого, изменяя значение ползунка. Вот несколько советов о том, как сделать это как можно эффективнее:
- Обновите значение слайдера с помощью таймера, который опрашивает положение в MediaPlayerElement.MediaPlayer. Убедитесь, что для таймера используется разумная частота обновления. Свойство Position обновляется только каждые 250 миллисекунда во время воспроизведения.
- Размер шага на ползунке должен адаптироваться к длине видео.
- Подпишитесь на события PointerPressed, PointerMoved, PointerReleased на ползунке, чтобы задать для свойства PlaybackRate значение 0, когда пользователь перетаскивает большой палец ползунка.
- В обработчике событий PointerReleased вручную установите положение носителя в положение ползунка, чтобы обеспечить оптимальную привязку отпечатков во время очистки.
Сопоставление разрешения видео с разрешением устройства
Декодирование видео занимает много памяти и циклов GPU, поэтому выберите формат видео близко к разрешению, в котором он будет отображаться. Нет смысла использовать ресурсы для декодирования видео 1080, если оно будет уменьшено до значительно меньшего размера. Многие приложения не имеют одного и того же видео, закодированного в разных разрешениях; но если он доступен, используйте кодировку, близкую к разрешению, в котором он будет отображаться.
Выбор рекомендуемых форматов
Выбор формата мультимедиа может быть конфиденциальным и часто определяется бизнес-решениями. С точки зрения производительности пакета SDK для приложений Windows рекомендуется видео H.264 в качестве основного формата видео и AAC и MP3 в качестве предпочитаемых аудиоформатов. Для воспроизведения локального файла MP4 является предпочтительным контейнером файлов для видеоконтента. Декодирование H.264 ускоряется с помощью последнего графического оборудования. Кроме того, хотя аппаратное ускорение для декодирования VC-1 широко доступно, для большого набора графического оборудования на рынке ускорение ограничено во многих случаях частичным ускорением (или уровнем IDCT), а не полнопотоковой аппаратной разгрузкой (то есть в режиме VLD).
Если у вас есть полный контроль над процессом создания содержимого видео, необходимо выяснить, как обеспечить хороший баланс между эффективностью сжатия и структурой GOP. Относительно меньший размер GOP с изображениями B может повысить производительность в режиме поиска или трюка.
При включении коротких, низкой задержки звуковых эффектов, например в играх, используйте WAV-файлы с несжатыми данными PCM, чтобы сократить затраты на обработку, типичные для сжатых звуковых форматов.
Оптимизация ресурсов изображений
Масштабирование изображений до соответствующего размера
Изображения записываются с очень высоким разрешением, что может привести к приложениям, использующим больше ЦП при декодировании данных изображения и больше памяти после загрузки с диска. Нет смысла декодировать и сохранять изображение с высоким разрешением в памяти только для того, чтобы показать его в уменьшенном виде. Вместо этого создайте версию изображения с точным размером, соответствующим отображению на экране, используя свойства DecodePixelWidth и DecodePixelHeight.
Не делайте этого:
<Image Source="ms-appx:///Assets/highresCar.jpg"
Width="300" Height="200"/> <!-- BAD CODE DO NOT USE.-->
Вместо этого сделайте следующее:
<Image>
<Image.Source>
<BitmapImage UriSource="ms-appx:///Assets/highresCar.jpg"
DecodePixelWidth="300" DecodePixelHeight="200"/>
</Image.Source>
</Image>
Единицы для DecodePixelWidth и DecodePixelHeight по умолчанию являются физическими пикселями. Свойство DecodePixelType можно использовать для изменения этого поведения: задание DecodePixelType в значение Logical приводит к тому, что размер декодирования автоматически учитывает текущий коэффициент масштабирования системы, как и другое содержимое XAML. Таким образом, обычно следует задать DecodePixelType как Logical, если, например, вы хотите, чтобы DecodePixelWidth и DecodePixelHeight соответствовали свойствам Высота и Ширина элемента управления Изображение, в котором будет отображаться изображение. При использовании физических пикселей по умолчанию необходимо самостоятельно учитывать текущий коэффициент масштабирования системы; и вы должны прослушивать уведомления об изменении масштаба, если пользователь изменяет свои настройки отображения.
Если DecodePixelWidth/Height явно указаны больше, чем размеры, в которых изображение будет отображаться на экране, приложение будет излишне использовать дополнительную память — до 4 байт на пиксель, что быстро становится дорогостоящим для больших изображений. Изображение также будет уменьшено с помощью билинейного масштабирования, что может вызвать размытие при больших коэффициентах масштабирования.
Если DecodePixelWidth/DecodePixelHeight установлены явно меньше, чем требуется для отображения изображения на экране, то оно будет масштабировано и может выглядеть пикселированным.
В некоторых случаях, когда оптимальный размер декодирования не может быть определен заранее, следует использовать автоматическое декодирование оптимального размера в XAML, которое постарается как можно лучше декодировать изображение соответствующего размера, если явные параметры DecodePixelWidth или DecodePixelHeight не указаны.
Вы должны задать явный размер декодировки, если вы знаете размер содержимого изображения заранее. Кроме того, в сочетании с набором DecodePixelType следует задать значение "Логический ", если указанный размер декодера соответствует другим размерам элементов XAML. Например, если вы явно задали размер содержимого с помощью Image.Width и Image.Height, вы можете задать для DecodePixelType значение DecodePixelType.Logical, чтобы использовать те же логические размеры пикселей, что и элемент управления Image, а затем явно использовать BitmapImage.DecodePixelWidth и/или BitmapImage.DecodePixelHeight для контроля размера изображения, что может значительно сэкономить память.
Обратите внимание, что Image.Stretch следует учитывать при определении размера декодированного содержимого.
Декодирование оптимального размера
Если вы не задали явный размер декодирования, XAML попытается сохранить память, декодируя изображение до точного размера, которое будет отображаться на экране в соответствии с начальным макетом страницы. Рекомендуется написать приложение таким образом, чтобы использовать эту функцию, когда это возможно. Эта функция будет отключена, если выполнены какие-либо из следующих условий.
- BitmapImage подключается к динамическому дереву XAML после установки содержимого с помощью методов SetSourceAsync или UriSource.
- Изображение декодируется с помощью синхронного декодирования, например SetSource.
- Изображение скрыто с помощью установки Opacity в значение 0 или Видимость в Свернуто на элементе изображения, кисти или любого родительского элемента.
- Элемент управления изображением или кисть использует Stretch c параметром None.
- Изображение используется как NineGrid.
-
CacheMode="BitmapCache"устанавливается в элементе изображения или на любом родительском элементе. - Кисть изображения не прямоугольная (например, при применении к фигуре или тексту).
В приведенных выше сценариях настройка явного размера декодирования — единственный способ добиться экономии памяти.
Перед настройкой источника всегда следует подключить BitmapImage к динамическому дереву. В любой момент, когда элемент изображения или кисть указан в разметке, это будет автоматически происходить. Ниже приведены примеры под заголовком "Примеры динамических деревьев". Всегда следует избегать использования SetSource и вместо этого использовать SetSourceAsync при настройке источника потока. И рекомендуется избегать скрытия содержимого изображения (или с нулевой непрозрачностью, или с закрытой видимостью) при ожидании вызова события ImageOpened. Это решение на ваше усмотрение: вы не сможете воспользоваться автоматическим декодированием правильного размера, если это уже выполнено. Если приложение должно изначально скрыть содержимое изображения, то при возможности он также должен задать размер декодировки явным образом.
Примеры динамических деревьев
Пример 1 (хорошо) — универсальный идентификатор ресурса (URI), указанный в разметке.
<Image x:Name="myImage" UriSource="Assets/cool-image.png"/>
Пример разметки 2 — URI, указанный в программной части кода.
<Image x:Name="myImage"/>
Пример 2 кода позади (хорошо) — подключение BitmapImage к дереву перед настройкой UriSource.
var bitmapImage = new BitmapImage();
myImage.Source = bitmapImage;
bitmapImage.UriSource = new Uri("ms-appx:///Assets/cool-image.png", UriKind.RelativeOrAbsolute);
Пример 2 кода позади (плохо) — установка UriSource BitmapImage перед подключением к дереву.
var bitmapImage = new BitmapImage();
bitmapImage.UriSource = new Uri("ms-appx:///Assets/cool-image.png", UriKind.RelativeOrAbsolute);
myImage.Source = bitmapImage;
Оптимизация кэширования
Оптимизация кэширования действует для изображений, использующих UriSource для загрузки содержимого из пакета приложения или из Интернета. Универсальный код ресурса (URI) используется для уникальной идентификации базового содержимого, а платформа XAML не будет загружать или декодировать содержимое несколько раз. Вместо этого он будет использовать кэшированные программные или аппаратные ресурсы для отображения содержимого несколько раз.
Исключение этой оптимизации заключается в том, что изображение отображается несколько раз в разных разрешениях (которые можно задать явным образом или с помощью автоматического декодирования правого размера). Каждая запись кэша также сохраняет разрешение изображения, и если XAML не может найти изображение с исходным универсальным кодом ресурса (URI), соответствующий требуемому разрешению, то он декодирует новую версию с таким размером. Однако он снова не скачивает данные закодированного изображения.
Следовательно, при загрузке образов из пакета приложения следует использовать UriSource и избегать использования потока файлов и SetSourceAsync , если это не требуется.
Изображения в виртуализированных панелях (ListView, например)
Если изображение удаляется из дерева( так как приложение явно удалило его, или потому что оно находится на современной виртуализированной панели и было неявно удалено при прокрутке вне представления), XAML оптимизирует использование памяти путем освобождения аппаратных ресурсов для образа, так как они больше не требуются. Память не освобождается немедленно, а освобождается во время обновления кадра, которое происходит через одну секунду после того, как элемент изображения больше не находится в дереве.
Следовательно, рекомендуется использовать современные виртуализированные панели для размещения списков изображений.
Растровые изображения, обработанные программным обеспечением
Если изображение используется для не прямоугольной кисти или для NineGrid, изображение будет использовать путь растеризации программного обеспечения, который не будет масштабировать изображения вообще. Кроме того, он должен хранить копию образа как в программной, так и в аппаратной памяти. Например, если изображение используется в качестве кисти для эллипса, то потенциально большое полное изображение будет храниться дважды. При использовании NineGrid или непрямоугольной кисти приложение должно предварительно масштабировать изображения до примерного размера, в котором они будут отображаться.
Загрузка изображений в фоновом потоке
XAML имеет внутреннюю оптимизацию, которая позволяет декодировать содержимое изображения асинхронно на поверхность в аппаратной памяти без необходимости промежуточной поверхности в программной памяти. Это снижает пиковое использование памяти и задержку отрисовки. Эта функция будет отключена, если выполнены какие-либо из следующих условий.
- Изображение используется как NineGrid.
-
CacheMode="BitmapCache"устанавливается на элементе изображения или на любом родительском элементе. - Кисть изображения не прямоугольная (например, при применении к фигуре или тексту).
SoftwareBitmapSource
Класс SoftwareBitmapSource обменивается несжатными изображениями между разными пространствами имен WinRT, такими как BitmapDecoder, API камеры и XAML. Этот класс устраняет необходимость в дополнительной копии, которая обычно требуется при использовании WriteableBitmap, что помогает уменьшить пиковое использование памяти и задержку от источника до экрана.
SoftwareBitmap, предоставляющий исходные сведения, также можно настроить на использование пользовательского IWICBitmap для обеспечения перезагрузимого резервного хранилища, позволяющего приложению повторно сопоставить память по своему усмотрению. Это расширенный вариант использования C++ .
Приложение должно использовать SoftwareBitmap и SoftwareBitmapSource для взаимодействия с другими API WinRT, которые создают и используют изображения. Приложение должно использовать SoftwareBitmapSource при загрузке несжатых данных изображения вместо использования WriteableBitmap.
Использование GetThumbnailAsync для эскизов
Одним из вариантов использования масштабирования изображений является создание миниатюр. Хотя вы можете использовать DecodePixelWidth и DecodePixelHeight для предоставления небольших версий изображений, Windows предоставляет еще более эффективные API для получения эскизов. GetThumbnailAsync предоставляет эскизы для изображений, которые уже закэшированы в файловой системе. Это обеспечивает еще большую производительность, чем API XAML, так как образ не должен быть открыт или декодирован.
В приложении пакета SDK для приложений Windows инициализируйте средство выбора с помощью дескриптора окна приложения, например, сохраняя главное окно как App.MainWindow.
FileOpenPicker picker = new FileOpenPicker();
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd);
picker.FileTypeFilter.Add(".bmp");
picker.FileTypeFilter.Add(".jpg");
picker.FileTypeFilter.Add(".jpeg");
picker.FileTypeFilter.Add(".png");
picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
StorageFile file = await picker.PickSingleFileAsync();
StorageItemThumbnail fileThumbnail = await file.GetThumbnailAsync(ThumbnailMode.SingleItem, 64);
BitmapImage bmp = new BitmapImage();
await bmp.SetSourceAsync(fileThumbnail);
Image img = new Image();
img.Source = bmp;
Декодирование изображений один раз
Чтобы предотвратить декодирование изображений несколько раз, назначьте свойство Image.Source из URI, а не с помощью потоков памяти. Платформа XAML может связать один и тот же Uri в нескольких местах с одним декодированным изображением, но не может сделать то же самое для нескольких потоков памяти, содержащих одни и те же данные, и создает разное декодированное изображение для каждого потока памяти.
Windows developer