CSS 仅 3D 旋转文本

CSS only 3D spinning text

我有一个 div 有一些文本旋转。如何获得文本深度以提供更好的 3d 效果?需要澄清的是,在 90deg 处文本变得 1px 粗,因为我们只能从侧面看到它 - 如何使其变粗,例如,10px 粗?此外,应显示适当的深度 - 即在 0deg 处我们看不到深度;在 45deg,我们看到了 5px 的深度;在 90deg 我们看到完整的 10px 深度;等等

我正在寻找 CSS 唯一的解决方案。

#spinner {
  animation-name: spinner;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-duration: 3s;
  transform-style: preserve-3d;
  text-align:center;
}
@keyframes spinner {
  from {
    transform: rotateY(0deg);
  }
  to {
    transform: rotateY(-360deg);
  }
}
<p id="spinner">Stop, I'm getting dizzy!</p>

我使用 :before:after 元素添加了额外的文本层,并且分别将它们的 Y 轴偏移了 -3deg 和 3deg。结果是文本现在具有 "sort of" 深度。如果我使用较短的字符串(例如 "RPK"),我可以将偏移量增加到大约 7 度,并且看起来仍然不错。然而,这只会给外边缘一个额外的 "depth".

#spinner {
  animation-name: spinner;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-duration: 3s;
  transform-style: preserve-3d;
  text-align:center;
  font-size:2em;
}
#spinner:after,
#spinner:before {
  content:"Stop, I'm getting dizzy!";
  position:absolute;
  top:0;
  left:0;
  width:100%;
  text-align:center;
  transform: rotateY(2deg);
}
#spinner:before {
  transform: rotateY(-2deg);
}
#spinner,
#spinner:after,
#spinner:before {
  font-weight:narrow;
}
@keyframes spinner {
  from {
    transform: rotateY(0deg);
  }
  to {
    transform: rotateY(-360deg);
  }
}
<p id="spinner">Stop, I'm getting dizzy!</p>

在这个版本中,我将 rotate 属性 更改为 translateZ。同样,使用较短的字符串效果会更好。

#spinner {
  animation-name: spinner;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-duration: 3s;
  transform-style: preserve-3d;
  text-align: center;
  font-size: 2em;
}
#spinner:after,
#spinner:before {
  content: "Stop, I'm getting dizzy!";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  text-align: center;
  transform: translateZ(3px);
}
#spinner:before {
  transform:translateZ(-3px);
}
@keyframes spinner {
  from {
    transform: rotateY(0deg);
  }
  to {
    transform: rotateY(-360deg);
  }
}
<p id="spinner">Stop, I'm getting dizzy!</p>

我不会接受这个作为答案,因为它没有给我想要的确切效果。我相信有人会找到完美的方法 :o)

纯 CSS 解决方案看起来不太好。要获得真正的 3D 效果,请使用 JS。我建议使用 three.js,因为它包含一个相当易于使用的 textGeometry 功能。

我写了一个脚本,用 class rotatingText 替换元素的内容,其中元素内的文本在旋转。

您当然可以使用 three.js 制作各种炫酷效果,也可以制作您的文字 look nicer. See textGeometry doc for text formatting. Look in the three.js docs 了解更多详情。

我在代码的注释中做了一些注释。

.rotatingText {
  width: 100%;
  height: 200px;
}
<!--your rotating text, just give it class rotatingText-->
<div class="rotatingText">Stop, I'm getting dizzy!</div>


<!--three.js library-->
<script src="https://ajax.googleapis.com/ajax/libs/threejs/r69/three.min.js"></script>
<!--the default font for three.js-->
<script src="http://threejs.org/examples/fonts/helvetiker_regular.typeface.js"></script>
<!--the script that converts each .rotatingText element into 3D -->
<script>
  function rotateText(container,i){
    var t = THREE;
    var containerW = container.offsetWidth;
    var containerH = container.offsetHeight;
    var theText = container.innerHTML; //grab the text from the element...
    container.innerHTML = ""; // ...and clear it
    var renderer = new t.WebGLRenderer({
      alpha: true,
      antialiasing: true
    });
    renderer.setSize(containerW, containerH);
    renderer.setClearColor(0x000000, 0);
    container.appendChild(renderer.domElement);
    var scene = new t.Scene();
    var camera = new t.PerspectiveCamera(
      75, //vertical field of view
      containerW / containerH, //aspect ratio
      0.1, //near plane
      1000 //far plane
    );
    scene.add(camera);
    camera.position.set(0, 0, 100);
    // This is your 3D text:
    var geometry = new t.TextGeometry(theText, { //insert the text we grabbed earlier here
      size: 10, //your font size
      height: 1 //your font depth
    });
    var material = new t.MeshLambertMaterial({
      color: 0x000000 //your font color
    });
    var text = new t.Mesh(geometry, material); //this is your 3D text object
    //I created a pivot object to act as the center point for rotation:
    geometry.computeBoundingBox();
    var textWidth = geometry.boundingBox.max.x - geometry.boundingBox.min.x;
    text.position.set(-0.5 * textWidth, 0, 0);
    scene.add(text);
    var pivot = new t.Object3D();
    pivot.add(text);
    scene.add(pivot);
    //Then just render your scene and request for new frames
    this.render = function() {
      pivot.rotation.y += .02; //how much you want your text to rotate per frame
      requestAnimationFrame(render);
      renderer.render(scene, camera);
    }
    render();
  }

  var rotatingTextElements = document.getElementsByClassName("rotatingText");
  for (var i = 0; i < rotatingTextElements.length; i++) {
    rotateText(rotatingTextElements[i]);
  }
</script>

添加原始元素的多个克隆,每个都向后平移 1px,然后处理它们的颜色以提供 3d 外观并不是那么糟糕,

check the Pen(仅在 Chrome 上测试)


HTML

<section>
<p >Stop, I'm getting dizzy!</p>
</section>

SCSS

section{
  animation-name: spinner;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-duration: 4s;
  transform-style: preserve-3d;
}


p {
  text-align: center;
  font-size: 2em;
  position:absolute;
  width:100%;
  letter-spacing: 0.2em;
}

@keyframes spinner {
  from {
    transform: rotateY(0deg);
  }
  to {
    transform: rotateY(-360deg);
  }
}

$colors: #000,#111,#222,#333,#444,#555,#666,#777,#888,#999,#aaa,#bbb;

@for $i from 1 through length($colors) {
    p:nth-child( #{$i} ){
       transform: translateZ(- $i+px);
       color: (nth($colors, $i));

    }
}

JS

var p = document.querySelector('p');

for(var i = 0 ; i<13 ; i++){
var node = document.createElement('p');
var child = "Stop, I'm getting dizzy!";
  child = document.createTextNode(child);
  node.appendChild(child);
p.parentNode.appendChild(node);
}

section {
  animation-name: spinner;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-duration: 4s;
  transform-style: preserve-3d;
}

p {
  text-align: center;
  font-size: 2em;
  position: absolute;
  width: 100%;
  letter-spacing: 0.2em;
}

p:last-child {
  -webkit-text-fill-color: silver;
}

@keyframes spinner {
  from {
    transform: rotateY(0deg);
  }
  to {
    transform: rotateY(-360deg);
  }
}

p:nth-child(1) {
  transform: translateZ(-1px);
  color: #000;
}

p:nth-child(2) {
  transform: translateZ(-2px);
  color: #111;
}

p:nth-child(3) {
  transform: translateZ(-3px);
  color: #222;
}

p:nth-child(4) {
  transform: translateZ(-4px);
  color: #333;
}

p:nth-child(5) {
  transform: translateZ(-5px);
  color: #444;
}

p:nth-child(6) {
  transform: translateZ(-6px);
  color: #555;
}

p:nth-child(7) {
  transform: translateZ(-7px);
  color: #666;
}

p:nth-child(8) {
  transform: translateZ(-8px);
  color: #777;
}

p:nth-child(9) {
  transform: translateZ(-9px);
  color: #888;
}

p:nth-child(10) {
  transform: translateZ(-10px);
  color: #999;
}

p:nth-child(11) {
  transform: translateZ(-11px);
  color: #aaa;
}

p:nth-child(12) {
  transform: translateZ(-12px);
  color: #bbb;
}
 <section>
   <p>Stop, I'm getting dizzy!</p>
   <p>Stop, I'm getting dizzy!</p>
   <p>Stop, I'm getting dizzy!</p>
   <p>Stop, I'm getting dizzy!</p>
   <p>Stop, I'm getting dizzy!</p>
   <p>Stop, I'm getting dizzy!</p>
   <p>Stop, I'm getting dizzy!</p>
   <p>Stop, I'm getting dizzy!</p>
   <p>Stop, I'm getting dizzy!</p>
   <p>Stop, I'm getting dizzy!</p>
   <p>Stop, I'm getting dizzy!</p>
   <p>Stop, I'm getting dizzy!</p>
   </section>

简单 text-shadow 可以解决问题:

body {
  perspective: 500px;
}
#spinner {
  text-align: center;
  animation-name: spin, depth;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-duration: 3s;
}
@keyframes spin {
  from { transform: rotateY(0deg); }
  to { transform: rotateY(-360deg); }
}
@keyframes depth {
  0% { text-shadow: 0 0 black; }
  25% { text-shadow: 1px 0 black, 2px 0 black, 3px 0 black, 4px 0 black, 5px 0 black; }
  50% { text-shadow: 0 0 black; }
  75% { text-shadow: -1px 0 black, -2px 0 black, -3px 0 black, -4px 0 black, -5px 0 black; }
  100% { text-shadow: 0 0 black; }
}
<p id="spinner">Stop, I'm getting dizzy!</p>

另一个改进可能是使用 ::before::after 伪 类:

克隆文本

body {
  perspective: 1000px;
}
#spinner {
  font-size: 50px;
  text-align: center;
  animation-name: spin, depth;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-duration: 3s;
  transform-style: preserve-3d;
  position: relative;
}
#spinner::before,
#spinner::after {
  content: "Stop, I'm getting dizzy!";
  display: block;
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  transform: rotateY(0.5deg);
  transform-origin: 0 50%;
}
#spinner::after {
  transform: rotateY(-0.5deg);
  transform-origin: 100% 50%;
}
@keyframes spin {
  from { transform: rotateY(0deg); }
  to { transform: rotateY(-360deg); }
}
@keyframes depth {
  0% { text-shadow: 0 0 black; }
  25% { text-shadow: 1px 0 black, 2px 0 black, 3px 0 black, 4px 0 black, 5px 0 black, 6px 0 black; }
  50% { text-shadow: 0 0 black; }
  75% { text-shadow: -1px 0 black, -2px 0 black, -3px 0 black, -4px 0 black, -5px 0 black, -6px 0 black; }
  100% { text-shadow: 0 0 black; }
}
<p id="spinner">Stop, I'm getting dizzy!</p>

我也想要 CSS-driven 旋转文本,尽管我的方法不同。我会添加它以防它对其他人有帮助:

@keyframes spin {
  from {
    transform: rotateY(0deg) rotateX(0deg);
  }
  to {
    transform: rotateY(360deg) rotateX(360deg);
  }
}

#shared-loader {
  box-sizing: border-box;
  font-family: sans-serif;
  width: 200px;
  height: 200px;
  margin: 0;
  perspective: 400px;
  transform: scale(0.2);
}
    
.cube {
  width: 200px;
  height: 200px;
  position: relative;
  transform-style: preserve-3d;
  transform: translateZ(-100px);
  transition: transform 2s;
  animation: spin 6s linear infinite;
}

.cube-face {
  position: absolute;
  width: 200px;
  height: 200px;
  border: 2px solid #fff;
  line-height: 200px;
  font-size: 80px;
  font-weight: bold;
  color: white;
  text-align: center;
  background: #de662c;
}

.cube-face-front  { transform: rotateY(  0deg) translateZ(100px); }
.cube-face-right  { transform: rotateY( 90deg) translateZ(100px); }
.cube-face-back   { transform: rotateY(180deg) translateZ(100px); }
.cube-face-left   { transform: rotateY(-90deg) translateZ(100px); }
.cube-face-top    { transform: rotateX( 90deg) translateZ(100px); }
.cube-face-bottom { transform: rotateX(-90deg) translateZ(100px); }
<div id="shared-loader">
  <div class="cube">
    <div class="cube-face cube-face-front">Y</div>
    <div class="cube-face cube-face-back">Y</div>
    <div class="cube-face cube-face-right">Y</div>
    <div class="cube-face cube-face-left">Y</div>
    <div class="cube-face cube-face-top">Y</div>
    <div class="cube-face cube-face-bottom">Y</div>
  </div>
</div>