【问题标题】:How can I create pure CSS 3-dimensional spheres?如何创建纯 CSS 3 维球体?
【发布时间】:2017-12-27 12:53:17
【问题描述】:

tl;dr: 我想用 CSS 创建一个实际的 3d 球体 - 而不仅仅是幻觉

注意:一些 sn-p 示例没有响应。请使用全屏。


使用纯 CSS,您可以像这样创建和动画 3d 立方体:

#cube-wrapper {
  position: absolute;
  left: 50%;
  top: 50%;
  perspective: 1500px;
}

.cube {
  position: relative;
  transform-style: preserve-3d;
  animation-name: rotate;
  animation-duration: 30s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

@keyframes rotate {
  0% {
    transform: rotate3d(0, 0, 0, 0);
  }
  100% {
    transform: rotate3d(0, 1, 0, 360deg);
    ;
  }
}

.face {
  position: absolute;
  width: 200px;
  height: 200px;
  border: solid green 3px;
}

#front_face {
  transform: translateX(-100px) translateY(-100px) translateZ(100px);
  background: rgba(255, 0, 0, 0.5);
}

#back_face {
  transform: translateX(-100px) translateY(-100px) translateZ(-100px);
  background: rgba(255, 0, 255, 0.5);
}

#right_face {
  transform: translateY(-100px) rotateY(90deg);
  background: rgba(255, 255, 0, 0.5);
}

#left_face {
  transform: translateY(-100px) translateX(-200px) rotateY(90deg);
  background: rgba(0, 255, 0, 0.5);
}

#top_face {
  transform: translateX(-100px) translateY(-200px) rotateX(90deg);
  background: rgba(0, 255, 255, 0.5);
}

#bottom_face {
  transform: translateX(-100px) rotateX(90deg);
  background: rgba(255, 255, 255, 0.5);
}

.cube {
  transform: rotateX(90deg) rotateY(90deg);
}
<div id="cube-wrapper">
  <div class="cube">
    <div id="front_face" class="face"></div>
    <div id="right_face" class="face"></div>
    <div id="back_face" class="face"></div>
    <div id="left_face" class="face"></div>
    <div id="top_face" class="face"></div>
    <div id="bottom_face" class="face"></div>
  </div>
</div>

我想以同样的方式创建一个 3d 球体并为其设置动画。

所以...我得到的第一个想法是使用border-radius 并且...嗯...它不起作用。

#cube-wrapper {
  position: absolute;
  left: 50%;
  top: 50%;
  perspective: 1500px;
}

.cube {
  position: relative;
  transform-style: preserve-3d;
  animation-name: rotate;
  animation-duration: 30s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

@keyframes rotate {
  0% {
    transform: rotate3d(0, 0, 0, 0);
  }
  100% {
    transform: rotate3d(0, 1, 0, 360deg);
   
    ;
  }
}


.face {
  position: absolute;
  width: 200px;
  height: 200px;
  border: solid green 3px;
  border-radius: 100vw
}


 

#front_face {
  transform: translateX(-100px) translateY(-100px) translateZ(100px);
  background: rgba(255, 0, 0, 0.5);
}

#back_face {
  transform: translateX(-100px) translateY(-100px) translateZ(-100px);
  background: rgba(255, 0, 255, 0.5);
}

#right_face {
  transform: translateY(-100px) rotateY(90deg);
  background: rgba(255, 255, 0, 0.5);
}

#left_face {
  transform: translateY(-100px) translateX(-200px) rotateY(90deg);
  background: rgba(0, 255, 0, 0.5);
}

#top_face {
  transform: translateX(-100px) translateY(-200px) rotateX(90deg);
  background: rgba(0, 255, 255, 0.5);
}

#bottom_face {
  transform: translateX(-100px) rotateX(90deg);
  background: rgba(255, 255, 255, 0.5);
}

.cube {
  transform: rotateX(90deg) rotateY(90deg);
}
<div id="cube-wrapper">
  <div class="cube">
    <div id="front_face" class="face"></div>
    <div id="right_face" class="face"></div>
    <div id="back_face" class="face"></div>
    <div id="left_face" class="face"></div>
    <div id="top_face" class="face"></div>
    <div id="bottom_face" class="face"></div>
  </div>
</div>

所以,我重新考虑了我的方法并寻找不同的方法。

我看了:

然后我再次尝试...我得到的最好结果是过于复杂 3d 对象幻觉

像这样:

body {
  overflow: hidden;
  background: #333;
}

.wrapper {
  margin: 1em;
  animation-duration: 20s;
}

.planet,
.planet:before,
.planet:after {
  height: 300px;
  width: 300px;
  border-radius: 100vw;
  will-change: transform;
  margin: 0 auto;
}

.planet {
  box-shadow: inset 0px 0px 10px 10px rgba(0, 0, 0, 0.4);
  position: relative;
}

.wrapper,
.planet,
.planet:before {
  animation-name: myrotate;
  animation-duration: 20s;
}

.wrapper,
.planet,
.planet:before,
.planet:after {
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

.planet:before,
.planet:after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
}

.planet:before {
  box-shadow: inset 20px 20px 100px 00px rgba(0, 0, 0, .5), 0px 0px 5px 3px rgba(0, 0, 0, .1);
}

.planet:after {
  filter: saturate(2.5);
  background: linear-gradient(rgba(0, 0, 0, 1), transparent), url("https://i.stack.imgur.com/eDYPN.jpg");
  opacity: 0.3;
  box-shadow: inset -20px -20px 14px 2px rgba(0, 0, 0, .2);
  animation-name: myopacity;
  animation-duration: 5000000s;
}

@keyframes myrotate {
  0% {
    transform: rotatez(0deg);
  }
  100% {
    transform: rotatez(360deg);
  }
}

@keyframes myopacity {
  0% {
    background-position: 0px;
    transform: rotatez(0deg);
  }
  50% {
    background-position: 100000000px;
  }
  100% {
    background-position: 0;
    transform: rotatez(-360deg);
  }
}
<div class="wrapper">
  <div class="planet"></div>
</div>

还有这个:

body {
  background: #131418;
}

.wrapper {
  margin: 1em;
  max-width: 100%;
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

.planet,
.planet:before,
.planet:after {
  height: 500px;
  width: 500px;
  max-height: 30vw;
  max-width: 30vw;
  border-radius: 100vw;
  will-change: transform;
}

.planet {
  box-shadow: inset 0px 0px 100px 10px rgba(0, 0, 0, .5);
  position: relative;
  float: left;
  margin: 0 2em;
}

.planet,
.planet:before,
.planet:after {
  animation-name: myrotate;
  animation-duration: 10s;
}

.wrapper,
.planet,
.planet:before,
.planet:after {
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

.planet:before,
.planet:after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
}

.planet:before {
  box-shadow: inset 50px 100px 50px 0 rgba(0, 0, 0, .5), 0 0 50px 3px rgba(0, 0, 0, .25);
  background-image: -webkit-radial-gradient( top, circle cover, #ffffff 0%, #000000 80%);
  opacity: .5;
}

.planet:after {
  opacity: .3;
  background-image: -webkit-radial-gradient( bottom, circle, #ffffff 0%, #000000 -200%);
  box-shadow: inset 0px 0px 100px 50px rgba(0, 0, 0, .5);
}

@keyframes myrotate {
  0% {
    transform: rotatez(0deg);
  }
  100% {
    transform: rotatez(-360deg);
  }
}

.bg {
  background: wheat;
}
<div class="wrapper">
  <div class="planet bg"></div>
</div>

这没关系,直到您尝试在 x 轴y 轴 上像我的第一个示例中的立方体一样实际旋转它们...这就是然后发生:(简化示例)

.sphere {
  background: black;
  width: 300px;
  height: 300px;
  border-radius: 100vw;
  animation: myrotate 10s linear infinite
}

@keyframes myrotate {
  0% {
    transform: rotate3d(0, 0, 0, 0);
  }
  100% {
    transform: rotate3d(0, 1, 0, 360deg);
  }
}
&lt;div class="sphere"&gt;&lt;/div&gt;

你得到的只是一个扁平的 2d 对象 - 考虑到它就是元素,这是预期的


我发现的最接近的东西是在tutorial by Timo Korinth 中创建的以下形状

@-webkit-keyframes animateWorld {
  0% {
    -webkit-transform: rotateY(0deg) rotateX(0deg) rotateZ(0deg);
  }
  50% {
    -webkit-transform: rotateY(360deg) rotateX(180deg) rotateZ(180deg);
  }
  100% {
    -webkit-transform: rotateY(720deg) rotateX(360deg) rotateZ(360deg);
  }
}

html {
  background: #FFFFFF;
}

. world {
  -webkit-perspective: 1000px;
}

.cube {
  margin-left: auto;
  margin-right: auto;
  position: relative;
  width: 200px;
  height: 200px;
  -webkit-transform-style: preserve-3d;
  -webkit-animation-name: animateWorld;
  -webkit-animation-duration: 10s;
  -webkit-animation-iteration-count: infinite;
  -webkit-animation-timing-function: linear;
}

.circle {
  position: absolute;
  width: 100%;
  height: 100%;
  border: 2px dashed #009BC2;
  border-radius: 50%;
  opacity: 0.8;
  background: rgba(255, 255, 255, 0);
}

.zero {
  -webkit-transform: rotateX(90deg);
}

.two {
  -webkit-transform: rotateY(45deg);
}

.three {
  -webkit-transform: rotateY(90deg);
}

.four {
  -webkit-transform: rotateY(135deg);
}

.five {
  width: 173px;
  height: 173px;
  margin: 14px;
  -webkit-transform: rotateX(90deg) translateZ(50px);
}

.six {
  width: 173px;
  height: 173px;
  margin: 14px;
  -webkit-transform: rotateX(90deg) translateZ(-50px);
}
<div class="world">
  <div class="cube">
    <div class="circle zero"></div>
    <div class="circle one"></div>
    <div class="circle two"></div>
    <div class="circle three"></div>
    <div class="circle four"></div>
    <div class="circle five"></div>
    <div class="circle six"></div>
  </div>
</div>

这是我的

问题:

如何使用纯 CSS 创建一个实际的 3 维球体?更具体地说,它被覆盖——不仅仅是一个框架——并且不涉及数百个 html 元素。


注意事项:

  1. 三维球体具有高度、宽度和深度 - 只是 就像我的第一个示例 sn-p 中的立方体一样
  2. 我不需要任何物理,也不需要任何 用户交互。只是一个动画旋转球体。

其他资源:

  1. paulrhayes.com - Spheres
  2. 3d (2d illusion) Earth with Rotating Animation with CSS
  3. Interactive CSS sphere

【问题讨论】:

  • 这有什么帮助吗? paulrhayes.com/experiments/sphere
  • 你想用这个球体做什么?它应该有什么行为,只是旋转或应用物理?
  • 这是不可能的。 HTML/CSS 只能操作平面。立方体很简单,它只有 6 个面(平面)。球体没有平面,所以你能做的最好的就是用许多模拟球体整体形状的小平面来模拟它。使用很少的刻面,您会得到一个非常粗糙的球体,并且随着添加刻面,它会变得更平滑。甚至 WebGL(或它的桌面等价物)也是这样工作的。唯一的选择是将您的球体投影到一个平面上,但是您不能直接在 3D 中操作它,您必须在移动/旋转球体时重新计算投影。
  • @Rob,理论上,您可以使用单像素框(元素或伪元素)及其大量box-shadows 绘制任何像素矩阵图像具有不同的偏移量和颜色。所以 CSS 可以 用作一种(非常缓慢和无效,但仍然......)图形绘制工具:)
  • @Ihazkode 我添加了css-shapes,因为您要求使用css 和css-transforms 创建形状,因为所有答案都在使用它,我想不可能避免使用它来创建3d 球体错觉.

标签: css css-animations css-shapes css-transforms


【解决方案1】:

严格来说,任何平面屏幕上的“3D”形状更像是3D对象的错觉。我们所看到的只是该形状在屏幕平面上的 2D 投影,我们的大脑会尽力猜测哪种形状可以给出我们看到的投影。如果投影发生变化,我们的大脑会将其解释为 3D 对象改变其方向,这有助于它更好地确定该对象的形状。

它适用于非对称对象和由多边形(例如立方体)制成的对象,但球体是一个非常特殊的情况:它在平面上的投影总是只给出一个圆。静止的球体和旋转的球体具有相同的投影,相同的圆。即使在现实生活中,如果我们观察一个表面均匀且没有任何标记的球体(例如,抛光的金属球),也很难确定它是静止还是旋转。我们的眼睛需要一些提示,一些根据球体的几何形状沿着球体表面移动的细节。此类细节从球面上的点以您期望的方式移动越多,旋转球体的感知(嗯,错觉)就越清晰。

这是制作一个能够提供这种感知的 CSS 场景的问题的关键:为了使这种错觉足够强烈,我们需要许多标记沿着位于不同平面的路径移动。在 CSS 中实现这一点的唯一方法是将每个标记作为一个单独的 CSS 框(元素或伪元素)。如果我们的球体由移动标记组成,我们确实需要其中的许多才能将其视为一个球体——因此在您看到的大多数演示中都有“数百个元素”。

因此,如果您想用相当少的元素使球体看起来更逼真,您可能需要结合产生静态基本球形“幻觉”的效果(具有径向渐变的圆形、内部阴影等)具有一些相对较小的元素(以使其实际上是平坦的不太明显),沿球体表面定向并进行 3D 变换,并进行动画处理 - 与第一个立方体的面基本相同演示。

以下是我自己将这种方法付诸实践的尝试。我使用了 20 个圆形元素,它们的方向大致为regular icosahedron 的面(就像经典足球上的白色六边形)。为了方便起见,我将它们分成两组,每组制作一个半球(这不是必需的,但它使样式更简单一些)。整个 3D 场景由球体本身和在球体中心附近穿过球体的背景框架(伪元素)组成(稍微靠近一点,以减少圆圈从近侧到远侧再返回时的“闪烁” ) 并且始终面向屏幕。所以总共有 24 个元素(至少不是字面上的“数百个”:)。为了使圆圈看起来更“鼓起”(如球面部分),我为每个圆圈添加了两个伪元素,并将它们稍微抬高。在 Chrome 和 Firefox 57+ 中效果最佳(在 Firefox 56- 和 iOS Safari 中,边缘附近仍有一些“闪烁”)。如果悬停圆圈,您可以看到没有背景框的场景(也没有“闪烁”)。 on Codepen 也提供了稍微修改的版本。

.scene {
  perspective: 400vmin;
  transform-style: preserve-3d;
  position: absolute;
  width: 80vmin;
  height: 80vmin;
  top: 10vmin;
  left: 10vmin;
}

.sphere {
  transform-style: preserve-3d;
  position: absolute;
  animation: rotate 20s infinite linear;
  width: 100%;
  height: 100%;
  transform-origin: 50% 50%;
  top: 0;
  left: 0;
}

.scene::before {
  content: '';
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0%;
  left: 0%;
  background: radial-gradient(circle farthest-corner at 33% 33%, rgba(240, 240, 220, 0.85) 0%, rgba(30, 30, 40, 0.85) 80%), radial-gradient(circle farthest-corner at 45% 45%, rgba(0, 0, 0, 0) 50%, #000000 80%);
  border-radius: 50%;
  transform: translateZ(2vmin);
}

.scene:hover::before {
  display: none;
}

.hemisphere {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transform-origin: 50% 50%;
  transform: rotateX(90deg);
}

.hemisphere:nth-child(2) {
  transform: rotateX(-90deg);
}

.face {
  position: absolute;
  width: 40vmin;
  height: 40vmin;
  background: radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.1) 48%, #ff0000 49%, #ff0000 50%, rgba(0, 0, 0, 0) 51%);
  transform-style: preserve-3d;
  transform-origin: 50% 0;
  top: 50%;
  left: 20vmin;
}

.face::before, .face::after {
  content: '';
  position: absolute;
  border-radius: 50%;
  box-sizing: border-box;
}

.face::before {
  width: 50%;
  height: 50%;
  top: 25%;
  left: 25%;
  border: 2px solid #333;
  background: rgba(255, 255, 255, 0.3);
  transform: translateZ(1.6vmin);
}

.face::after {
  width: 20%;
  height: 20%;
  top: 40%;
  left: 40%;
  background: rgba(0, 0, 0, 0.2);
  transform: translateZ(2.8vmin);
}

.face:nth-child(1) {
  transform: translateZ(-41.6vmin) rotateZ(36deg) translateY(-6.8vmin) rotateX(143deg);
}

.face:nth-child(2) {
  transform: translateZ(-41.6vmin) rotateZ(108deg) translateY(-6.8vmin) rotateX(143deg);
}

.face:nth-child(3) {
  transform: translateZ(-41.6vmin) rotateZ(180deg) translateY(-6.8vmin) rotateX(143deg);
}

.face:nth-child(4) {
  transform: translateZ(-41.6vmin) rotateZ(252deg) translateY(-6.8vmin) rotateX(143deg);
}

.face:nth-child(5) {
  transform: translateZ(-41.6vmin) rotateZ(-36deg) translateY(-6.8vmin) rotateX(143deg);
}

.face:nth-child(6) {
  transform: translateZ(-26.8vmin) rotateZ(36deg) translateY(-33.2vmin) rotateX(100deg);
}

.face:nth-child(7) {
  transform: translateZ(-26.8vmin) rotateZ(108deg) translateY(-33.2vmin) rotateX(100deg);
}

.face:nth-child(8) {
  transform: translateZ(-26.8vmin) rotateZ(180deg) translateY(-33.2vmin) rotateX(100deg);
}

.face:nth-child(9) {
  transform: translateZ(-26.8vmin) rotateZ(252deg) translateY(-33.2vmin) rotateX(100deg);
}

.face:nth-child(10) {
  transform: translateZ(-26.8vmin) rotateZ(-36deg) translateY(-33.2vmin) rotateX(100deg);
}

.face:nth-child(11) {
  transform: translateZ(-26.8vmin) rotateZ(36deg) translateY(-33.2vmin) rotateX(100deg);
}

@keyframes rotate {
  0% {
transform: rotateZ(25deg) rotateX(20deg) rotateY(0deg);
  }
  50% {
transform: rotateZ(-25deg) rotateX(-20deg) rotateY(180deg);
  }
  100% {
transform: rotateZ(25deg) rotateX(20deg) rotateY(360deg);
  }
}

body {
  background: #555;
  overflow: hidden;
}
<div class="scene">
  <div class="sphere">
    <div class="hemisphere">
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
    </div>
    <div class="hemisphere">
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
      <div class="face"></div>
    </div>
  </div>
</div>

【讨论】:

    【解决方案2】:

    如何使用纯 CSS 创建一个实际的 3 维球体?

    好吧,正如许多人在答案和 cmets 中所说的那样,目前根本不可能在浏览器中使用 html 和 css 创建单个 3D 实体,但可以创建 3D 对象的错觉。以下是我解决问题的方法。

    为了使人眼能够看到球形物体,眼睛需要参考点来跟随。在我的例子中,定义球体形状的是线条。线条是通过为 X 轴集合中的 5 个元素和 Y 轴集合中的 5 个元素设置边框来实现的。只有 X/Y 集被赋予了边界,因为仅这一点就提供了足够的参考来产生球体的错觉。 Z 轴上的附加线只是杂乱无章,没有必要。如果关闭所有线条,则整个物体看起来就像一个实心的“完美”球体(看起来像一个圆圈,但它的所有部分都在移动,并且呈现在浏览器的 3D 平面上!)。


    我做了什么:
    1. 创建了 15 个 html 元素,每个元素代表一个圆圈,分成 3 组,每组 5 个

      所有这些集合的原因是,当整个装置在 x、y、z 轴上旋转时,x、y、z 集合中每个元素的背景会相互填充空白空间。

    2. 每组 5 个元素分别以 36 度增量在 X、Y、Z 轴上旋转。
    3. 所有元素都使用border-radius:50%;进行四舍五入
    4. 将圆形元素的背景设置为纯色
    5. 将这些集合放在一起,使它们重叠
    6. 在容器上使用 clip-path: circle(96px at center); 剪裁了由于没有足够的元素覆盖 x、y、z 圆圈之间的空白空间而导致的微小间隙,并在容器上添加了一个很酷的阴影/灯光效果以密封交易
      vs

      更多的圆圈会产生一个不那么“前卫”的球体,但由于问题中强调了表现,整个事情的快速剪辑似乎是要做的事情


    作为一个结束的想法,我想表达我对提出的问题的赞赏,它真的让我思考并且是一个很棒的项目,让我学习了很多关于 html/css 的 3D 功能的东西。

    还要感谢所有花时间撬开这个问题并想出解决问题的好方法的人。

    我希望我的研究成果是有用的。干杯!

    This Pen is also based on Timo Korinth's example.

    * {
      margin: 0;
      padding: 0;
    }
    
    
    /* Rotate Sphere animation */
    
    @-webkit-keyframes animateSphere {
      0% {
        transform: rotateY(0deg) rotateX(0deg) rotateZ(0deg);
      }
      50% {
        transform: rotateY(360deg) rotateX(360deg) rotateZ(0deg);
      }
      100% {
        transform: rotateY(720deg) rotateX(720deg) rotateZ(0deg);
      }
    }
    
    html {
      background: black;
    }
    
    .scene {
      perspective: 1000px;
    }
    
    .container {
      margin-top: 5vh;
      margin-left: auto;
      margin-right: auto;
      position: relative;
      width: 200px;
      height: 200px;
      transform-style: preserve-3d;
      animation-name: animateSphere;
      animation-duration: 30s;
      animation-iteration-count: infinite;
      animation-timing-function: linear;
    }
    
    .border {
      border: 1px solid white;
    }
    
    .circle {
      position: absolute;
      width: 100%;
      height: 100%;
      border-radius: 50%;
      background: rgba(204, 0, 102, 1);
    }
    
    .circle:nth-child(1) {
      transform: rotate3d(1, 0, 0, 0deg);
    }
    
    .circle:nth-child(2) {
      transform: rotate3d(1, 0, 0, 36deg);
    }
    
    .circle:nth-child(3) {
      transform: rotate3d(1, 0, 0, 72deg);
    }
    
    .circle:nth-child(4) {
      transform: rotate3d(1, 0, 0, 108deg);
    }
    
    .circle:nth-child(5) {
      transform: rotate3d(1, 0, 0, 144deg);
    }
    
    
    /* 18! difference to align*/
    
    .circle:nth-child(6) {
      transform: rotate3d(0, 1, 0, 0deg);
    }
    
    .circle:nth-child(7) {
      transform: rotate3d(0, 1, 0, 36deg);
    }
    
    
    /* Upper and Lower circle */
    
    .circle:nth-child(8) {
      transform: rotate3d(0, 1, 0, 72deg);
    }
    
    .circle:nth-child(9) {
      transform: rotate3d(0, 1, 0, 108deg);
    }
    
    .circle:nth-child(10) {
      transform: rotate3d(0, 1, 0, 144deg);
    }
    
    .circle:nth-child(11) {
      transform: rotate3d(0, 1, 0, 90deg) rotate3d(1, 0, 0, 0deg);
    }
    
    .circle:nth-child(12) {
      transform: rotate3d(0, 1, 0, 90deg) rotate3d(1, 0, 0, 36deg);
    }
    
    
    /* Upper and Lower circle */
    
    .circle:nth-child(13) {
      transform: rotate3d(0, 1, 0, 90deg) rotate3d(1, 0, 0, 72deg);
    }
    
    .circle:nth-child(14) {
      transform: rotate3d(0, 1, 0, 90deg) rotate3d(1, 0, 0, 108deg);
    }
    
    .circle:nth-child(15) {
      transform: rotate3d(0, 1, 0, 90deg) rotate3d(1, 0, 0, 144deg);
    }
    
    .shadow {
      margin: auto;
      border-radius: 50%;
      width: 200px;
      height: 200px;
      box-shadow: 10px 1px 30px white;
    }
    
    
    /* Clip the sphere a bit*/
    
    .clip {
      clip-path: circle(96px at center);
    }
    <div class="scene">
      <div class="shadow">
        <div class="clip">
          <div class="container">
            <div class="circle border"></div>
            <div class="circle border"></div>
            <div class="circle border"></div>
            <div class="circle border"></div>
            <div class="circle border"></div>
            <div class="circle border"></div>
            <div class="circle border"></div>
            <div class="circle border"></div>
            <div class="circle border"></div>
            <div class="circle border"></div>
            <div class="circle"></div>
            <div class="circle"></div>
            <div class="circle"></div>
            <div class="circle"></div>
            <div class="circle"></div>
          </div>
        </div>
      </div>
    </div>

    【讨论】:

    • 很酷的方法,让我想起了旧的电子游戏:P 这样做的缺点是可视化实际的 3D 装饰。你会如何用这个来制作足球?您仍然需要在球的“表面”上显示图像。
    • 我已经有一段时间没有解决这个问题了!感谢您的评论 :) 我记得我在想一种方法来使用一些额外的元素来为这个结构创建一个“表面”。我决定不这样做的一个原因是,使用的元素数量是问题中定义的约束,另一个我找不到优雅的方法来做到这一点!也许在 4 年的网络和我自己前进之后,我再次打破了这个温暖的罐头:)
    • 这是最好的答案,恕我直言。它的实际 3d 特性使其能够很好地处理更大的场景,并进行更多的变换。
    【解决方案3】:

    正如上面已经提到的,CSS3 不能为您提供真正的 3d 形状,而只是提供幻觉。 使用最少的 HTML 元素并使用图像作为纹理的良好球体错觉可以通过将您所做的结合使用 CSS 阴影来完成。

    使用:after 伪元素可以在移动位置和更小的尺寸中创建额外的火花,这可以使蒙版更加逼真。成功效果的关键是记住不同的材料反射光的方式不同。这意味着如果您尝试创建金属球体,box-shadow 创建的照明将不同于塑料球体的照明。

    另一个不错的补充是使用:before 伪元素来创建反射效果。在具有一定不透明度的球体上添加预先制作的世界图像可以产生非常有说服力的效果。另请注意,您尝试创建的材质将决定您想要的反射不透明度量。

    注意,当 3d 变换应用其透视时,我使用八角棱镜让场景后面的图像看起来更圆。 即使只使用 8 个元素,结果也非常逼真。更真实的结果可以通过更多的多边形和更复杂的形状和纹理映射来完成,但即便如此,由于在一切之上添加了阴影和火花,因此不需要太多元素。

    最后,为了隐藏八角棱镜的其余部分并仅显示我正在使用的球体边界内的部分clip-path: circle(99px at center);

    body {
      width: 100%;
      height: 100%;
      background-color: #000; 
    }
    .cube-wrapper {
      width: 0;
      height: 0;
      top: 100px;
      left: 100px;
      position: absolute;
      perspective-origin: 0 0;
      perspective: 80px;
    }
    .cube-2 {
      transform: translateZ(-100px) scaleX(1.8);
      transform-style: preserve-3d;
    }
    .cube {
      top: -100px;
      position: relative;
      transform-style: preserve-3d;
      animation-name: rotate;
      animation-duration: 10s;
      animation-timing-function: linear;
      animation-iteration-count: infinite;
    }
    @keyframes rotate {
      0% {
        transform: rotate3d(0 0, 0, 360deg);
      }
      100% {
        transform: rotate3d(0, 1, 0, 360deg);
        ;
      }
    }
    
    .face {
      position: absolute;
      background-size: 662.4px 200px;
      width: 84px;
      height: 200px;
    }
    
    #face1 {
      transform: translateX(-41.4px) translateZ(100px);
      background-position: 0 0;
    }
    #face2 {
      transform: translateX(29.2px) translateZ(70.8px) rotateY(45deg);
      background-position: -82.8px 0;
    }
    #face3 {
      transform: translateX(58.5px) rotateY(90deg);
      background-position: -165.6px 0;
    }
    #face4 {
      transform: translateX(29.2px) translateZ(-70.8px) rotateY(135deg);
      background-position: -248.4px 0;
    }
    #face5 {
      transform: translateX(-41.4px) translateZ(-100px) rotateY(180deg);
      background-position: -331.2px 0;
    }
    #face6 {
      transform: translateX(-111.4px) translateZ(-70.8px) rotateY(225deg);
      background-position: -414px 0;
    }
    #face7 {
      transform: translateX(-141.4px) rotateY(270deg);
      background-position: -496.8px 0;
    }
    #face8 {
      transform: translateX(-111.4px) translateZ(70px) rotateY(315deg);
      background-position: -579.6px 0;
    }
    
    .circle {
      position: absolute;
      width: 200px;
      height: 200px;
      border-radius: 50%;
    }
    .clip-circle {
      position: absolute;
      padding: 0;
      top: -16px;
      width: 200px;
      height: 200px;
      border-radius: 50%;
      clip-path: circle(99px at center);
    }
    .lighting:after {
        content: '';
        position: absolute;
        top: 50px;
        left: 67px;
    }
    .reflection:before {
        content: '';
        position: absolute;
        top: 0px;
        left: 0px;
        height: 200px;
        width: 200px;
        background-image:url(https://i.stack.imgur.com/ayCw7.png);
        background-size: 200px 200px;
    }
    
    .earth {
      position: absolute;
      left: 20px;
    }
    .earth .face{
      background-image:url(https://i.stack.imgur.com/fdtNz.jpg);
    }
    .earth .clip-circle {
      transform: rotateX(7deg) rotateZ(15deg);
    }
    .earth .lighting {
      box-shadow: -20px -30px 55px 0 rgba(0, 0, 0, 0.9) inset, -75px -100px 150px 0 rgba(0, 0, 0, 0.4) inset, 75px 100px 200px 0 rgba(255, 255, 255, 0.2) inset, -1px -2px 10px 2px rgba(200, 190, 255, 0.2);
    }
    .earth .lighting:after {
        box-shadow: 0 0 150px 51px rgba(255, 255, 255, 0.2), 0 0 26px 10px rgba(255, 255, 255, 0.2);
    }
    
    .wood {
      position: absolute;
      left: 240px;
    }
    .wood .face{
      background-image:url(https://i.stack.imgur.com/sa5P8.jpg);
    }
    .wood .cube-wrapper {
      transform: rotateZ(45deg);
    }
    .wood .lighting {
      box-shadow: -20px -30px 90px 0 rgba(0, 0, 0, 0.7) inset, -75px -100px 140px 0 rgba(0, 0, 0, 0.6) inset;
    }
    .wood .lighting:after {
        box-shadow: 0 0 42px 15px rgba(255, 255, 255, 0.5);
    }
    .wood .reflection:before {
        opacity: 0.04;
    }
    
    .metal {
      position: absolute;
      left: 460px;
    }
    .metal .face{
      background-image:url(https://i.stack.imgur.com/PGmVN.jpg);
    }
    .metal .cube-wrapper {
      transform: rotateZ(-32deg);
    }
    .metal .lighting {
      box-shadow: -20px -30px 100px 0 rgba(0, 0, 0, 0.9) inset, -75px -100px 107px 0 rgba(0, 0, 0, 0.3) inset, 75px 100px 127px 0 rgba(255, 255, 255, 0.23) inset;
    }
    .metal .lighting:after {
        box-shadow: 0 0 42px 20px rgba(255, 255, 255, 0.7), 0 0 7px 6px rgba(255, 255, 255, 0.9);
    }
    .metal .reflection:before {
        opacity: 0.2;
    }
    <body>
      <div style="position:absolute;top:20px;">
        <div class="earth">
          <dir class="clip-circle">
            <div class="cube-wrapper">
              <div class="cube-2">
                <div class="cube">
                  <div id="face1" class="face"></div>
                  <div id="face2" class="face"></div>
                  <div id="face3" class="face"></div>
                  <div id="face4" class="face"></div>
                  <div id="face5" class="face"></div>
                  <div id="face6" class="face"></div>
                  <div id="face7" class="face"></div>
                  <div id="face8" class="face"></div>
                </div>
              </div>
            </div>
          </dir>
          <div class="circle lighting"></div>
        </div>
        <div class="wood">
          <dir class="clip-circle">
            <div class="cube-wrapper">
              <div class="cube-2">
                <div class="cube">
                  <div id="face1" class="face"></div>
                  <div id="face2" class="face"></div>
                  <div id="face3" class="face"></div>
                  <div id="face4" class="face"></div>
                  <div id="face5" class="face"></div>
                  <div id="face6" class="face"></div>
                  <div id="face7" class="face"></div>
                  <div id="face8" class="face"></div>
                </div>
              </div>
            </div>
          </dir>
          <div class="circle reflection lighting"></div>
        </div>
        <div class="metal">
          <dir class="clip-circle">
            <div class="cube-wrapper">
              <div class="cube-2">
                <div class="cube">
                  <div id="face1" class="face"></div>
                  <div id="face2" class="face"></div>
                  <div id="face3" class="face"></div>
                  <div id="face4" class="face"></div>
                  <div id="face5" class="face"></div>
                  <div id="face6" class="face"></div>
                  <div id="face7" class="face"></div>
                  <div id="face8" class="face"></div>
                </div>
              </div>
            </div>
          </dir>
          <div class="circle reflection lighting"></div>
        </div>
      </div>
      <div style="position:absolute;top:240px">
        <div class="earth">
          <div class="cube-wrapper">
            <div class="cube-2">
              <div class="cube">
                <div id="face1" class="face"></div>
                <div id="face2" class="face"></div>
                <div id="face3" class="face"></div>
                <div id="face4" class="face"></div>
                <div id="face5" class="face"></div>
                <div id="face6" class="face"></div>
                <div id="face7" class="face"></div>
                <div id="face8" class="face"></div>
              </div>
            </div>
          </div>
        </div>
        <div class="wood">
            <div class="cube-wrapper">
              <div class="cube-2">
                <div class="cube">
                  <div id="face1" class="face"></div>
                  <div id="face2" class="face"></div>
                  <div id="face3" class="face"></div>
                  <div id="face4" class="face"></div>
                  <div id="face5" class="face"></div>
                  <div id="face6" class="face"></div>
                  <div id="face7" class="face"></div>
                  <div id="face8" class="face"></div>
                </div>
              </div>
            </div>
        </div>
        <div class="metal">
            <div class="cube-wrapper">
              <div class="cube-2">
                <div class="cube">
                  <div id="face1" class="face"></div>
                  <div id="face2" class="face"></div>
                  <div id="face3" class="face"></div>
                  <div id="face4" class="face"></div>
                  <div id="face5" class="face"></div>
                  <div id="face6" class="face"></div>
                  <div id="face7" class="face"></div>
                  <div id="face8" class="face"></div>
                </div>
              </div>
            </div>
        </div>
      </div>
      <div style="position:absolute;top:460px;">
        <div class="earth">
          <div class="circle lighting"></div>
        </div>
        <div class="wood">
          <div class="circle reflection lighting"></div>
        </div>
        <div class="metal">
          <div class="circle reflection lighting"></div>
        </div>
      </div>
    
    </body>

    【讨论】:

      【解决方案4】:

      制作一个逼真的 3d css 球体,没有某种程度的 2d 幻觉,需要许多元素才能使其具有平滑的周边。

      但是,我制作了 Timo Korinth 示例的一个版本:

      • 剪切“背面”网格线
      • 通过移动径向渐变来近似球面着色

      只要重新计算着色动画就可以任意旋转。

      This page 有一些使用 CSS 实现球面着色的数学原理,可用于此目的。

      编辑:其他答案看起来更好,因此将其转换为死星

      .ball {
        position: absolute;
        top:0px;
        left:0px;
        width: 98vmin;
        height: 98vmin;
        margin: 1vmin;  
        transform-style: preserve-3d;  
        transform: rotateX(-5deg);
      }
      
      @keyframes rot{
        0% { transform: rotateY(0deg) rotateX(0deg) rotateZ(0deg); }
        100% { transform: rotateY(360deg) rotateX(0deg) rotateZ(0deg); }
      }
      
      .layer {
        position: absolute;
        top: 0px;
        left: 0px;
        width: 98vmin;
        height: 98vmin;
      }
      
      .moving
      {
        transform-style: preserve-3d;
        transform-origin: 49vmin 49vmin;
        animation: rot 10s linear infinite;
      }
      
      .gridplane {
        width: 97vmin;
        height: 97vmin;       
        border-radius: 50%;
        border: 0.5vmin dashed rgb(128, 128, 128);
      }
      
      .xline { transform: translateY(1%) rotateX(90deg); }
      .xline2 { transform: translateY(-1%) rotateX(90deg); }
      .yline { transform: rotateY(90deg); }
      .zline { transform: rotateZ(90deg); }
      
      .laser { 
        background-color: rgba(0, 0, 0, 0.05);
        transform:  translateX(-27.7128%) translateY(-27.7128%) rotateY(90deg) translateX(-27.7128%) rotateY(-135deg) rotateX(45deg) scale(0.3) translateY(-25%);
      }
      
      .laser2 { 
        background-color: rgba(0, 0, 0, 0.05);
        transform:  translateX(-27.0128%) translateY(-27.0128%) rotateY(90deg) translateX(-27.0128%) rotateY(-135deg) rotateX(45deg) scale(0.2) translateY(-35%);
      }
      
      .clip
      {
        border-radius: 50%;  
        overflow:hidden;
        transform: translateZ(-0vmin);
      }
      
      @keyframes highlightanim {     
        0.00% {left: -150.00%; top: -178.00% }
        12.50% {left: -117.67%; top: -179.64% }
        25.00% {left: -97.69%; top: -195.87% }
        28.75% {left: -95.00%; top: -207.09% }
        32.50% {left: -97.69%; top: -220.70% }
        40.00% {left: -117.67%; top: -240.01% }
        47.50% {left: -150.00%; top: -247.50% }
        55.00% {left: -182.33%; top: -240.01% }
        62.50% {left: -202.31%; top: -220.70% }
        68.75% {left: -205.00%; top: -207.09% }
        75.00% {left: -202.31%; top: -195.87% }
        87.50% {left: -182.33%; top: -179.64% }
        100.00% {left: -150.00%; top: -178.00% }
      }     
          
      .shade
      {
        position: relative;
        top: -150%;
        left: -150%;
        width: 400%;
        height: 400%;
        background: radial-gradient(at 50% 50%, white, black, grey, black, black);
        animation: highlightanim 10s linear infinite;
      }
      <div class='ball'>
        <div class='layer moving'>
          <div class='layer gridplane xline'></div>
          <div class='layer gridplane xline2'></div>
          <div class='layer gridplane yline'></div>   
          <div class='layer gridplane zline'></div>  
          <div class='layer gridplane laser'></div>  
          <div class='layer gridplane laser2'></div>  
        </div> 
        <div class='layer clip'>
          <div class='shade'> 
          </div>
        </div>
      </div>

      【讨论】:

        【解决方案5】:

        不,根据您的标准,这是不可能的。所有仅使用 HTML 和 CSS 的 3D 示例都存在性能问题,因为这不是它的目的。

        当涉及到繁重的图形效果时,HTML 和 CSS 真的很不擅长。

        制作真正的 3D 球体的最佳方法是使用 WebGL,它是一种用于创建 3D 内容的 JavaScript API。

        【讨论】:

        • @KanstantsinArlouski 公平地说,问题是“有可能......”所以这对我来说似乎是一个合理的答案。
        • 我同意你们所有人的看法。我的回答没有足够的信息来支持它,而且它有很大的偏见。不过,这个问题也有点抽象。 “真实”的 3d 球体是主观的,我不知道所有作者的标准。严格要求作者的问题,答案肯定是,不可能。即使是“真实”的 3D 也使用了许多元素,但与 HTML 标记不同的是,这些元素都是为此而设计的,并且考虑到了性能。
        • 这真的应该是一个评论或扩展以包含一些参考。 @sol "is it possible" 比其他任何东西都更具修辞性。 OP 并没有真正怀疑它在技术上是否可行,而只是礼貌地要求展示如何。否则,很多关于 SE 的问题都应该用不,不是,因为这不是一个好主意来回答。
        【解决方案6】:

        看看这个 - 听起来像您需要的,并且您可以根据自己的喜好编辑代码片段。 https://codepen.io/Mamboleoo/post/sphere-css

        HTML

        .mommy
        .daddy
          - for (var x = 1; x < 300; x++)
            span
        

        CSS

        @import "compass";
        
        body{
          margin: 0;
          display: flex;
          height: 100vh;
          overflow: hidden;
          justify-content: center;
          align-items: center;
          background:black;
        }
        
        .mommy{
          width: 500px;
          height: 500px;
          position: relative;
          perspective: 800px;
        }
        .daddy{
          width: 500px;
          height: 500px;
          transform-style: preserve-3d;
          animation : rotate 25s infinite linear;
        }
        span{
          display: inline-block;
          position: absolute;
          top:50%;
          left:50%;
          perspective: 800px;
          transform-style: preserve-3d;
          width: 0;
          height: 0;
          &:before{
            content:"";
            width: 4px;
            height: 4px;
            display: inline-block;
            position: absolute;
            top: calc(50% - 2px);
            left: calc(50% - 2px);
            background: currentColor;
            color: inherit;
            border-radius: 50%;
            animation: invertRotate 25s infinite linear, scale 2s infinite linear;
            box-shadow: 0 0 10px currentColor;
          }
        }
        
        $amount : 300;
        @for $i from 1 through $amount {
        
          $theta : ($i / $amount)*120;
          $phi : ($i / $amount) * pi();
          $x : 250 * sin($phi) * cos($theta);
          $y : 250 * sin($phi) * sin($theta);
          $z : 250 * cos($phi);
          .mommy span:nth-child(#{$i}){
            transform: translate3d($x+px, $y+px, $z+px);
            color: hsl(($i/$amount)*360,100%,50%);
            &:before{
              animation-delay: 0s, -($i/$amount)*2+s;
            }
          }  
        }
        
        @keyframes rotate{
          to{transform:rotateY(360deg);}
        }
        @keyframes invertRotate{
          to{transform:rotateY(-360deg);}
        }
        @keyframes scale{
          0%, 45%,55%{ box-shadow: 0 0 10px 0px  currentColor;}
          50%{ box-shadow: 0 0 10px 5px currentColor;}
        }
        

        【讨论】:

        • 你错过了...并且不涉及数百个html元素
        【解决方案7】:

        这里是一个动画球体/气泡的例子,虽然这个例子更多的是一种幻觉。我不知道你所要求的一切是否只能通过纯 css 实现,但我可能弄错了。

        .ball {
          display: inline-block;
          width: 100%;
          height: 100%;
          border-radius: 100%;
          position: relative;
          background: radial-gradient(circle at bottom, #81e8f6, #76deef 10%, #055194 80%, #062745 100%); }
          .ball:before {
            content: "";
            position: absolute;
            top: 1%;
            left: 5%;
            width: 90%;
            height: 90%;
            border-radius: 100%;
            background: radial-gradient(circle at top, white, rgba(255, 255, 255, 0) 58%);
            -webkit-filter: blur(5px);
            filter: blur(5px);
            z-index: 2; }
          .ball:after {
            content: "";
            position: absolute;
            display: none;
            top: 5%;
            left: 10%;
            width: 80%;
            height: 80%;
            border-radius: 100%;
            -webkit-filter: blur(1px);
            filter: blur(1px);
            z-index: 2;
            -webkit-transform: rotateZ(-30deg);
            transform: rotateZ(-30deg); }
          .ball .shadow {
            position: absolute;
            width: 100%;
            height: 100%;
            background: radial-gradient(circle, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.1) 40%, rgba(0, 0, 0, 0) 50%);
            -webkit-transform: rotateX(90deg) translateZ(-160px);
            transform: rotateX(90deg) translateZ(-160px);
            z-index: 1; }
          .ball.plain {
            background: black; }
            .ball.plain:before, .ball.plain:after {
              display: none; }
          .ball.bubble {
            background: radial-gradient(circle at 50% 55%, rgba(240, 245, 255, 0.9), rgba(240, 245, 255, 0.9) 40%, rgba(225, 238, 255, 0.8) 60%, rgba(43, 130, 255, 0.4));
            -webkit-animation: bubble-anim 2s ease-out infinite;
            animation: bubble-anim 2s ease-out infinite; }
            .ball.bubble:before {
              -webkit-filter: blur(0);
              filter: blur(0);
              height: 80%;
              width: 40%;
              background: radial-gradient(circle at 130% 130%, rgba(255, 255, 255, 0) 0, rgba(255, 255, 255, 0) 46%, rgba(255, 255, 255, 0.8) 50%, rgba(255, 255, 255, 0.8) 58%, rgba(255, 255, 255, 0) 60%, rgba(255, 255, 255, 0) 100%);
              -webkit-transform: translateX(131%) translateY(58%) rotateZ(168deg) rotateX(10deg);
              transform: translateX(131%) translateY(58%) rotateZ(168deg) rotateX(10deg); }
            .ball.bubble:after {
              display: block;
              background: radial-gradient(circle at 50% 80%, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0) 74%, white 80%, white 84%, rgba(255, 255, 255, 0) 100%); }
        
        .stage {
          width: 300px;
          height: 300px;
          display: inline-block;
          margin: 20px;
          -webkit-perspective: 1200px;
          -moz-perspective: 1200px;
          -ms-perspective: 1200px;
          -o-perspective: 1200px;
          perspective: 1200px;
          -webkit-perspective-origin: 50% 50%;
          -moz-perspective-origin: 50% 50%;
          -ms-perspective-origin: 50% 50%;
          -o-perspective-origin: 50% 50%;
          perspective-origin: 50% 50%;
        }
        body {
          width: 300px;
          margin: 20px auto;
          background: linear-gradient(to bottom, rgba(100, 100, 100, 0.2) 0%, rgba(255, 255, 255, 0.5) 40%, #ffffff 100%);
          background-repeat: no-repeat;
        }
        
        @-webkit-keyframes bubble-anim {
          0% {
            -webkit-transform: scale(1);
            transform: scale(1); }
        
          20% {
            -webkit-transform: scaleY(0.95) scaleX(1.05);
            transform: scaleY(0.95) scaleX(1.05); }
        
          48% {
            -webkit-transform: scaleY(1.1) scaleX(0.9);
            transform: scaleY(1.1) scaleX(0.9); }
        
          68% {
            -webkit-transform: scaleY(0.98) scaleX(1.02);
            transform: scaleY(0.98) scaleX(1.02); }
        
          80% {
            -webkit-transform: scaleY(1.02) scaleX(0.98);
            transform: scaleY(1.02) scaleX(0.98); }
        
          97%, 100% {
            -webkit-transform: scale(1);
            transform: scale(1); } }
        
        @keyframes bubble-anim {
          0% {
            -webkit-transform: scale(1);
            transform: scale(1); }
        
          20% {
            -webkit-transform: scaleY(0.95) scaleX(1.05);
            transform: scaleY(0.95) scaleX(1.05); }
        
          48% {
            -webkit-transform: scaleY(1.1) scaleX(0.9);
            transform: scaleY(1.1) scaleX(0.9); }
        
          68% {
            -webkit-transform: scaleY(0.98) scaleX(1.02);
            transform: scaleY(0.98) scaleX(1.02); }
        
          80% {
            -webkit-transform: scaleY(1.02) scaleX(0.98);
            transform: scaleY(1.02) scaleX(0.98); }
        
          97%, 100% {
            -webkit-transform: scale(1);
            transform: scale(1); } }
        
        
          
        <section class="stage">
              <figure class="ball bubble"></figure>
        </section>

        【讨论】:

        • 看起来很像 2D,没有 3D 旋转。
        【解决方案8】:

        在没有 JavaScript+WebGL 的情况下我能看到的唯一方法是使用自定义过滤器编写一个顶点着色器,将 6 个立方体面的顶点映射到一个球体。您获取顶点法线并将它们乘以球体半径。会有一些拉伸,但它和 CSS 一样好

        https://developers.google.com/web/updates/2013/03/Introduction-to-Custom-Filters-aka-CSS-Shaders

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-06-08
          • 2015-09-02
          • 2012-05-07
          • 1970-01-01
          • 2012-08-20
          • 1970-01-01
          • 2014-05-18
          • 2016-02-02
          相关资源
          最近更新 更多