Dela via


Optimera animeringar, media och bilder

Skapa WinUI-appar med smidiga animeringar, hög bildfrekvens och mediaupptagning och uppspelning med höga prestanda.

Gör animeringar smidiga

En viktig aspekt av WinUI-appar är smidiga interaktioner. Detta inkluderar pekmanipuleringar som "håller sig till fingret", smidiga övergångar och animeringar samt små rörelser som ger feedback om indata. I XAML-ramverket finns en tråd som kallas kompositionstråden som är dedikerad till komposition och animering av en apps visuella element. Eftersom kompositionstråden är separat från användargränssnittstråden (tråden som kör ramverk och utvecklarkod) kan appar uppnå en konsekvent bildfrekvens och smidiga animeringar oavsett komplicerade layoutpass eller utökade beräkningar. Det här avsnittet visar hur du använder kompositionstråden för att hålla en apps animationer mjuka och smidiga. Mer information om animeringar finns i Översikt över animeringar. Mer information om hur du ökar svarstiden för en app när du utför intensiva beräkningar finns i Håll användargränssnittstråden dynamisk.

Använda oberoende i stället för beroende animeringar

Oberoende animeringar kan beräknas från början till slut vid tidpunkten för skapandet eftersom ändringar i egenskapen som animerats inte påverkar resten av objekten i en scen. Oberoende animeringar kan därför köras på kompositionstråden i stället för användargränssnittstråden. Detta garanterar att de förblir smidiga eftersom kompositionstråden uppdateras med en konsekvent takt.

Alla dessa typer av animeringar är garanterat oberoende:

Beroende animeringar påverkar layouten, som därför inte kan beräknas utan extra indata från användargränssnittstråden. Beroende animeringar omfattar ändringar av egenskaper som Bredd och Höjd. Som standard körs inte beroende animeringar och kräver en anmälning från apputvecklaren. När de är aktiverade körs de smidigt om användargränssnittstråden förblir avblockerad, men de börjar stamma om ramverket eller appen utför mycket annat arbete i användargränssnittstråden.

Nästan alla animeringar i XAML-ramverket är oberoende som standard, men det finns vissa åtgärder som du kan vidta för att inaktivera den här optimeringen. Se särskilt upp för dessa scenarier:

  • Ange egenskapen EnableDependentAnimation så att en beroende animering kan köras på användargränssnittstråden. Konvertera dessa animeringar till en oberoende version. Till exempel animera ScaleTransform.ScaleX och ScaleTransform.ScaleY i stället för ett objekts bredd och höjd . Var inte rädd för att skala objekt som bilder och text. Ramverket tillämpar endast bilinearskalning medan ScaleTransform animeras. Bilden/texten kommer att omrasteriseras i den slutliga storleken för att säkerställa att det alltid är tydligt.
  • Göra uppdateringar per bildruta, som i praktiken är beroende animeringar. Ett exempel på detta är att tillämpa transformeringar i hanteraren för händelsen CompositionTarget.Rendering .
  • Köra animeringar som anses vara oberoende i ett element med egenskapen CacheMode inställd på BitmapCache. Detta anses vara beroende eftersom cacheminnet måste rastreras på nytt för varje bildruta.

Animera inte en WebView2 eller MediaPlayerElement

Webbinnehåll som finns i en WebView2-kontroll återges inte direkt av XAML-ramverket, så att skapa det med resten av scenen kräver extra arbete. Den här kostnaden ökar när du animerar kontrollen runt skärmen och kan introducera synkroniseringsproblem, till exempel om webbinnehållet verkar gå ur synkronisering med omgivande XAML. Om du behöver rörelse runt webbinnehåll animerar du omgivande WinUI-krom i stället för själva WebView2-ytan. Tidigare riktlinjer kan nämna WebViewBrush; i moderna WinUI-appar är WebView2 den webbvärdskontroll som stöds och det finns ingen direkt ersättning för WebViewBrush.

Att animera ett MediaPlayerElement är en liknande dålig idé. Utöver prestandaförfånget kan det orsaka rivningar eller andra artefakter i videoinnehållet som spelas upp.

Använd oändliga animeringar sparsamt

De flesta animeringar körs under en angiven tid, men om du anger egenskapen Timeline.Duration till Forever kan en animering köras på obestämd tid. Vi rekommenderar att du minimerar användningen av oändliga animeringar eftersom de kontinuerligt använder CPU-resurser och kan förhindra att processorn hamnar i ett tillstånd med låg ström eller inaktivitet, vilket gör att den får slut på ström snabbare.

Att lägga till en hanterare för CompositionTarget.Rendering liknar att köra en oändlig animering. Normalt är användargränssnittstråden endast aktiv när det finns arbete att göra, men att lägga till en hanterare för den här händelsen tvingar den att köra varje bildruta. Ta bort hanteraren när det inte finns något arbete att göra och registrera den igen när den behövs igen.

Använda animeringsbiblioteket

Namnområdet Microsoft.UI.Xaml.Media.Animation innehåller ett bibliotek med högpresterande, smidiga animeringar som ser ut och känns konsekventa med andra Windows-animeringar. De relevanta klasserna har "Tema" i sitt namn och beskrivs i Översikt över animeringar. Det här biblioteket stöder många vanliga animeringsscenarier, till exempel att animera den första vyn av appen och skapa tillstånds- och innehållsövergångar. Vi rekommenderar att du använder det här animeringsbiblioteket när det är möjligt för att öka prestanda och konsekvens för WinUI-appar.

Observera Animeringsbiblioteket kan inte animera alla möjliga egenskaper. Information om XAML-scenarier där animeringsbiblioteket inte gäller finns i Storyboarded-animeringar.

Animera CompositeTransform3D-egenskaper oberoende av varandra

Du kan animera varje egenskap för en CompositeTransform3D separat, så använd bara de animeringar du behöver. Exempel och mer information finns i UIElement.Transform3D. Mer information om hur du animerar transformationer finns i Storyboard-animeringar och Funktionsanimeringar med nyckelramar och avmattning.

Optimera medieresurser

Ljud, video och bilder är övertygande former av innehåll som de flesta appar använder. I takt med att medieinsamlingen ökar och innehållet flyttas från standarddefinition till hög definition ökar mängden resurser som behövs för att lagra, avkoda och spela upp det här innehållet. XAML-ramverket bygger på modern Windows-medieinfrastruktur, så WinUI-appar ärver många av dessa förbättringar automatiskt. Här följer några ytterligare knep som hjälper dig att få ut mesta möjliga av media i din WinUI-app.

Frigöra medieströmmar

Mediefiler är några av de vanligaste och dyraste resurser som appar vanligtvis använder. Eftersom mediefilresurser kan öka storleken på appens minnesfotavtryck avsevärt måste du komma ihåg att släppa handtaget till media så snart appen är klar med den.

Om din app till exempel arbetar med ett RandomAccessStream - eller IInputStream-objekt ska du anropa stängningsmetoden för objektet när appen har använt det för att frigöra det underliggande objektet.

Visa videouppspelning i helskärmsläge när det är möjligt

I WinUI-appar använder du alltid egenskapen IsFullWindowMediaPlayerElement för att aktivera och inaktivera fullständig fönsteråtergivning. Detta säkerställer att optimeringar på systemnivå används under medieuppspelning.

XAML-ramverket kan optimera visning av videoinnehåll när det är det enda som återges, vilket resulterar i en upplevelse som använder mindre ström och ger högre bildfrekvens. För den mest effektiva medieuppspelningen anger du storleken på ett MediaPlayerElement till skärmens bredd och höjd och visar inte andra XAML-element.

Det finns legitima skäl att lägga över XAML-element på ett MediaPlayerElement som tar upp hela skärmens bredd och höjd, till exempel undertexter eller momentära transportkontroller. Se till att dölja dessa element (ange Visibility="Collapsed") när de inte behövs för att återställa medieuppspelningen till det mest effektiva tillståndet.

Visa inaktivering och sparande av ström

Om du vill förhindra att visningen inaktiveras när användaråtgärden inte längre identifieras, till exempel när en app spelar upp video, kan du anropa DisplayRequest.RequestActive.

För att spara ström och batteritid bör du anropa DisplayRequest.RequestRelease för att släppa visningsbegäran så snart den inte längre krävs.

Här är några situationer när du bör släppa visningsbegäran:

  • Videouppspelningen pausas, till exempel av användaråtgärder, buffring eller justering på grund av begränsad bandbredd.
  • Uppspelningen stoppas. Videon är till exempel klar med uppspelningen eller så är presentationen över.
  • Ett uppspelningsfel har uppstått. Till exempel problem med nätverksanslutningen eller en skadad fil.

Placera andra element på sidan av inbäddad video

Ofta erbjuder appar en inbäddad vy där video spelas upp på en sida. Nu har du uppenbarligen förlorat fullskärmsoptimeringen eftersom MediaPlayerElement inte är storleken på sidan och det finns andra XAML-objekt ritade. Akta dig för att oavsiktligt komma in i det här läget genom att rita en kantlinje runt ett MediaPlayerElement.

Rita inte XAML-element ovanpå video när de är i inbäddat läge. Om du gör det tvingas ramverket att göra lite extra arbete för att skapa scenen. Att placera transportkontroller under ett inbäddat medieelement i stället för ovanpå videon är ett bra exempel på optimering för den här situationen. I den här bilden anger det röda fältet en uppsättning transportkontroller (spela upp, pausa, stoppa och så vidare).

MediaPlayerElement med överläggselement

Placera inte dessa kontroller ovanpå media som inte är helskärmsläge. Transportkontrollerna bör i stället placeras någonstans utanför det område där mediet återges. I nästa bild placeras kontrollerna under mediet.

MediaPlayerElement med närliggande element

Fördröjning vid inställning av källan för ett MediaPlayerElement

Mediemotorer är dyra objekt och XAML-ramverket fördröjer inläsningen av DLL:er och skapar stora objekt så länge som möjligt. MediaPlayerElement tvingas utföra detta arbete när källan har angetts via egenskapen Källa. Om du anger detta när användaren verkligen är redo att spela upp media fördröjs merparten av kostnaden som är kopplad till MediaPlayerElement så länge som möjligt.

Ange MediaPlayerElement.PosterSource

Om du anger MediaPlayerElement.PosterSource kan XAML frigöra vissa GPU-resurser som annars skulle ha använts. Med det här API:et kan en app använda så lite minne som möjligt.

Förbättra medierensning

Rensning är alltid en tuff uppgift för medieplattformar att göra riktigt responsiva. Vanligtvis gör människor detta genom att ändra värdet för ett skjutreglage. Här är några tips på hur du gör detta så effektivt som möjligt:

Matcha videoupplösning med enhetsupplösning

Avkodningsvideon tar mycket minne och GPU-cykler, så välj ett videoformat nära den upplösning som den ska visas på. Det är ingen idé att använda resurserna för att avkoda 1080-video om den ska skalas ned till en mycket mindre storlek. Många appar har inte samma videokodad med olika upplösningar. men om den är tillgänglig använder du en kodning som ligger nära den upplösning som den ska visas på.

Val av medieformat kan vara ett känsligt ämne och drivs ofta av affärsbeslut. Ur ett prestandaperspektiv för Windows App SDK rekommenderar vi H.264-video som primärt videoformat och AAC och MP3 som föredragna ljudformat. För lokal filuppspelning är MP4 den föredragna filcontainern för videoinnehåll. H.264-avkodning accelereras genom den senaste grafikmaskinvaran. Även om maskinvaruacceleration för VC-1-avkodning är allmänt tillgänglig, för en stor uppsättning grafikmaskinvara på marknaden, begränsas accelerationen i många fall till en partiell accelerationsnivå (eller IDCT-nivå), snarare än en fullströms maskinvarubelastning (det vill: VLD-läge).

Om du har fullständig kontroll över videoinnehållsgenereringsprocessen måste du ta reda på hur du kan hålla en bra balans mellan komprimeringseffektivitet och GOP-struktur. Relativt mindre GOP-storlek med B-bilder kan öka prestandan i sök- eller tricklägen.

När du inkluderar korta ljudeffekter med låg latens, till exempel i spel, använder du WAV-filer med okomprimerade PCM-data för att minska bearbetningskostnaderna som är typiska för komprimerade ljudformat.

Optimera avbildningsresurser

Skala bilder till lämplig storlek

Bilder fångas med mycket höga upplösningar, vilket kan leda till att appar använder mer CPU när avkodning av avbildningsdata och mer minne när de har lästs in från disken. Men det finns ingen mening med att avkoda och spara en högupplöst bild i minnet bara för att visa den mindre än dess ursprungliga storlek. Skapa i stället en version av bilden med exakt den storlek som den kommer att ritas på skärmen med egenskaperna DecodePixelWidth och DecodePixelHeight .

Gör inte så här:

<Image Source="ms-appx:///Assets/highresCar.jpg"
       Width="300" Height="200"/>    <!-- BAD CODE DO NOT USE.-->

Gör i stället så här:

<Image>
    <Image.Source>
    <BitmapImage UriSource="ms-appx:///Assets/highresCar.jpg"
                 DecodePixelWidth="300" DecodePixelHeight="200"/>
    </Image.Source>
</Image>

Enheterna för DecodePixelWidth och DecodePixelHeight är som standard fysiska pixlar. Egenskapen DecodePixelType kan användas för att ändra det här beteendet: ange DecodePixelType till Logiska resultat i avkodningsstorleken som automatiskt motsvarar systemets aktuella skalningsfaktor, ungefär som annat XAML-innehåll. Det är därför allmänt lämpligt att ange DecodePixelType till Logisk om du till exempel vill att DecodePixelWidth och DecodePixelHeight ska matcha egenskaperna Höjd och Bredd för bildkontrollen som bilden ska visas i. Med standardbeteendet att använda fysiska pixlar måste du själv ta hänsyn till systemets aktuella skalningsfaktor. och du bör lyssna efter meddelanden om skalningsändringar om användaren ändrar sina visningsinställningar.

Om DecodePixelWidth/Height uttryckligen anges som större än bilden visas på skärmen kommer appen i onödan att använda extra minne – upp till 4 byte per pixel – vilket snabbt blir dyrt för stora bilder. Bilden skalas också ned med bilinearskalning, vilket kan göra att den blir suddig för storskaliga faktorer.

Om DecodePixelWidth/DecodePixelHeight uttryckligen anges som mindre än bilden visas på skärmen skalas den upp och kan visas som pixelerad.

I vissa fall där en lämplig dekodningsstorlek inte kan fastställas i förväg bör du använda dig av XAML:s automatiska dekodning i rätt storlek, vilket gör sitt bästa för att avkoda bilden med lämplig storlek om en explicit DecodePixelWidth/DecodePixelHeight inte anges.

Du bör ange en explicit avkodningsstorlek om du vet storleken på bildinnehållet i förväg. Du bör också tillsammans ange DecodePixelType till Logisk om den angivna avkodningsstorleken är relativ till andra XAML-elementstorlekar. Om du till exempel uttryckligen anger innehållsstorleken med Image.Width och Image.Height kan du ange DecodePixelType till DecodePixelType.Logical för att använda samma logiska pixeldimensioner som en bildkontroll och sedan uttryckligen använda BitmapImage.DecodePixelWidth och/eller BitmapImage.DecodePixelHeight för att kontrollera bildens storlek för att uppnå potentiellt stora minnesbesparingar.

Observera att Image.Stretch bör beaktas när du fastställer storleken på det avkodade innehållet.

Avkodning i rätt storlek

Om du inte anger en explicit avkodningsstorlek gör XAML ett bästa försök att spara minne genom att avkoda en bild till den exakta storlek som visas på skärmen enligt den ursprungliga layouten för den innehållande sidan. Du rekommenderas att skriva ditt program på ett sådant sätt att du kan använda den här funktionen när det är möjligt. Den här funktionen inaktiveras om något av följande villkor uppfylls.

  • BitmapImage är ansluten till XAML-liveträdet när innehållet har angetts med SetSourceAsync eller UriSource.
  • Bilden avkodas med synkron avkodning, till exempel SetSource.
  • Bilden döljs via inställningen Opacitet till 0 eller Synlighet till Komprimerad på värdbildelementet eller penseln eller något överordnat element.
  • Bildkontrollen eller penseln använder Stretch ofNone.
  • Bilden används som en NineGrid.
  • CacheMode="BitmapCache" anges på bildelementet eller på ett överordnat element.
  • Bildpenseln är inte rektangulär (till exempel när den tillämpas på en form eller text).

I ovanstående scenarier är det enda sättet att uppnå minnesbesparingar att ange en explicit avkodningsstorlek.

Du bör alltid koppla en BitmapImage till liveträdet innan du anger källan. Varje gång ett bildelement eller en pensel anges i markup blir detta automatiskt fallet. Exempel finns nedan under rubriken "Exempel på levande träd". Du bör alltid undvika att använda SetSource och i stället använda SetSourceAsync när du ställer in en dataströmkälla. Och det är en bra idé att undvika att dölja bildinnehåll (antingen utan opacitet eller med dold synlighet) i väntan på att ImageOpened-händelsen ska höjas . Att göra detta är en bedömningsfråga: du kommer inte att dra nytta av automatisk avkodning med optimal storlek om det görs. Om din app måste dölja bildinnehållet från början bör den också uttryckligen ange avkodningsstorleken om möjligt.

Exempel på liveträd

Exempel 1 (bra)– URI (Uniform Resource Identifier) angivet i markering.

<Image x:Name="myImage" UriSource="Assets/cool-image.png"/>

Exempel 2-markering – URI som anges i code-behind.

<Image x:Name="myImage"/>

Exempel 2 kod bakom (bra)– ansluta BitmapImage till trädet innan du ställer in dess UriSource.

var bitmapImage = new BitmapImage();
myImage.Source = bitmapImage;
bitmapImage.UriSource = new Uri("ms-appx:///Assets/cool-image.png", UriKind.RelativeOrAbsolute);

Exempel 2 kod bakom (felaktig)– ange BitmapImages UriSource innan du ansluter den till trädet.

var bitmapImage = new BitmapImage();
bitmapImage.UriSource = new Uri("ms-appx:///Assets/cool-image.png", UriKind.RelativeOrAbsolute);
myImage.Source = bitmapImage;

Cachelagringsoptimeringar

Cachelagringsoptimeringar gäller för bilder som använder UriSource för att läsa in innehåll från ett apppaket eller från webben. URI:n används för att unikt identifiera det underliggande innehållet och internt laddar XAML-ramverket inte ned eller avkodar innehållet flera gånger. I stället använder den cachelagrade programvaru- eller maskinvaruresurser för att visa innehållet flera gånger.

Undantaget för den här optimeringen är om bilden visas flera gånger med olika upplösningar (som kan anges explicit eller genom automatisk avkodning i rätt storlek). Varje cachepost lagrar också bildens upplösning, och om XAML inte kan hitta en bild med en käll-URI som matchar den upplösning som krävs avkodas en ny version med den storleken. Den laddar dock inte ned kodade bilddata igen.

Därför bör du använda UriSource när du läser in bilder från ett apppaket och undvika att använda en filström och SetSourceAsync när det inte behövs.

Bilder i virtualiserade paneler (ListView, till exempel)

Om en bild tas bort från trädet – eftersom appen uttryckligen tog bort den, eller för att den finns i en modern virtualiserad panel och implicit togs bort när den rullades ur vyn – optimerar XAML minnesanvändningen genom att frigöra maskinvaruresurserna för avbildningen eftersom de inte längre krävs. Minnet frigörs inte omedelbart, utan frigörs i stället under skärmbildsuppdateringen som inträffar efter att bildelementet har varit borttaget från trädet i en sekund.

Därför bör du sträva efter att använda moderna virtualiserade paneler som värd för listor över bildinnehåll.

Programvaru rastrerade avbildningar

När en bild används för en icke-rektangulär pensel eller för ett NineGrid använder bilden en programvarurastreringsväg, som inte skalar bilder överhuvudtaget. Dessutom måste den lagra en kopia av avbildningen i både programvaru- och maskinvaruminnet. Om en bild till exempel används som pensel för en ellips lagras den potentiellt stora fullständiga avbildningen två gånger internt. När du använder NineGrid eller en icke-rektangulär borste bör appen förskala sina bilder till ungefär den storlek som de återges till.

Bildinläsning av bakgrundstråd

XAML har en intern optimering som gör att den kan avkoda innehållet i en bild asynkront till en yta i maskinvaruminnet utan att kräva en mellanliggande yta i programvaruminnet. Detta minskar den högsta minnesanvändningen och svarstiden för återgivning. Den här funktionen inaktiveras om något av följande villkor uppfylls.

  • Bilden används som en NineGrid.
  • CacheMode="BitmapCache" anges på bildelementet eller på ett överordnat element.
  • Bildpenseln är inte rektangulär (till exempel när den tillämpas på en form eller text).

SoftwareBitmapSource

Klassen SoftwareBitmapSource utbyter samverkande okomprimerade bilder mellan olika WinRT-namnområden som BitmapDecoder, kamera-API:er och XAML. Den här klassen undanröjer en extra kopia som vanligtvis skulle behövas med WriteableBitmap, och som hjälper till att minska maximalt minne och svarstid från källa till skärm.

SoftwareBitmap som tillhandahåller källinformation kan också konfigureras för att använda en anpassad IWICBitmap för att tillhandahålla ett återläsbart säkerhetskopieringsarkiv som gör att appen kan mappa om minnet som det passar. Det här är ett avancerat C++-användningsfall.

Din app bör använda SoftwareBitmap och SoftwareBitmapSource för att samverka med andra WinRT-API:er som producerar och använder bilder. Och din app bör använda SoftwareBitmapSource när du läser in okomprimerade bilddata i stället för att använda WriteableBitmap.

Använda GetThumbnailAsync för miniatyrbilder

Ett användningsfall för att skala bilder är att skapa miniatyrbilder. Även om du kan använda DecodePixelWidth och DecodePixelHeight för att tillhandahålla små versioner av bilder, ger Windows ännu effektivare API:er för att hämta miniatyrbilder. GetThumbnailAsync tillhandahåller miniatyrbilderna för bilder som redan har filsystemet cachelagrat. Detta ger ännu bättre prestanda än XAML-API:erna eftersom avbildningen inte behöver öppnas eller avkodas.

I en Windows App SDK-app initierar du urvalskomponenten med ditt appfönsterhandtag, till exempel genom att lagra huvudfönstret som 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;

Avkoda bilder en gång

Om du vill förhindra att bilder avkodas mer än en gång tilldelar du egenskapen Image.Source från en URI i stället för att använda minnesströmmar. XAML-ramverket kan associera samma Uri på flera platser med en avkodad bild, men det kan inte göra samma sak för flera minnesströmmar som innehåller samma data och skapar en annan avkodad bild för varje minnesström.