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模型制作,總結一點就是由左到右開始。
1.Positions
3D模型是三角組成,所以描述一個物體的樣子時候,盡可能畫三角形。每個點就是頂點,如果存在相同的頂點,選擇一個就行。
描述頂點的時候是XYZ,當你正面看物體,物體的Z軸是指向你的方向為正方向。Y軸是向上為正,X軸是向右為正。
這里值得注意的是,為了方便 ,建議使用左 左下 右下 右上的順序去定義點。
這個模型的頂點定義就是 P1(x,y,z), P2(x,y,z) ,P3(x,y,z) ,P4(x,y,z)
2.TriangleIndices
這個就是 描述 這些頂點是如何組成三角形。當我們認為一個面是正面時,要記得逆時針定義頂點集合下標。頂點集合就是Positions,內容就是你輸入的坐標點,下標由0開始。
背面為順時針。
3.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秒時做的不是很好,略有重影,所以還有很多地方需要學習。