css 效果博客地址:
3D 穿梭效果?使用 CSS 轻松搞定 - ChokCoco - 博客园 (cnblogs.com)
uwp 效果博客地址
3D 穿梭效果?使用 UWP 也能搞定 - dino.c - 博客园 (cnblogs.com)
大佬們真實登峰造極啊。
真是能學到很多東西啊。
我也就湊湊熱鬧,用 wpf 來試試,效果不算太好,勉強過得去吧。

相對 css 和 uwp,老牌的 wpf 對於 3d 效果的操作是相對繁瑣的。要首先做出 3d 模型,接著旋轉 xy 軸,其次調整 z 軸拉伸,最後一步調整 fov。穿梭效果要靠這 z 軸拉伸的數字決定,圖中是 10-20。
當然最難的是動畫的首尾銜接。
我們來一步一步的做。
製作 3d 模型
這一塊可以直接用不用 2d 控制項其實沒啥可以研究,所以就直接是 3d 模型,讓圖片以貼圖的形式存在。
至於 3d 模型製作,總結一點就是由左到右開始。
- Positions
3d 模型是三角組成,所以描述一個物體的樣子時候,儘可能畫三角形。每個點就是頂點,如果存在相同的頂點,選擇一個就行。
描述頂點的時候是 xyz,當你正面看物體,物體的 z 軸是指向你的方向為正方向。y 軸是向上為正,x 軸是向右為正。

這裡值得注意的是,為了方便 ,建議使用左 左下 右下 右上的順序去定義點。

這個模型的頂點定義就是 p1(x,y,z), p2(x,y,z) ,p3(x,y,z) ,p4(x,y,z)
- TriangleIndices
這個就是 描述 這些頂點是如何組成三角形。當我們認為一個面是正面時,要記得逆時針定義頂點集合下標。頂點集合就是 positions,內容就是你輸入的坐標點,下標由 0 開始。
背面為順時針。
- TextureCoordinates
這個是材質的定位順序,這個比較 2d 坐標系,和其他的不同。也就是左上角為 0,0 右下角為 1,1,也就是普通的屏幕坐標系
做好模型,要做四個哦。

旋轉 3d 模型
這個就比較有意思了,上面模型做好了四個,我們要組成四個面,也就是旋轉 y 軸,旋轉 x 軸,但是方向是不一樣的。旋轉我們使用 transform 就可以了。

看起來還行。
我們來做四個圖片並試試。
方便調試,我們帶著進度條。
資源
<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}"
/>
運行,看看

看起來,有點味道了。
剩下就是調試動畫了。
我們想要首尾銜接的動畫,實際上就是要兩段動畫看不出首尾的區別。參考大佬們的基本思路,依靠透明度,延遲播放讓首尾看不出區別。

也就是說要

這裡呢,我們純用 xaml 來實現這個方案。
首先要解決定時問題,首先頁面加載時,我們通過動畫修改某個對象的某個值 ,這個對象上存在一個觸發器,監控這個對象並開始定時,之後播放動畫。
修改值,我們要用 objectanimationusingkeyframes 動畫。
觸發器,數值觸發器,因為可以綁定。
整個過程中,最重要的就是時間線,兩組動畫再何時出現,何時隱藏。

最終代碼
<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>
這樣呢,我們就不用任何 c#代碼來完成這個效果了。

必須承認,在開頭 0 秒時做的不是很好,略有重影,所以還有很多地方需要學習。