如何实现文字环绕固定图片的3D旋转视觉效果
解决方案:让图片固定静止,文字环绕旋转
问题根源
你的.phone_static容器嵌套在.preloader内部,而.preloader应用了tiltSpin旋转动画——父元素的3D变换会传递给所有子元素,所以无论给.phone_static设置position:fixed还是反向动画,都会被父容器的动画覆盖,导致图片跟着旋转。
修复步骤
- 移出图片容器:把
.phone_static从.preloader中剥离,作为body的直接子元素,脱离父容器的动画上下文。 - 重新定位图片:调整
.phone_static的CSS,让它居中显示在文字旋转的中心位置,同时设置合适的z-index保证文字能在图片前后穿梭。 - 修正CSS错误:清理原代码里的无效语法(比如多余的闭合大括号、
reversespin动画里的rotateY(deg)错误值)。
修改后的完整代码
HTML
<body> <div class="preloader"> <div class="preloader__ring"> <div class="preloader__sector"></div> <div class="preloader__sector">S</div> <div class="preloader__sector">E</div> <div class="preloader__sector">A</div> <div class="preloader__sector">M</div> <div class="preloader__sector">L</div> <div class="preloader__sector">E</div> <div class="preloader__sector">S</div> <div class="preloader__sector">S</div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> </div> <div class="preloader__ring"> <div class="preloader__sector">U</div> <div class="preloader__sector">S</div> <div class="preloader__sector">E</div> <div class="preloader__sector">R</div> <div class="preloader__sector"></div> <div class="preloader__sector">E</div> <div class="preloader__sector">X</div> <div class="preloader__sector">P</div> <div class="preloader__sector">E</div> <div class="preloader__sector">R</div> <div class="preloader__sector">I</div> <div class="preloader__sector">E</div> <div class="preloader__sector">N</div> <div class="preloader__sector">C</div> <div class="preloader__sector">S</div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> <div class="preloader__sector"></div> </div> </div> <div class="phone_static"> <img src="https://github.com/Jitlclarke/ux_portfolio/blob/main/images/small_version_mockup.png?raw=true" alt="" height=560 width=300> </div> </body>
CSS
body { background-color: #333; background: #313030; color: #D3E2C0; font: 1em Dosis, sans-serif; height: 100vh; line-height: 1.5; perspective: 40em; margin: 0; position: relative; } :root { font-size: calc(16px + (24 - 16)*(100vw - 320px)/ (1280 - 320)); } .preloader { animation: tiltSpin 10s linear infinite; flex-direction: column; justify-content: center; align-items: center; margin: auto; width: 17em; height: 17em; display: flex; } .preloader, .preloader__ring { transform-style: preserve-3d; } .preloader__ring { animation-name: spin; animation-duration: 70s; animation-timing-function: inherit; animation-iteration-count: inherit; font-size: 2em; position: relative; top: 100px; height: 3rem; width: 1.5rem; } .phone_static { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 1; } .preloader__ring:nth-child(even) { animation-direction: forward; } .second_preloader:nth-child(even) { animation-direction: forward; animation-duration: 100s; } .preloader__sector { font-weight: 600; position: absolute; top: 0; left: 0; text-align: center; text-transform: uppercase; transform: translateZ(7rem); } .preloader__sector, .preloader__sector:empty:before { display: inline-block; width: 100%; height: 100%; } .preloader__sector:empty:before { background: linear-gradient(transparent 45%, currentColor 45% 55%, transparent 55%); content: ""; } .preloader__sector:nth-child(2) { transform: rotateY(12deg) translateZ(7rem); } .preloader__sector:nth-child(3) { transform: rotateY(24deg) translateZ(7rem); } .preloader__sector:nth-child(4) { transform: rotateY(36deg) translateZ(7rem); } .preloader__sector:nth-child(5) { transform: rotateY(48deg) translateZ(7rem); } .preloader__sector:nth-child(6) { transform: rotateY(60deg) translateZ(7rem); } .preloader__sector:nth-child(7) { transform: rotateY(72deg) translateZ(7rem); } .preloader__sector:nth-child(8) { transform: rotateY(84deg) translateZ(7rem); } .preloader__sector:nth-child(9) { transform: rotateY(96deg) translateZ(7rem); } .preloader__sector:nth-child(10) { transform: rotateY(108deg) translateZ(7rem); } .preloader__sector:nth-child(11) { transform: rotateY(120deg) translateZ(7rem); } .preloader__sector:nth-child(12) { transform: rotateY(132deg) translateZ(7rem); } .preloader__sector:nth-child(13) { transform: rotateY(144deg) translateZ(7rem); } .preloader__sector:nth-child(14) { transform: rotateY(156deg) translateZ(7rem); } .preloader__sector:nth-child(15) { transform: rotateY(168deg) translateZ(7rem); } .preloader__sector:nth-child(16) { transform: rotateY(180deg) translateZ(7rem); } .preloader__sector:nth-child(17) { transform: rotateY(192deg) translateZ(7rem); } .preloader__sector:nth-child(18) { transform: rotateY(204deg) translateZ(7rem); } .preloader__sector:nth-child(19) { transform: rotateY(216deg) translateZ(7rem); } .preloader__sector:nth-child(20) { transform: rotateY(228deg) translateZ(7rem); } .preloader__sector:nth-child(21) { transform: rotateY(240deg) translateZ(7rem); } .preloader__sector:nth-child(22) { transform: rotateY(252deg) translateZ(7rem); } .preloader__sector:nth-child(23) { transform: rotateY(264deg) translateZ(7rem); } .preloader__sector:nth-child(24) { transform: rotateY(276deg) translateZ(7rem); } .preloader__sector:nth-child(25) { transform: rotateY(288deg) translateZ(7rem); } .preloader__sector:nth-child(26) { transform: rotateY(300deg) translateZ(7rem); } .preloader__sector:nth-child(27) { transform: rotateY(312deg) translateZ(7rem); } .preloader__sector:nth-child(28) { transform: rotateY(324deg) translateZ(7rem); } .preloader__sector:nth-child(29) { transform: rotateY(336deg) translateZ(7rem); } .preloader__sector:nth-child(30) { transform: rotateY(348deg) translateZ(7rem); } /* Animations */ @keyframes tiltSpin { from { transform: rotateY(0) rotateX(30deg); } to { transform: rotateY(1turn) rotateX(30deg); } } @keyframes spin { from { transform: rotateY(0deg); } to { transform: rotateY(360deg); } } @keyframes reversespin { from { transform: rotateY(0deg); } to { transform: rotateY(-360deg); } }
额外说明
- 调整了
body的样式,合并了原代码里重复的背景和字体设置,添加position:relative让.phone_static的绝对定位以body为基准。 - 把
.phone_static的left从70%改为50%,确保它完全居中在文字旋转环的中心。 - 修正了
reversespin动画里的无效值,将rotateY(deg)改为rotateY(0deg)。
内容的提问来源于stack exchange,提问作者Justin Clarke
相关产品推荐
相关产品推荐

