CSS effects blog address:
3D 穿梭效果?使用 CSS 轻松搞定 - ChokCoco - 博客园 (cnblogs.com)
UWP effects blog address
3D 穿梭效果?使用 UWP 也能搞定 - dino.c - 博客园 (cnblogs.com)
The big guys are really at the top of the world.
You can really learn a lot.
I just joined in and tried using WPF. The effect was not very good, so I could barely get by.

Compared with CSS and UWP, the old WPF is relatively cumbersome to operate 3D effects. You need to first make a 3D model, then rotate the XY axis, then adjust the Z-axis stretch, and finally adjust the FOV. The shuttle effect depends on the number stretched on the Z-axis, which is 10 - 20 in the figure.
Of course, the most difficult thing is the connection between the end and the end of the animation.
Let's do it step by step.
Making 3D models
There is actually nothing to study whether this piece can be directly used or not, so it is a 3D model directly and the picture exists in the form of a texture.
As for 3D model making, to sum up, it starts from left to right.
- Positions
3D models are composed of triangles, so when describing what an object looks like, draw triangles as much as possible. Every point is a vertex. If there is the same vertex, just choose one.
When describing a vertex, it is XYZ. When you look at the object head-on, the direction the object's Z-axis points towards you is the positive direction. The Y axis is positive upward, and the X axis is positive rightward.

It is worth noting here that for convenience, it is recommended to use the order of left, left, bottom, right, bottom and right to define points.

The vertex definitions of this model are P1 (x, y, z), P2 (x, y, z), P3 (x, y, z), P4 (x, y, z)
- TriangleIndices
This describes how these vertices form a triangle. When we think of a face as a front, remember to define the vertex set subscripts counterclockwise. The set of vertices is Positions, and the content is the coordinate points you enter, with the subscripts starting from 0.
The back is clockwise.
- TextureCoordinates
This is the positioning order of the materials, and this compares the 2D coordinate system and is different from the others. That is, the upper left corner is 0, the lower right corner of 0 is 1, 1, which is the ordinary screen coordinate system
To make a good model, you need to make four.

-
- Rotating 3D model **
This is more interesting. Four models have been made above. We need to form four faces, that is, rotate the Y axis and the X axis, but the directions are different. To rotate, we can use Transform.

Looks okay.
Let's make four pictures and try them out.
Convenient debugging, we bring progress bars.
resources
<MeshGeometry3D x:Key="Rect3D_O">
<MeshGeometry3D.Positions>
-50, 50, 0, -50, -50, 0, 50, -50, 0, 50, 50, 0
</MeshGeometry3D.Positions>
<MeshGeometry3D.TriangleIndices> 0 1 2 2 3 0 </MeshGeometry3D.TriangleIndices>
<MeshGeometry3D.TextureCoordinates>
0,0 0,1 1,1 1,0
</MeshGeometry3D.TextureCoordinates>
<MeshGeometry3D.Normals> 0,0,1, 0,0,1, 0,0,1, 0,0,1 </MeshGeometry3D.Normals>
</MeshGeometry3D>
<DiffuseMaterial x:Key="Img">
<DiffuseMaterial.Brush>
<ImageBrush ImageSource="start.jpg" />
</DiffuseMaterial.Brush>
</DiffuseMaterial>
<Viewport3D x:Name="View3D_2">
<Viewport3D.Camera>
<PerspectiveCamera
FieldOfView="{Binding ElementName=FOV, Path=Value}"
Position="0,0,100"
LookDirection="0,0,-1"
/>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<AmbientLight Color="White" />
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Left_Top_Z, Path=Value}"
Axis="0 1 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ_2, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Left_Top_Z, Path=Value}"
Axis="1 0 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ_2, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Right_Bottom_Z, Path=Value}"
Axis="0 1 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ_2, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Right_Bottom_Z, Path=Value}"
Axis="1 0 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ_2, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
</Viewport3D>
<Slider
Grid.Row="1"
x:Name="Left_Top_Z"
Minimum="-90"
Value="12"
Maximum="90"
ToolTip="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value}"
/>
<Slider
Grid.Row="2"
x:Name="Right_Bottom_Z"
Minimum="-90"
Value="-12"
Maximum="0"
ToolTip="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value}"
/>
<Slider
Grid.Row="3"
x:Name="ScaleZ_2"
Background="Red"
Minimum="10"
Maximum="100"
ToolTip="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value}"
>
<Slider.Style>
<style TargetType="Slider">
<Style.Triggers>
<Trigger Property="Tag" Value="1">
<Trigger.EnterActions>
<BeginStoryboard >
<Storyboard>
<DoubleAnimation RepeatBehavior="Forever" Storyboard.TargetProperty="Value" From="11" To="20" BeginTime="0:0:0" Duration="0:0:10" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</style>
</Slider.Style>
</Slider>
<Slider
Grid.Row="4"
x:Name="FOV"
Minimum="0"
Maximum="180"
Value="176"
ToolTip="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value}"
/>
Run and see

It looks like it has a taste.
The rest is to debug the animation.
We want animations that are connected from end to end, but in fact, we want the two animations to not see the difference between the beginning and the end. Referring to the basic ideas of the big shots, relying on transparency and delaying playback makes it impossible to see the difference between the beginning and the end.

that is, to

Here, we implement this solution purely in XAML.
First of all, we must solve the timing problem. First, when the page loads, we modify a certain value of an object through animation. There is a trigger on this object. Monitor the object and start timing, and then play the animation.
To modify the value, we need to use ObjectAnimationUsingKeyFrames animation.
Triggers, numerical triggers, because they can be bound.
In the whole process, the most important thing is the timeline, when the two sets of animations appear and when they are hidden.

-
- Final Code **
<Window.Resources>
<Storyboard
RepeatBehavior="Forever"
Storyboard.TargetName="ScaleZ_2"
Duration="0:0:10"
x:Key="sb"
>
<DoubleAnimation
Storyboard.TargetName="ScaleZ_2"
Storyboard.TargetProperty="Value"
From="11"
To="20"
BeginTime="0:0:0"
Duration="0:0:10"
/>
<DoubleAnimationUsingKeyFrames
Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="View3D_2"
BeginTime="0:0:0"
>
<LinearDoubleKeyFrame KeyTime="0:0:0" Value="0" />
<LinearDoubleKeyFrame KeyTime="0:0:2" Value="1" />
<LinearDoubleKeyFrame KeyTime="0:0:6" Value="1" />
<LinearDoubleKeyFrame KeyTime="0:0:10" Value="0" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<MeshGeometry3D x:Key="Rect3D_O">
<MeshGeometry3D.Positions>
-50, 50, 0, -50, -50, 0, 50, -50, 0, 50, 50, 0
</MeshGeometry3D.Positions>
<MeshGeometry3D.TriangleIndices>
0 1 2 2 3 0
</MeshGeometry3D.TriangleIndices>
<MeshGeometry3D.TextureCoordinates>
0,0 0,1 1,1 1,0
</MeshGeometry3D.TextureCoordinates>
<MeshGeometry3D.Normals>
0,0,1, 0,0,1, 0,0,1, 0,0,1
</MeshGeometry3D.Normals>
</MeshGeometry3D>
<DiffuseMaterial x:Key="Img">
<DiffuseMaterial.Brush>
<ImageBrush ImageSource="start.jpg" />
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="Loaded" SourceName="ScaleZ">
<BeginStoryboard>
<Storyboard
Storyboard.TargetName="ScaleZ"
Storyboard.TargetProperty="Tag"
Duration="0:0:5.1"
>
<ObjectAnimationUsingKeyFrames>
<DiscreteObjectKeyFrame KeyTime="0:0:5">
<DiscreteObjectKeyFrame.Value> 1 </DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
<BeginStoryboard Storyboard="{StaticResource sb}" />
</EventTrigger>
</Window.Triggers>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Viewport3D x:Name="View3D_1">
<Viewport3D.Style>
<style TargetType="Viewport3D">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ScaleZ,Path=Value}" Value="1">
<DataTrigger.EnterActions>
<BeginStoryboard >
<Storyboard>
<DoubleAnimationUsingKeyFrames RepeatBehavior="Forever" Storyboard.TargetProperty="Opacity" BeginTime="0:0:0" Duration="0:0:10">
<LinearDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
<LinearDoubleKeyFrame KeyTime="0:0:5" Value="1"/>
<LinearDoubleKeyFrame KeyTime="0:0:8" Value="1"/>
<LinearDoubleKeyFrame KeyTime="0:0:10" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</style>
</Viewport3D.Style>
<Viewport3D.Camera>
<PerspectiveCamera
FieldOfView="{Binding ElementName=FOV, Path=Value}"
Position="0,0,100"
LookDirection="0,0,-1"
/>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<AmbientLight Color="White" />
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Left_Top_Z, Path=Value}"
Axis="0 1 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Left_Top_Z, Path=Value}"
Axis="1 0 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Right_Bottom_Z, Path=Value}"
Axis="0 1 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Right_Bottom_Z, Path=Value}"
Axis="1 0 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
</Viewport3D>
<Viewport3D x:Name="View3D_2">
<Viewport3D.Camera>
<PerspectiveCamera
FieldOfView="{Binding ElementName=FOV, Path=Value}"
Position="0,0,100"
LookDirection="0,0,-1"
/>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<AmbientLight Color="White" />
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Left_Top_Z, Path=Value}"
Axis="0 1 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ_2, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Left_Top_Z, Path=Value}"
Axis="1 0 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ_2, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Right_Bottom_Z, Path=Value}"
Axis="0 1 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ_2, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
<GeometryModel3D
Geometry="{StaticResource Rect3D_O}"
Material="{StaticResource Img}"
BackMaterial="{StaticResource Img}"
>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D
Angle="{Binding ElementName=Right_Bottom_Z, Path=Value}"
Axis="1 0 0"
/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<ScaleTransform3D
ScaleZ="{Binding ElementName=ScaleZ_2, Path=Value}"
/>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
</Viewport3D>
<Slider
Grid.Row="1"
x:Name="Left_Top_Z"
Minimum="-90"
Value="12"
Maximum="90"
ToolTip="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value}"
/>
<Slider
Grid.Row="2"
x:Name="Right_Bottom_Z"
Minimum="-90"
Value="-12"
Maximum="0"
ToolTip="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value}"
/>
<Slider
Grid.Row="3"
x:Name="ScaleZ"
Background="Red"
Minimum="10"
Maximum="100"
ToolTip="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value}"
>
<Slider.Style>
<style TargetType="Slider">
<Style.Triggers>
<Trigger Property="Tag" Value="1">
<Trigger.EnterActions>
<BeginStoryboard >
<Storyboard>
<DoubleAnimation RepeatBehavior="Forever" Storyboard.TargetProperty="Value" From="11" To="20" BeginTime="0:0:0" Duration="0:0:10" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</style>
</Slider.Style>
</Slider>
<Slider
Grid.Row="4"
x:Name="FOV"
Minimum="0"
Maximum="180"
Value="176"
ToolTip="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value}"
/>
<Slider
Grid.Row="5"
x:Name="ScaleZ_2"
Background="Black"
Minimum="10"
Maximum="100"
ToolTip="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Value}"
/>
<StackPanel Grid.Row="6" Orientation="Vertical">
<TextBlock>
<Run Text=" View3D_1 透明度" />
<Run Text="{Binding ElementName=View3D_1, Path=Opacity}" />
<Run Text=" View3D_1 value" />
<Run Text="{Binding ElementName=ScaleZ, Path=Value}" />
<Run Text=" Tag value" />
<Run Text="{Binding ElementName=ScaleZ, Path=Tag}" />
<Run Text=" Zindex value" />
<Run Text="{Binding ElementName=View3D_1, Path=(Panel.ZIndex)}" />
</TextBlock>
<TextBlock>
<Run Text=" View3D_2 透明度" />
<Run Text="{Binding ElementName=View3D_2, Path=Opacity}" />
<Run Text=" View3D_2 value" />
<Run Text="{Binding ElementName=ScaleZ_2, Path=Value}" />
<Run Text=" Zindex value" />
<Run Text="{Binding ElementName=View3D_2, Path=(Panel.ZIndex)}" />
</TextBlock>
</StackPanel>
</Grid>
In this way, we don't need any C#code to achieve this effect.

I must admit that I didn't do very well in the first 0 seconds, with a slight double image, so there is still a lot to learn.