Visual komposisi

Visual Komposisi membentuk struktur pohon visual yang semua fitur lain dari komposisi API menggunakan dan membangun. API memungkinkan pengembang untuk menentukan dan membuat satu atau banyak objek visual yang masing-masing mewakili satu simpul di pohon visual.

Visual

Ada beberapa jenis visual yang membentuk struktur pohon visual ditambah kelas sikat dasar dengan beberapa subkelas yang memengaruhi konten visual:

  • Visual – objek dasar, sebagian besar properti ada di sini, dan diwarisi oleh objek Visual lainnya.
  • ContainerVisual – berasal dari Visual, dan menambahkan kemampuan untuk membuat anak-anak.
    • SpriteVisual – berasal dari ContainerVisual. Memiliki kemampuan untuk mengaitkan kuas sehingga Visual dapat merender piksel termasuk gambar, efek, atau warna solid.
    • LayerVisual – berasal dari ContainerVisual. Anak-anak visual diratakan menjadi satu lapisan.
      (Diperkenalkan dalam Windows 10, versi 1607, SDK 14393.)
    • ShapeVisual – berasal dari ContainerVisual. Simpul pohon visual yang merupakan akar dari CompositionShape.
      (Diperkenalkan dalam Windows 10, versi 1803, SDK 17134.)
    • RedirectVisual – berasal dari ContainerVisual. Visual mendapatkan kontennya dari visual lain.
      (Diperkenalkan dalam Windows 10, versi 1809, SDK 17763.)
    • SceneVisual – berasal dari ContainerVisual. Visual kontainer untuk simpul adegan 3D.
      (Diperkenalkan dalam Windows 10, versi 1903, SDK 18362.)

Anda dapat menerapkan konten dan efek ke SpriteVisuals menggunakan KomposisiBrush dan subkelasnya termasuk KomposisiColorBrush, KomposisiSurfaceBrush dan KomposisiEffectBrush. Untuk mempelajari selengkapnya tentang kuas, lihat Ringkasan CompositionBrush.

Sampel KomposisiVisual

Di sini, kita akan melihat beberapa kode sampel yang menunjukkan tiga jenis visual berbeda yang tercantum sebelumnya. Meskipun sampel ini tidak mencakup konsep seperti Animasi atau efek yang lebih kompleks, sampel ini berisi blok penyusun yang digunakan semua sistem tersebut. (Kode sampel lengkap tercantum di akhir artikel ini.)

Dalam sampel ada sejumlah kotak warna solid yang dapat diklik dan diseret tentang layar. Ketika persegi diklik, itu akan datang ke depan, memutar 45 derajat, dan menjadi buram ketika diseret sekitar.

Ini menunjukkan sejumlah konsep dasar untuk bekerja dengan API termasuk:

  • Membuat komposit
  • Membuat SpriteVisual dengan CompositionColorBrush
  • Mengklip Visual
  • Memutar Visual
  • Mengatur Opasitas
  • Mengubah posisi Visual dalam koleksi.

Membuat Compositor

Membuat Compositor dan menyimpannya dalam variabel untuk digunakan karena pabrik adalah tugas sederhana. Cuplikan berikut menunjukkan pembuatan Compositor baru:

_compositor = new Compositor();

Membuat SpriteVisual dan ColorBrush

Menggunakan Compositor mudah untuk membuat objek kapan pun Anda membutuhkannya, seperti SpriteVisual dan CompositionColorBrush:

var visual = _compositor.CreateSpriteVisual();
visual.Brush = _compositor.CreateColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));

Meskipun ini hanya beberapa baris kode, ini menunjukkan konsep yang kuat: objek SpriteVisual adalah inti dari sistem efek. SpriteVisual memungkinkan fleksibilitas dan interplay yang hebat dalam pembuatan warna, gambar, dan efek. SpriteVisual adalah jenis visual tunggal yang dapat mengisi persegi panjang 2D dengan kuas, dalam hal ini, warna solid.

Mengklip Visual

Compositor juga dapat digunakan untuk membuat klip ke Visual. Di bawah ini adalah contoh dari sampel penggunaan InsetClip untuk memangkas setiap sisi visual:

var clip = _compositor.CreateInsetClip();
clip.LeftInset = 1.0f;
clip.RightInset = 1.0f;
clip.TopInset = 1.0f;
clip.BottomInset = 1.0f;
_currentVisual.Clip = clip;

Seperti objek lain di API, InsetClip dapat menerapkan animasi ke propertinya.

Memutar Klip

Visual dapat diubah dengan rotasi. Perhatikan bahwa RotationAngle mendukung radian dan derajat. Ini default ke radian, tetapi mudah untuk menentukan derajat seperti yang ditunjukkan dalam cuplikan berikut:

child.RotationAngleInDegrees = 45.0f;

Rotasi hanyalah salah satu contoh set komponen transformasi yang disediakan oleh API untuk mempermudah tugas-tugas ini. Lainnya termasuk Offset, Scale, Orientation, RotationAxis, dan 4x4 TransformMatrix.

Mengatur Opasitas

Mengatur keburaman visual adalah operasi sederhana menggunakan nilai float. Misalnya, dalam sampel semua kuadrat dimulai pada keburaman .8:

visual.Opacity = 0.8f;

Seperti rotasi, properti Opacity dapat dianimasikan.

Mengubah posisi Visual dalam koleksi

API Komposisi memungkinkan posisi Visual dalam VisualCollection diubah dengan sejumlah cara. Ini dapat ditempatkan di atas Visual lain dengan InsertAbove, ditempatkan di bawah ini dengan InsertBelow, dipindahkan ke bagian atas dengan InsertAtTop, atau bagian bawah dengan InsertAtBottom.

Dalam sampel, Visual yang telah diklik diurutkan ke bagian atas:

parent.Children.InsertAtTop(_currentVisual);

Contoh Lengkap

Dalam sampel lengkap, semua konsep di atas digunakan bersama-sama untuk membangun dan berjalan di pohon sederhana objek Visual untuk mengubah opasitas tanpa menggunakan XAML, WWA, atau DirectX. Sampel ini memperlihatkan bagaimana objek Visual anak dibuat dan ditambahkan dan bagaimana properti diubah.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.Foundation;
using Windows.UI;
using Windows.UI.Composition;
using Windows.UI.Core;

namespace compositionvisual
{
    class VisualProperties : IFrameworkView
    {
        //------------------------------------------------------------------------------
        //
        // VisualProperties.Initialize
        //
        // This method is called during startup to associate the IFrameworkView with the
        // CoreApplicationView.
        //
        //------------------------------------------------------------------------------

        void IFrameworkView.Initialize(CoreApplicationView view)
        {
            _view = view;
            _random = new Random();
        }

        //------------------------------------------------------------------------------
        //
        // VisualProperties.SetWindow
        //
        // This method is called when the CoreApplication has created a new CoreWindow,
        // allowing the application to configure the window and start producing content
        // to display.
        //
        //------------------------------------------------------------------------------

        void IFrameworkView.SetWindow(CoreWindow window)
        {
            _window = window;
            InitNewComposition();
            _window.PointerPressed += OnPointerPressed;
            _window.PointerMoved += OnPointerMoved;
            _window.PointerReleased += OnPointerReleased;
        }

        //------------------------------------------------------------------------------
        //
        // VisualProperties.OnPointerPressed
        //
        // This method is called when the user touches the screen, taps it with a stylus
        // or clicks the mouse.
        //
        //------------------------------------------------------------------------------

        void OnPointerPressed(CoreWindow window, PointerEventArgs args)
        {
            Point position = args.CurrentPoint.Position;

            //
            // Walk our list of visuals to determine who, if anybody, was selected
            //
            foreach (var child in _root.Children)
            {
                //
                // Did we hit this child?
                //
                Vector3 offset = child.Offset;
                Vector2 size = child.Size;

                if ((position.X >= offset.X) &&
                    (position.X < offset.X + size.X) &&
                    (position.Y >= offset.Y) &&
                    (position.Y < offset.Y + size.Y))
                {
                    //
                    // This child was hit. Since the children are stored back to front,
                    // the last one hit is the front-most one so it wins
                    //
                    _currentVisual = child as ContainerVisual;
                    _offsetBias = new Vector2((float)(offset.X - position.X),
                                              (float)(offset.Y - position.Y));
                }
            }

            //
            // If a visual was hit, bring it to the front of the Z order
            //
            if (_currentVisual != null)
            {
                ContainerVisual parent = _currentVisual.Parent as ContainerVisual;
                parent.Children.Remove(_currentVisual);
                parent.Children.InsertAtTop(_currentVisual);
            }
        }

        //------------------------------------------------------------------------------
        //
        // VisualProperties.OnPointerMoved
        //
        // This method is called when the user moves their finger, stylus or mouse with
        // a button pressed over the screen.
        //
        //------------------------------------------------------------------------------

        void OnPointerMoved(CoreWindow window, PointerEventArgs args)
        {
            //
            // If a visual is selected, drag it with the pointer position and
            // make it opaque while we drag it
            //
            if (_currentVisual != null)
            {
                //
                // Set up the properties of the visual the first time it is
                // dragged. This will last for the duration of the drag
                //
                if (!_dragging)
                {
                    _currentVisual.Opacity = 1.0f;

                    //
                    // Transform the first child of the current visual so that
                    // the image is rotated
                    //
                    foreach (var child in _currentVisual.Children)
                    {
                        child.RotationAngleInDegrees = 45.0f;
                        child.CenterPoint = new Vector3(_currentVisual.Size.X / 2, _currentVisual.Size.Y / 2, 0);
                        break;
                    }

                    //
                    // Clip the visual to its original layout rect by using an inset
                    // clip with a one-pixel margin all around
                    //
                    var clip = _compositor.CreateInsetClip();
                    clip.LeftInset = 1.0f;
                    clip.RightInset = 1.0f;
                    clip.TopInset = 1.0f;
                    clip.BottomInset = 1.0f;
                    _currentVisual.Clip = clip;

                    _dragging = true;
                }

                Point position = args.CurrentPoint.Position;
                _currentVisual.Offset = new Vector3((float)(position.X + _offsetBias.X),
                                                    (float)(position.Y + _offsetBias.Y),
                                                    0.0f);
            }
        }

        //------------------------------------------------------------------------------
        //
        // VisualProperties.OnPointerReleased
        //
        // This method is called when the user lifts their finger or stylus from the
        // screen, or lifts the mouse button.
        //
        //------------------------------------------------------------------------------

        void OnPointerReleased(CoreWindow window, PointerEventArgs args)
        {
            //
            // If a visual was selected, make it transparent again when it is
            // released and restore the transform and clip
            //
            if (_currentVisual != null)
            {
                if (_dragging)
                {
                    //
                    // Remove the transform from the first child
                    //
                    foreach (var child in _currentVisual.Children)
                    {
                        child.RotationAngle = 0.0f;
                        child.CenterPoint = new Vector3(0.0f, 0.0f, 0.0f);
                        break;
                    }

                    _currentVisual.Opacity = 0.8f;
                    _currentVisual.Clip = null;
                    _dragging = false;
                }

                _currentVisual = null;
            }
        }

        //------------------------------------------------------------------------------
        //
        // VisualProperties.Load
        //
        // This method is called when a specific page is being loaded in the
        // application.  It is not used for this application.
        //
        //------------------------------------------------------------------------------

        void IFrameworkView.Load(string unused)
        {

        }

        //------------------------------------------------------------------------------
        //
        // VisualProperties.Run
        //
        // This method is called by CoreApplication.Run() to actually run the
        // dispatcher's message pump.
        //
        //------------------------------------------------------------------------------

        void IFrameworkView.Run()
        {
            _window.Activate();
            _window.Dispatcher.ProcessEvents(CoreProcessEventsOption.ProcessUntilQuit);
        }

        //------------------------------------------------------------------------------
        //
        // VisualProperties.Uninitialize
        //
        // This method is called during shutdown to disconnect the CoreApplicationView,
        // and CoreWindow from the IFrameworkView.
        //
        //------------------------------------------------------------------------------

        void IFrameworkView.Uninitialize()
        {
            _window = null;
            _view = null;
        }

        //------------------------------------------------------------------------------
        //
        // VisualProperties.InitNewComposition
        //
        // This method is called by SetWindow(), where we initialize Composition after
        // the CoreWindow has been created.
        //
        //------------------------------------------------------------------------------

        void InitNewComposition()
        {
            //
            // Set up Windows.UI.Composition Compositor, root ContainerVisual, and associate with
            // the CoreWindow.
            //

            _compositor = new Compositor();

            _root = _compositor.CreateContainerVisual();



            _compositionTarget = _compositor.CreateTargetForCurrentView();
            _compositionTarget.Root = _root;

            //
            // Create a few visuals for our window
            //
            for (int index = 0; index < 20; index++)
            {
                _root.Children.InsertAtTop(CreateChildElement());
            }
        }

        //------------------------------------------------------------------------------
        //
        // VisualProperties.CreateChildElement
        //
        // Creates a small sub-tree to represent a visible element in our application.
        //
        //------------------------------------------------------------------------------

        Visual CreateChildElement()
        {
            //
            // Each element consists of three visuals, which produce the appearance
            // of a framed rectangle
            //
            var element = _compositor.CreateContainerVisual();
            element.Size = new Vector2(100.0f, 100.0f);

            //
            // Position this visual randomly within our window
            //
            element.Offset = new Vector3((float)(_random.NextDouble() * 400), (float)(_random.NextDouble() * 400), 0.0f);

            //
            // The outer rectangle is always white
            //
            //Note to preview API users - SpriteVisual and Color Brush replace SolidColorVisual
            //for example instead of doing
            //var visual = _compositor.CreateSolidColorVisual() and
            //visual.Color = Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF);
            //we now use the below

            var visual = _compositor.CreateSpriteVisual();
            element.Children.InsertAtTop(visual);
            visual.Brush = _compositor.CreateColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
            visual.Size = new Vector2(100.0f, 100.0f);

            //
            // The inner rectangle is inset from the outer by three pixels all around
            //
            var child = _compositor.CreateSpriteVisual();
            visual.Children.InsertAtTop(child);
            child.Offset = new Vector3(3.0f, 3.0f, 0.0f);
            child.Size = new Vector2(94.0f, 94.0f);

            //
            // Pick a random color for every rectangle
            //
            byte red = (byte)(0xFF * (0.2f + (_random.NextDouble() / 0.8f)));
            byte green = (byte)(0xFF * (0.2f + (_random.NextDouble() / 0.8f)));
            byte blue = (byte)(0xFF * (0.2f + (_random.NextDouble() / 0.8f)));
            child.Brush = _compositor.CreateColorBrush(Color.FromArgb(0xFF, red, green, blue));

            //
            // Make the subtree root visual partially transparent. This will cause each visual in the subtree
            // to render partially transparent, since a visual's opacity is multiplied with its parent's
            // opacity
            //
            element.Opacity = 0.8f;

            return element;
        }

        // CoreWindow / CoreApplicationView
        private CoreWindow _window;
        private CoreApplicationView _view;

        // Windows.UI.Composition
        private Compositor _compositor;
        private CompositionTarget _compositionTarget;
        private ContainerVisual _root;
        private ContainerVisual _currentVisual;
        private Vector2 _offsetBias;
        private bool _dragging;

        // Helpers
        private Random _random;
    }


    public sealed class VisualPropertiesFactory : IFrameworkViewSource
    {
        //------------------------------------------------------------------------------
        //
        // VisualPropertiesFactory.CreateView
        //
        // This method is called by CoreApplication to provide a new IFrameworkView for
        // a CoreWindow that is being created.
        //
        //------------------------------------------------------------------------------

        IFrameworkView IFrameworkViewSource.CreateView()
        {
            return new VisualProperties();
        }


        //------------------------------------------------------------------------------
        //
        // main
        //
        //------------------------------------------------------------------------------

        static int Main(string[] args)
        {
            CoreApplication.Run(new VisualPropertiesFactory());

            return 0;
        }
    }
}