次の方法で共有


コンポジション ビジュアル

コンポジション ビジュアルは、Microsoft.UI.Composition API の他のすべての機能で使用および構築されるビジュアル ツリー構造を構成します。 この API を使用すると、開発者は、ビジュアル ツリー内の 1 つのノードを表す 1 つまたは複数のビジュアル オブジェクトを定義して作成できます。

Visuals

ビジュアル ツリー構造と、ビジュアルのコンテンツに影響を与える複数のサブクラスを持つ基本ブラシ クラスを構成するいくつかのビジュアル型があります。

  • Visual – 基本オブジェクト。プロパティの大部分はここにあり、他の Visual オブジェクトによって継承されます。
  • ContainerVisualVisual から派生し、子を作成する機能を追加します。
    • SpriteVisualContainerVisual から派生します。 イメージ、効果、または単色を含むピクセルをビジュアルでレンダリングできるように、ブラシを関連付けることができます。
    • LayerVisualContainerVisual から派生します。 ビジュアル要素の子要素は、1 つのレイヤーに統合されます。
    • ShapeVisualContainerVisual から派生します。 CompositionShape のルートであるビジュアル ツリー ノード。
    • RedirectVisualContainerVisual から派生します。 ビジュアルは、別のビジュアルからコンテンツを取得します。
    • SceneVisualContainerVisual から派生します。 3D シーンのノードのコンテナー ビジュアル。

CompositionBrush とそのサブクラス (CompositionColorBrushCompositionSurfaceBrushCompositionEffectBrush など) を使用して、SpriteVisuals にコンテンツと効果を適用できます。 ブラシの詳細については、「 CompositionBrush の概要」を参照してください。

CompositionVisual サンプル

ここでは、前に示した 3 つの異なるビジュアルの種類を示す WinUI 3 サンプル コードをいくつか見ていきます。 このサンプルでは、アニメーションやより複雑な効果などの概念については説明しませんが、すべてのシステムで使用される構成要素が含まれています。 (完全なサンプル コードは、この記事の最後に記載されています)。

サンプルには、画面をクリックしてドラッグできる多くの純色の四角形があります。 四角形をクリックすると、前面に移動し、45 度回転し、ドラッグすると不透明になります。

以下に、API を操作するための基本的な概念を示します。

  • コンポジターの作成
  • CompositionColorBrush を使用して SpriteVisual を作成する
  • ビジュアルのクリッピング
  • ビジュアルの回転
  • 不透明度の設定
  • コレクション内のビジュアルの位置を変更する。

コンポジターの作成

コンポジターを作成し、ファクトリとして使用するために変数に格納するのは簡単な作業です。 WinUI アプリでは、通常、ビジュアル ツリーに既に接続されている XAML 要素からコンポジターを取得します。

Compositor compositor = ElementCompositionPreview.GetElementVisual(MyHost).Compositor;

コンポジターが必要で、UIElement を使用できない場合は、代わりに CompositionTarget.GetCompositorForCurrentThread() を使用できます。

SpriteVisual と ColorBrush の作成

Compositor を使用すると、SpriteVisualCompositionColorBrush など、オブジェクトが必要なときにいつでも簡単に作成できます。

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

これはほんの数行のコードですが、強力な概念を示しています 。SpriteVisual オブジェクトはエフェクト システムの中心です。 SpriteVisual を使用すると、色、画像、効果の作成における優れた柔軟性と相互作用が可能になります。 SpriteVisual は、2D 四角形をブラシ (この場合は単色) で塗りつぶすことができる単一のビジュアル型です。

ビジュアルのクリッピング

コンポジターを使用して、ビジュアルにクリップを作成することもできます。 次に示すのは、 InsetClip を使用してビジュアルの両側をトリミングするサンプルの例です。

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

API の他のオブジェクトと同様に、 InsetClip では、プロパティにアニメーションを適用できます。

クリップの回転

ビジュアルは回転を用いて変形させることができます。 RotationAngle ではラジアンと度の両方がサポートされることに注意してください。 既定ではラジアンですが、次のスニペットに示すように度を指定するのは簡単です。

child.RotationAngleInDegrees = 45.0f;

ローテーションは、これらのタスクを容易にするために API によって提供される一連の変換コンポーネントの一例にすぎません。 その他には、Offset、Scale、Orientation、RotationAxis、4x4 TransformMatrix などがあります。

不透明度の設定

ビジュアルの不透明度の設定は、float 値を使用した簡単な操作です。 たとえば、サンプルでは、すべての正方形の不透明度が .8 から始まります。

visual.Opacity = 0.8f;

回転と同様に、 Opacity プロパティをアニメーション化できます。

コレクション内のビジュアルの位置を変更する

コンポジション API を使用すると、 VisualCollection 内のビジュアルの位置をさまざまな方法で変更できます。 InsertAbove を使用して別のビジュアルの上に配置したり、InsertBelow で下に配置したり、InsertAtTop を使用して上部に移動したり、InsertAtBottom を使用して下部に移動したりできます。

このサンプルでは、クリックされた ビジュアル が一番上に並べ替えられます。

parent.Children.InsertAtTop(_currentVisual);

完全な例

完全な WinUI サンプルでは、上記のすべての概念を組み合わせて使用して 、カスタム DirectX レンダリングなしで不透明度を変更する Visual オブジェクトの単純なツリーを構築およびウォークします。 このサンプルでは、ポインター入力に WinUI Grid を使用し、コンポジション ツリーを ElementCompositionPreviewでホストします。

<Page
    x:Class="CompositionVisualSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Grid
        x:Name="MyHost"
        Background="Transparent"
        PointerPressed="OnPointerPressed"
        PointerMoved="OnPointerMoved"
        PointerReleased="OnPointerReleased" />
</Page>
using System;
using System.Numerics;
using Microsoft.UI.Composition;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Hosting;
using Microsoft.UI.Xaml.Input;
using Windows.Foundation;
using Windows.UI;

namespace CompositionVisualSample
{
    public sealed partial class MainPage : Page
    {
        private readonly Random _random = new();
        private Compositor _compositor;
        private ContainerVisual _root;
        private ContainerVisual _currentVisual;
        private Vector2 _offsetBias;
        private bool _dragging;

        public MainPage()
        {
            InitializeComponent();
            Loaded += OnLoaded;
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            if (_root != null)
            {
                return;
            }

            _compositor = ElementCompositionPreview.GetElementVisual(MyHost).Compositor;
            _root = _compositor.CreateContainerVisual();
            ElementCompositionPreview.SetElementChildVisual(MyHost, _root);

            for (int index = 0; index < 20; index++)
            {
                _root.Children.InsertAtTop(CreateChildElement());
            }
        }

        private ContainerVisual CreateChildElement()
        {
            var element = _compositor.CreateContainerVisual();
            element.Size = new Vector2(100.0f, 100.0f);

            var maxX = (float)Math.Max(MyHost.ActualWidth - element.Size.X, 0.0);
            var maxY = (float)Math.Max(MyHost.ActualHeight - element.Size.Y, 0.0);
            element.Offset = new Vector3(
                (float)(_random.NextDouble() * maxX),
                (float)(_random.NextDouble() * maxY),
                0.0f);

            var outer = _compositor.CreateSpriteVisual();
            outer.Size = new Vector2(100.0f, 100.0f);
            outer.Brush = _compositor.CreateColorBrush(Colors.White);
            element.Children.InsertAtTop(outer);

            var inner = _compositor.CreateSpriteVisual();
            inner.Offset = new Vector3(3.0f, 3.0f, 0.0f);
            inner.Size = new Vector2(94.0f, 94.0f);

            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)));
            inner.Brush = _compositor.CreateColorBrush(Color.FromArgb(0xFF, red, green, blue));
            outer.Children.InsertAtTop(inner);

            element.Opacity = 0.8f;
            return element;
        }

        private void OnPointerPressed(object sender, PointerRoutedEventArgs e)
        {
            Point position = e.GetCurrentPoint(MyHost).Position;
            _currentVisual = null;

            foreach (var child in _root.Children)
            {
                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))
                {
                    _currentVisual = child as ContainerVisual;
                    _offsetBias = new Vector2(
                        (float)(offset.X - position.X),
                        (float)(offset.Y - position.Y));
                }
            }

            if (_currentVisual != null)
            {
                ContainerVisual parent = (ContainerVisual)_currentVisual.Parent;
                parent.Children.Remove(_currentVisual);
                parent.Children.InsertAtTop(_currentVisual);
            }
        }

        private void OnPointerMoved(object sender, PointerRoutedEventArgs e)
        {
            if (_currentVisual == null)
            {
                return;
            }

            if (!_dragging)
            {
                _currentVisual.Opacity = 1.0f;

                foreach (var child in _currentVisual.Children)
                {
                    child.RotationAngleInDegrees = 45.0f;
                    child.CenterPoint = new Vector3(_currentVisual.Size.X / 2, _currentVisual.Size.Y / 2, 0.0f);
                    break;
                }

                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 = e.GetCurrentPoint(MyHost).Position;
            _currentVisual.Offset = new Vector3(
                (float)(position.X + _offsetBias.X),
                (float)(position.Y + _offsetBias.Y),
                0.0f);
        }

        private void OnPointerReleased(object sender, PointerRoutedEventArgs e)
        {
            if (_currentVisual == null)
            {
                return;
            }

            if (_dragging)
            {
                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;
        }
    }
}

XAML ホスト要素を作成する前にコンポジターを初期化する必要がある場合は、 CompositionTarget.GetCompositorForCurrentThread() を使用し、ホスト要素が使用可能になったときにビジュアルを UI にアタッチします。