3D 穿梭效果?使用 UWP 也能搞定

Posted dino.c

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了3D 穿梭效果?使用 UWP 也能搞定相关的知识,希望对你有一定的参考价值。

昨天 ChokCoco 大佬搞了个 3D 穿梭效果出来,具体可见这里:

3D 穿梭效果?使用 CSS 轻松搞定

这个效果太神奇了,他还问我能不能用 WPF 搞出来,因为我完全没用过 WPF 的 3D,我第一反应是“这太难为我了”。

晚上回家吃饭溜娃打打帝国时代 4,突然想起我很久没有宠幸 UWP 了。一股“吾有上将 UWP,可搞定 3D 穿梭效果”的豪气油然而生。

于是就把这动画效果造出来了。

总的来说,实现 3D 穿梭的原理是靠改变 CSS 中的 perspective 产生透视效果。perspective指定了观察者与 z=0 平面的距离,使具有三维位置变换的元素产生透视效果。它的值越小,视角越深。

perspective 的具体用法可见此文档:

perspective - CSS(层叠样式表) _ MDN

与之对应,UWP 中提供了 PerspectiveTransform3D 类,它的 Depth 属性 也有类似的效果,当 Depth 越小,视觉越深,与平面相交的对象就越变形:

了解原理后马上开工。首先在 Xaml 中将 Grid 的大小写死为 300,然后将 PerspectiveTransform3D 的 Depth 设得很小:

<Grid Height="300" Width="300">
    <Grid.Transform3D>
        <media3D:PerspectiveTransform3D Depth="2" />
    </Grid.Transform3D>

然后从 ChokCoco 大佬那里搞到张星空图片,放在 Grid 里:

然后将 CompositeTransform3D 的 RotationY 设为 -90,这时候图片就扭曲起来了:

<media3D:CompositeTransform3D RotationY="-90" />

然后再设置 TranslateZ="100",让图片向外拉伸:

<media3D:CompositeTransform3D RotationY="-90" TranslateZ="100" />

搞定了一个方向后,所有方向都大致这样操作,只是改变 Rotation 和 Translate 还有中心点,就完成了一张静态的 3D 穿梭图:

<media3D:CompositeTransform3D x:Name="TransformLeft" x:Key="TransformLeft" RotationY="-90" TranslateZ="100" />
<media3D:CompositeTransform3D x:Name="TransformUp" x:Key="TransformUp" RotationX="90" TranslateZ="50" />
<media3D:CompositeTransform3D x:Name="TransformRight" x:Key="TransformRight" RotationY="90" CenterX="300" TranslateZ="50"/>
<media3D:CompositeTransform3D x:Name="TransformDown" x:Key="TransformDown" RotationX="-90" CenterY="300" TranslateZ="50" />

<Grid Background="{StaticResource ImageBackground}" Transform3D="{StaticResource TransformLeft}" />
<Grid Background="{StaticResource ImageBackground}" Transform3D="{StaticResource TransformUp}" />
<Grid Background="{StaticResource ImageBackground}" Transform3D="{StaticResource TransformRight}" />
<Grid Background="{StaticResource ImageBackground}" Transform3D="{StaticResource TransformDown}" />

下一步就是要让这四张图片动起来。这简单,用最基本的 DoubleAnimation 操作TranslateZ 从 10 变到 200:

<Storyboard x:Name="Move" x:Key="Move" RepeatBehavior="Forever">
    <DoubleAnimation Storyboard.TargetName="TransformLeft" Storyboard.TargetProperty="TranslateZ" From="10" To="200" Duration="0:0:8"/>
    <DoubleAnimation Storyboard.TargetName="TransformUp" Storyboard.TargetProperty="TranslateZ" From="10" To="200" Duration="0:0:8"/>
    <DoubleAnimation Storyboard.TargetName="TransformRight" Storyboard.TargetProperty="TranslateZ" From="10" To="200" Duration="0:0:8"/>
    <DoubleAnimation Storyboard.TargetName="TransformDown" Storyboard.TargetProperty="TranslateZ" From="10" To="200" Duration="0:0:8"/>
</Storyboard>

这时候基本的动画就已经实现了,但是没办法做到首尾相连,所以先把之前的成果封装成一个控件:

然后给它加上透明度变化的动画:

<Storyboard x:Name="Fade" RepeatBehavior="Forever">
    <DoubleAnimationUsingKeyFrames  Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" Duration="0:0:8"  >
        <LinearDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
        <LinearDoubleKeyFrame KeyTime="0:0:2" Value="1"/>
        <LinearDoubleKeyFrame KeyTime="0:0:4.8" Value="1"/>
        <LinearDoubleKeyFrame KeyTime="0:0:8" Value="0"/>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

通过叠加两个 GalaxyShuttleControl ,并且控制它们动画开始的时间,互相掩盖开头和结尾动画衔接不上的问题:

public TimeSpan Delay { get; set; }

private async void GalaxyShettleControl_Loaded(object sender, RoutedEventArgs e)
{
    await Task.Delay(Delay);
    Move.Begin();
    Fade.Begin();
}
<galaxyshuttles:GalaxyShuttleControl Delay="0:0:4" />
<galaxyshuttles:GalaxyShuttleControl/>

这样 3D 穿梭效果就实现了。

最后还差一点,ChokCoco 大佬的动画里加上了 hueRotate ,让颜色一直变化。UWP 里也可以使用 HueRotationEffect 实现这点,不过它的 Angle 的值范围是 0 到 2 * Math.Pi。要实现它的动画可以试试 Windows Community Toolkit 里的 PipelineVisualFactoryAnimationSet,这两个工具可以用来处理很复杂的效果和动画,用在这里反而大材小用:

<media:UIElementExtensions.VisualFactory>
    <media:PipelineVisualFactory>
        <media:HueRotationEffect x:Name="HueRotationEffect" IsAnimatable="True"/>
    </media:PipelineVisualFactory>
</media:UIElementExtensions.VisualFactory>
<animations:Explicit.Animations>
    <animations:AnimationSet x:Name="HueAnimation" >
        <animations:AnimationScope>
            <animations:HueRotationEffectAnimation From="0" To="6.28318530718" Target="{Binding ElementName=HueRotationEffect}"  Repeat="Forever" Duration="0:0:28"/>
        </animations:AnimationScope>
    </animations:AnimationSet>
</animations:Explicit.Animations>

最终实现的效果如下:

满足了好奇心后,下一步(自己生成图片)就不玩了。3D 穿梭动画实现起来不算难,最难的部分是 ChokCoco 大佬提供的创意,期待 ChokCoco 大佬下次再有别的动画让我抄来玩。

最后说一句,虽然用 MAIU 或 WinUI3 应该都能搞,而且这两个技术听起来更时髦些,可惜它们还没发布正式版,我还是趁现在多陪陪 UWP 好了。

源码: https://github.com/DinoChan/uwp_design_and_animation_lab


作者:dino.c
出处:http://www.cnblogs.com/dino623/
说明:欢迎转载并请标明来源和作者。如有错漏请指出,谢谢。

3D 穿梭效果?使用 CSS 轻松搞定

背景

周末在家习惯性登陆 Apex,准备玩几盘。在登陆加速器的过程中,发现加速器到期了。

我一直用的腾讯网游加速器,然而点击充值按钮,提示最近客户端升级改造,暂不支持充值(这个操作把我震惊了~)。只能转头下载网易 UU 加速器

打开 UU 加速器首页,映入眼帘的是这样一幅画面:

瞬间,被它这个背景图吸引。

出于对 CSS 的敏感,盲猜了一波这个用 CSS 实现的,至少也应该是 Canvas。打开控制台,稍微有点点失望,居然是一个 .mp4文件:

再看看 Network 面板,这个 .mp4 文件居然需要 3.5M?

emm,瞬间不想打游戏了。这么个背景图,CSS 不能搞定么

使用 CSS 3D 实现星际 3D 穿梭效果

这个技巧,我在 奇思妙想 CSS 3D 动画 | 仅使用 CSS 能制作出多惊艳的动画? 也有提及过,感兴趣的可以一并看看。

假设我们有这样一张图形:

这张图先放着备用。在使用这张图之前,我们会先绘制这样一个图形:

<div class="g-container">
  <div class="g-group">
      <div class="item item-right"></div>
      <div class="item item-left"></div>   
      <div class="item item-top"></div>
      <div class="item item-bottom"></div> 
      <div class="item item-middle"></div>    
  </div>
</div>
body {
  background: #000;
}
.g-container {
  position: relative;
}
.g-group {
  position: absolute;
  width: 100px;
  height: 100px;
  left: -50px;
  top: -50px;
  transform-style: preserve-3d;
}
.item {
  position: absolute;
  width: 100%;
  height: 100%;
  background: rgba(255, 255, 255, .5);
}
.item-right {
  background: red;
  transform: rotateY(90deg) translateZ(50px);
}
.item-left {
  background: green;
  transform: rotateY(-90deg) translateZ(50px);
}
.item-top {
  background: blue;
  transform: rotateX(90deg) translateZ(50px);
}
.item-bottom {
  background: deeppink;
  transform: rotateX(-90deg) translateZ(50px);
}
.item-middle {
  background: rgba(255, 255, 255, 0.5);
  transform: rotateX(180deg) translateZ(50px);
}

一共设置了 5 个子元素,不过仔细看 CSS 代码,其中 4 个子元素都设置了 rotateX/Y(90deg/-90deg),也就是绕 X 轴或者 Y 轴旋转了 90°,在视觉上是垂直屏幕的一张平面,所以直观视觉上我们是不到的,只能看到一个平面 .item-middle

我将 5 个子 item 设置了不同的背景色,结果如下:

现在看来,好像平平无奇,确实也是。

不过,见证奇迹的时候来了,此时,我们给父元素 .g-container 设置一个极小的 perspective,譬如,设置一个 perspective: 4px,看看效果:

.g-container {
  position: relative;
+ perspective: 4px;
}
// ...其余样式保持不变

此时,画风骤变,整个效果就变成了这样:

由于 perspective 生效,原本的平面效果变成了 3D 的效果。接下来,我们使用上面准备好的星空图,替换一下上面的背景颜色,全部都换成同一张图,神奇的事情发生了:

由于设置的 perspective 非常之小,而每个 item 的 transform: translateZ(50px) 设置的又比较大,所以图片在视觉上被拉伸的非常厉害。但是整体是充满整个屏幕的。

接下来,我们只需要让视角动起来,给父元素增加一个动画,通过控制父元素的 translateZ() 进行变化即可:

.g-container{
  position: relative;
  perspective: 4px;
  perspective-origin: 50% 50%;
}

.g-group{
  position: absolute;
  // ... 一些定位高宽代码
  transform-style: preserve-3d;
  + animation: move 8s infinite linear;
}

@keyframes move {
  0%{
    transform: translateZ(-50px) rotate(0deg);
  }
  100%{
    transform: translateZ(50px) rotate(0deg);
  }
}

看看,神奇美妙的星空穿梭的效果就出来了,Amazing:

美中不足之处在于,动画没能无限衔接上,开头和结尾都有很大的问题。

当然,这难不倒我们,我们可以:

  1. 通过叠加两组同样的效果,一组比另一组通过负的 animation-delay 提前行进,使两组动画衔接起来(一组结束的时候另外一组还在行进中)
  2. 再通过透明度的变化,隐藏掉 item-middle 迎面飞来的突兀感
  3. 最后,可以通过父元素的滤镜 hue-rotate 控制图片的颜色变化

我们尝试修改 HTML 结构如下:

<div class="g-container">
  <div class="g-group">
      <div class="item item-right"></div>
      <div class="item item-left"></div>   
      <div class="item item-top"></div>
      <div class="item item-bottom"></div> 
      <div class="item item-middle"></div>    
  </div>
  <!-- 增加一组动画 -->
  <div class="g-group">
      <div class="item item-right"></div>
      <div class="item item-left"></div>   
      <div class="item item-top"></div>
      <div class="item item-bottom"></div>   
      <div class="item item-middle"></div>    
  </div>
</div>

修改后的核心 CSS 如下:

.g-container{
  perspective: 4px;
  position: relative;
  // hue-rotate 变化动画,可以让图片颜色一直变换
  animation: hueRotate 21s infinite linear;
}

.g-group{
  transform-style: preserve-3d;
  animation: move 12s infinite linear;
}
// 设置负的 animation-delay,让第二组动画提前进行
.g-group:nth-child(2){
  animation: move 12s infinite linear;
  animation-delay: -6s;
}
.item {
  background: url(https://z3.ax1x.com/2021/08/20/fLwuMd.jpg);
  background-size: cover;
  opacity: 1;
  // 子元素的透明度变化,减少动画衔接时候的突兀感
  animation: fade 12s infinite linear;
  animation-delay: 0;
}
.g-group:nth-child(2) .item {
  animation-delay: -6s;
}
@keyframes move {
  0%{
    transform: translateZ(-500px) rotate(0deg);
  }
  100%{
    transform: translateZ(500px) rotate(0deg);
  }
}
@keyframes fade {
  0%{
    opacity: 0;
  }
  25%,
  60%{
    opacity: 1;
  }
  100%{
    opacity: 0;
  }
}
@keyframes hueRotate {
  0% {
    filter: hue-rotate(0);
  }
  100% {
    filter: hue-rotate(360deg);
  }
}

最终完整的效果如下,星空穿梭的效果,整个动画首尾相连,可以一直无限下去,几乎没有破绽,非常的赞:

上述的完整代码,你可以猛击这里:CSS 灵感 -- 3D 宇宙时空穿梭效果

这样,我们就基本还原了上述见到的网易 UU 加速器首页的动图背景。

更进一步,一个图片我都不想用

当然,这里还是会有读者吐槽,你这里不也用了一张图片资源么?没有那张星空图行不行?这张图我也懒得去找。

当然可以,CSS YYDS。这里我们尝试使用 box-shadow,去替换实际的星空图,也是在一个 div 标签内实现,借助了 SASS 的循环函数:

<div></div>
@function randomNum($max, $min: 0, $u: 1) {
	@return ($min + random($max)) * $u;
}

@function randomColor() {
    @return rgb(randomNum(255), randomNum(255), randomNum(255));
}

@function shadowSet($maxWidth, $maxHeight, $count) {
    $shadow : 0 0 0 0 randomColor();
    
    @for $i from 0 through $count {         
        $x: #{random(10000) / 10000 * $maxWidth};
        $y: #{random(10000) / 10000 * $maxHeight};

        
        $shadow: $shadow, #{$x} #{$y} 0 #{random(5)}px randomColor();
    }
    
    @return $shadow;
}

body {
    background: #000;
}

div {
    width: 1px;
    height: 1px;
    border-radius: 50%;
    box-shadow: shadowSet(100vw, 100vh, 500);
}

这里,我们用 SASS 封装了一个函数,利用多重 box-shadow 的特性,在传入的大小的高宽内,生成传入个数的点。

这样,我们可以得到这样一幅图,用于替换实际的星空图:

我们再把上述这个图,替换实际的星空图,主要是替换 .item 这个 class,只列出修改的部分:

// 原 CSS,使用了一张星空图
.item {
  position: absolute;
  width: 100%;
  height: 100%;
  background: url(https://z3.ax1x.com/2021/08/20/fLwuMd.jpg);
  background-size: cover;
  animation: fade 12s infinite linear;
}

// 修改后的 CSS 代码
.item {
  position: absolute;
  width: 100%;
  height: 100%;
  background: #000;
  animation: fade 12s infinite linear;
}
.item::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 1px;
  height: 1px;
  border-radius: 50%;
  box-shadow: shadowSet(100vw, 100vh, 500);
}

这样,我们就实现了这样一个效果,在不借助额外资源的情况下,使用纯 CSS 实现上述效果:

CodePen Demo -- Pure CSS Galaxy Shuttle 2

通过调整动画的时间,perspective 的值,每组元素的 translateZ() 变化距离,可以得到各种不一样的观感和效果,感兴趣的读者可以基于我上述给的 DEMO 自己尝试尝试。

最后

好了,本文到此结束,希望本文对你有所帮助

以上是关于3D 穿梭效果?使用 UWP 也能搞定的主要内容,如果未能解决你的问题,请参考以下文章

[UWP]使用PointLight并实现动画效果

[UWP]使用PointLight并实现动画效果

滤镜也能复制粘贴?视频编辑服务专属滤镜一键搞定

UNITY3D添加了刚体后,移动不成问题,也能实现CS效果,但是跳这个就出问题了,只能立定跳,不能边走边跳

啥?Flutter也能整3D了吗?我靠,竟然是这样的操作

啥?Flutter也能整3D了吗?我靠,竟然是这样的操作