如何在 WPF/XAML 中制作关于 Y 轴的列表的连续 360 度动画

Posted

技术标签:

【中文标题】如何在 WPF/XAML 中制作关于 Y 轴的列表的连续 360 度动画【英文标题】:How to make a continuous 360deg animation of a list about the Y-Axis in WPF/XAML 【发布时间】:2021-08-10 06:30:10 【问题描述】:

我对 WPF/XAML 中的动画完全陌生。下面是我在 SO 上找到的代码,用于围绕 Y 轴旋转 2D 列表。

但是,似乎只发生了通过左象限的旋转,然后它消失了一段时间,然后再次出现在同一象限中。

我需要做什么才能可视化围绕 Y 轴的完整 360 度连续旋转(不会消失)。

感谢您的帮助。

<DockPanel Grid.Row="1" Grid.Column="1" Background="White">
            <DockPanel.Resources>
                <!-- UI Mesh -->
                <MeshGeometry3D x:Key="uiMesh" TriangleIndices="0,1,2 3,4,5" 
                        Positions="-1,-1,2 1,-1,2 1,1,2 1,1,2 -1,1,2 -1,-1,2 "
                        TextureCoordinates="0,1 1,1 1,0 1,0, 0,0 0,1"/>
                <!-- UI Mesh Rotation -->
                <Storyboard x:Key="uiSpin" RepeatBehavior="Forever">
                    <DoubleAnimation BeginTime="00:00:00" Duration="00:00:02"   
                                 Storyboard.TargetName="uiRotate" 
                                 Storyboard.TargetProperty="Angle" From="0" To="360"/>
                </Storyboard>
            </DockPanel.Resources>

            <DockPanel.Triggers>
                <EventTrigger RoutedEvent="FrameworkElement.Loaded">
                    <BeginStoryboard Storyboard="StaticResource uiSpin"/>
                </EventTrigger>
            </DockPanel.Triggers>

            <Viewport3D>
                <!-- Camera -->
                <Viewport3D.Camera>
                    <PerspectiveCamera Position="0, 0, 4"/>
                </Viewport3D.Camera>

                <!-- Button on 3D -->
                <Viewport2DVisual3D >
                    <!-- Give the plane a slight rotation -->
                    <Viewport2DVisual3D.Transform>
                        <RotateTransform3D>
                            <RotateTransform3D.Rotation>
                                <AxisAngleRotation3D x:Name="uiRotate" 
                                                 Angle="40" Axis="0, 1, 0" />
                            </RotateTransform3D.Rotation>
                        </RotateTransform3D>
                    </Viewport2DVisual3D.Transform>

                    <!-- The Geometry, Material, and Visual for the 
                     Viewport2DVisual3D -->
                    <Viewport2DVisual3D.Geometry>
                        <MeshGeometry3D Positions="-1,1,0 -1,-1,0 1,-1,0 1,1,0"
                                    TextureCoordinates="0,0 0,1 1,1 1,0" 
                                    TriangleIndices="0 1 2 0 2 3"/>
                    </Viewport2DVisual3D.Geometry>
                    <!-- Setup the Material"
                     You can use any material you want.  For the material 
                     that you want to have the Visual be placed on, you simply 
                     need to set the Viewport2DVisual3D.IsVisualHostMaterial 
                     attached property to true.
                -->
                    <Viewport2DVisual3D.Material>
                        <DiffuseMaterial Viewport2DVisual3D.IsVisualHostMaterial="True" 
                                     Brush="White"/>
                    </Viewport2DVisual3D.Material>

                    <Viewport2DVisual3D.Visual>
                        <!-- The 2D UI-->
                        <ListBox 
                          Name="listBox1"
                          PreviewMouseLeftButtonDown="listBox1_PreviewMouseLeftButtonDown"
                          IsEnabled="Binding IsEnabled, Converter=cv:InverseBooleanConverter"
                          ItemsSource="Binding Printers"
                          ItemTemplate="StaticResource Printer"
                          ItemsPanel="StaticResource VSP"/>
                    </Viewport2DVisual3D.Visual>
                </Viewport2DVisual3D>

                <!-- Lights -->
                <ModelVisual3D>
                    <ModelVisual3D.Content>
                        <DirectionalLight Color="#FFFFFFFF" Direction="0,0,-1"/>
                    </ModelVisual3D.Content>
                </ModelVisual3D>
            </Viewport3D>

        </DockPanel>

【问题讨论】:

我目前没有时间查看代码,但是...您是否尝试过使用环境光而不是定向光?使用 3D API 时,有时会出现“事物消失”问题,这是因为缺少任何会产生图像的照明。当然,另一个常见的错误是三角形的朝向错误(您可以通过添加背面材料来仔细检查...使用艳丽的颜色,以便在查看三角形背面时很明显)。 @PeterDuniho 我不知道什么是“背面材料”,但强烈怀疑这是问题所在。在使用 AxisAngelRotation3D 时,材质在 90 到 360 度之间消失。 (我几乎找不到这方面的信息)。谢谢。 嗯...嗯,我在考虑docs.microsoft.com/en-us/dotnet/api/…,但再看上面我看到你使用的是Viewport2DVisual3D,我没有任何实际经验。我真的不知道“2D in 3D”是如何工作的。您可能只能看到 2D 元素的一侧,并且它打算放置在背面会被遮挡的实体的表面上。您可以尝试在反面镜像 2D 元素(ListBox),看看是否有帮助。 当然,请确保您还尝试了环境光,以防万一这是唯一的问题。在任何情况下,您都应该绝对解决您的问题,以便它包含正确的minimal reproducible example。消除并非绝对必要的所有内容,添加为实际可运行程序添加最少的代码,也许其他人将有时间玩弄代码并让它按照您的意愿工作。跨度> 就背面材料而言,正如我所指出的,当您使用Viewport2DVisual3D 时,这可能不是一个选项。 BackMaterial 属性在 Geometry3D 类上,因此您可以为您为 Viewport2DVisual3D.Geometry 定义的 MeshGeometry3D 设置它,但我不确定这是否会改变 @ 987654331@本身呈现;可能ListBox 仍然不可见,即使您可以将几何图形的背面显示为可见。不过,我猜还是值得一试。 【参考方案1】:

跟进@PeterDuniho 的建议导致了以下文章:Flippable 3D List Items in WPF 由 Ian Griffiths 在博客表单 (RSS 2.0) 中。这是对@PeterDuniho 提到的“BackMaterial”的一项很好的研究。但是,我不希望 Griffiths 的“跳跃”是代码。我发现 Viewport2DVisual3D 的当前分辨率已经足够了。阅读 Josh Smith 在 Viewport2DVisual3D Introducing ContentControl3D 上的文章非常有帮助。最后,needbrew 的seeing the back of the Viewport2DVisual3D 帮助我更好地理解了 Viewport2DVisual3D。还有很多其他的论文讨论Geometry3D,但是Viewport2DVisual3D的相对较少,我不知道为什么。

无论如何,对于像我这样的其他新手,我在下面发布了一个使用 Viewport2DVisual3D 完成的按钮(不是列表@PeterDuniho :) 的工作连续 360 度旋转。这是needbrew解决方案的近乎重印,如上所述。

<Window x:Class="TestV3.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:TestV3"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">

<DockPanel>
    <DockPanel.Resources>
        <!-- UI Mesh Rotation -->
        <Storyboard x:Key="uiSpin" RepeatBehavior="Forever">
            <DoubleAnimation BeginTime="00:00:00" Duration="00:00:15" 
                             Storyboard.TargetName="uiRotate" 
                             Storyboard.TargetProperty="Angle" From="0" To="360"/>
            <DoubleAnimation BeginTime="00:00:00" Duration="00:00:15" 
                             Storyboard.TargetName="uiRotate2" 
                             Storyboard.TargetProperty="Angle" From="0" To="360"/>
        </Storyboard>
    </DockPanel.Resources>

    <DockPanel.Triggers>
        <EventTrigger RoutedEvent="FrameworkElement.Loaded">
            <BeginStoryboard Storyboard="StaticResource uiSpin"/>
        </EventTrigger>
    </DockPanel.Triggers>

    <ScrollBar Name="horz" DockPanel.Dock="Bottom" Orientation="Horizontal"
Minimum="-180" Maximum="180"
LargeChange="10" SmallChange="1" />
    <Viewport3D>

        <Viewport3D.Camera>
            <PerspectiveCamera Position="0, 0, 4"/>
        </Viewport3D.Camera>

        <!-- Button on 3D -->
        <Viewport2DVisual3D>
            <!-- Give the plane a slight rotation -->
            <Viewport2DVisual3D.Transform>
                <RotateTransform3D>
                    <RotateTransform3D.Rotation>
                        <AxisAngleRotation3D  x:Name="uiRotate" Angle="Binding 
   ElementName=horz, Path=Value" Axis="0, 1, 0" />
                    </RotateTransform3D.Rotation>
                </RotateTransform3D>

            </Viewport2DVisual3D.Transform>

            <!-- The Geometry, Material, and Visual for the Viewport2DVisual3D -->
            <Viewport2DVisual3D.Geometry>
                <MeshGeometry3D Positions="-1,1,0 -1,-1,0 1,-1,0 1,1,0"
    TextureCoordinates="0,0 0,1 1,1 1,0" TriangleIndices="0 1 2 0 2 3"/>
            </Viewport2DVisual3D.Geometry>

            <Viewport2DVisual3D.Material>
                <DiffuseMaterial Viewport2DVisual3D.IsVisualHostMaterial="True" Brush="White"/>
            </Viewport2DVisual3D.Material>

            <Button>Hello, 3D</Button>

        </Viewport2DVisual3D>

        <Viewport2DVisual3D>

            <!-- Give the plane a slight rotation -->
            <Viewport2DVisual3D.Transform>
                <RotateTransform3D>
                    <RotateTransform3D.Rotation>
                        <AxisAngleRotation3D  x:Name="uiRotate2" Angle="Binding ElementName=horz, Path=Value" Axis="0, 1, 0" />
                    </RotateTransform3D.Rotation>
                </RotateTransform3D>
            </Viewport2DVisual3D.Transform>

            <!-- The Geometry, Material, and Visual for the Viewport2DVisual3D -->

            <Viewport2DVisual3D.Geometry>
                <MeshGeometry3D Positions="-1,1,0 -1,-1,0 1,-1,0 1,1,0"
  TextureCoordinates="0,0 0,1 1,1 1,0" TriangleIndices="0 2 1 0 3 2"/>
            </Viewport2DVisual3D.Geometry>

            <Viewport2DVisual3D.Material>
                <DiffuseMaterial Viewport2DVisual3D.IsVisualHostMaterial="True" Brush="White"/>
            </Viewport2DVisual3D.Material>

            <Button>Hello, 3D back>
                <Button.RenderTransform>
                    <ScaleTransform ScaleX="-1" ScaleY="-1"/>
                </Button.RenderTransform>
            </Button>
        </Viewport2DVisual3D>

        <!-- Lights -->
        <ModelVisual3D>
            <ModelVisual3D.Content>
                <DirectionalLight Color="#FFFFFFFF" Direction="0,0,-1"/>
            </ModelVisual3D.Content>
        </ModelVisual3D>

    </Viewport3D>

</DockPanel>

【讨论】:

以上是关于如何在 WPF/XAML 中制作关于 Y 轴的列表的连续 360 度动画的主要内容,如果未能解决你的问题,请参考以下文章

WPF xaml中列表依赖属性的定义

如何在具有不同 Y 轴的同一个 seaborn 图中很好地制作条形图和线图?

怎样用EXCEL制作一个X轴两个Y轴的柱形图,

Python制作带有辅助y轴的组合条形图和线图

将 WPF XAML 代码移植到 Silverlight XAML 代码

选择组合框时设置文本框的属性 WPF XAML