怎样用 CSS 做出 3D 效果的云

本贴最后更新于 3302 天前,其中的信息可能已经时异事殊

##介绍##

本文将介绍如何一步一步制作出一些 3D 效果的云彩,会用到一些高级的属性,主要是如何通过 CSS 的 3D 变换效果来实现,如果你想了解更多,这地方是个不错的开始

静态效果图

本教程将会分成几个部分,每一部分都会有详细的步骤让你理解 HTML,CSS 和 Javascript 都是如何工作的,每一步都有衔接,以及一个链接来测试单个部分的代码效果。教程里的代码是最终 demo 的简化版,但是主要区别在每一部分都有说明

  • 1.新建一个世界和视角
  • 2.向我们创建的世界里添加对象
  • 3.往我们的对象上添加层
  • 4.让 3D 效果运行起来
  • 5.最后总结

##1.新建一个世界和视角##
首先,我们需要两个 div 元素: viewportworld。剩下的部分将会在后面动态的加入。

viewport 包含了整一个屏幕和一个摄影机。由于在 CSS 3D 变换中没有摄影机本身,就假想你在通过一个透明的玻璃屏幕来看屏幕里的视野,你也可以改变看视野的方向。我们将会把所有对象都放在里面,然后对他们进行变换。

world 是一个用来固定所有对象的 div 盒子。 旋转,转化或者放大都变换都会改变我们的元素。为简单起见,我将在需要使用浏览器前缀(-webkit,-moz,-0,-ms 等等)的地方使用没有前缀的 CSS 属性

这就是我们需要的所有盒子模型:

<div id="viewport"> <div id = "world" ></div> </div>

下面是我们定义的两个 CSS 样式,这里非常重要的是要将 world div 放在 viewport div 里面,否则将无法渲染场景。要记住你将旋转一个放置在文档里的元素,像其他 2D 元素那样。

#viewport{ bottom:0; left:0; overflow:hidden; perspective:400; position:absolute; right:0; top:0; } #world{ height: 512px; left: 50%; margin-left: -256px; margin-top: -256px; position: absolute; top: 50%; transform-style: preserve-3d; width: 512px; }

现在写一些代码来初始化我们的对象,绑定 mousemove 事件以及定义 updateView() 函数。

/* Defining our variables world and viewport are DOM elements, worldXAngle and worldYAngle are floats that hold the world rotations, d is an int that defines the distance of the world from the camera */ var world = document.getElementById( 'world' ), viewport = document.getElementById( 'viewport' ), worldXAngle = 0, worldYAngle = 0, d = 0; /* Event listener to transform mouse position into angles from -180 to 180 degress, both vertically and horizontally */ window.addEventListener( 'mousemove', function( e ) { worldYAngle = -( .5 - ( e.clientX / window.innerWidth ) ) * 180; worldXAngle = ( .5 - ( e.clientY / window.innerHeight ) ) * 180; updateView(); } ); /* Changes the transform property of world to be translated in the Z axis by d pixels, rotated in the X axis by worldXAngle degrees and rotated in the Y axis by worldYAngle degrees. */ function updateView() { world.style.transform = 'translateZ( ' + d + 'px ) \ rotateX( ' + worldXAngle + 'deg) \ rotateY( ' + worldYAngle + 'deg)'; }

world 是红色的,viewport 有背景色来模拟天空,mousewheel 滚轮事件来监听摄影机的远近距离。移动鼠标来观察红色的 div 是如何改变方向的。

这是这一部分的演示链接

##2.向我们创建的世界里添加对象##

现在我们开始添加真正的 3D 内容。我们加入一些新的 div 放置在 world 空间里。有必要添加几个绝对位置的 div 作为 world 的子节点,但是用 3D 变换来替代 lefttop, 他们默认会出现在 world 的中央位置。widthheight 属性无关紧要,因为这些新的容器是放置那些真实云的层。如果正式应用里最好将他们居中(通过设置 margin-leftmargin-top 属性 来设置 widthheight 一半的负数值)。

.cloudBase { height: 20px; left: 256px; margin-left: -10px; margin-top: -10px; position: absolute; top: 256px; width: 20px; }

我们添加 generate()createCloud() 方法来充实 world注意 random_{var} 不是真实的变量值,而是一个代码占位符,应该返回一个给定范围内的随机数

/* objects is an array of cloud bases layers is an array of cloud layers */ var objects = [], layers = []; /* Clears the DOM of previous clouds bases and generates a new set of cloud bases */ function generate() { objects = []; layers = []; if ( world.hasChildNodes() ) { while ( world.childNodes.length >= 1 ) { world.removeChild( world.firstChild ); } } for( var j = 0; j <; 5; j++ ) { objects.push( createCloud() ); } } /* Creates a single cloud base: a div in world that is translated randomly into world space. Each axis goes from -256 to 256 pixels. */ function createCloud() { var div = document.createElement( 'div' ); div.className = 'cloudBase'; var t = 'translateX( ' + random_x + 'px ) \ translateY( ' + random_y + 'px ) \ translateZ( ' + random_z + 'px )'; div.style.transform = t; world.appendChild( div ); return div; }

页面中那些粉红色方块的层就是云层模型了,这里有一个 p 变量来更简单的改变 viewport.style.perspective 的值。试着去改变这个变量来观察我们的摄影机是如何变化的。这个值越大,视角的垂直度就会越大。再次提醒,那个 randowm_{var} 并不是真正的变量。

点我来观察这部分的演示

##3.往我们的对象上添加层##

现在有趣的事情开始发生了,我们添加了几个绝对位置的“云层” div 盒子来表示云,这些盒子将会用来装载云的贴图。

.cloudLayer { height: 256px; left: 50%; margin-left: -128px; margin-top: -128px; position: absolute; top: 50%; width: 256px; }

旧的 createCloud() 函数做了一些改动,添加了云层的随机数。

/* Creates a single cloud base and adds several cloud layers. Each cloud layer has random position ( x, y, z ), rotation (a) and rotation speed (s). layers[] keeps track of those divs. */ function createCloud() { var div = document.createElement( 'div' ); div.className = 'cloudBase'; var t = 'translateX( ' + random_x + 'px ) \ translateY( ' + random_y + 'px ) \ translateZ( ' + random_z + 'px )'; div.style.transform = t; world.appendChild( div ); for( var j = 0; j < 5 + Math.round( Math.random() * 10 ); j++ ) { var cloud = document.createElement( 'div' ); cloud.className = 'cloudLayer'; cloud.data = { x: random_x, y: random_y, z: random_z, a: random_a, s: random_s }; var t = 'translateX( ' + random_x + 'px ) \ translateY( ' + random_y + 'px ) \ translateZ( ' + random_z + 'px ) \ rotateZ( ' + random_a + 'deg ) \ scale( ' + random_s + ' )'; cloud.style.transform = t; div.appendChild( cloud ); layers.push( cloud ); } return div; }

点我来观察第三部分的演示

云层就是那些蓝色带有一点白色边边的部分,看起来相当有层次感呐。移动鼠标来观察下每一层的位置是怎样的以及如何旋转的。

##4.让 3D 效果运行起来##

接下来就是见证奇迹的时刻!我们用 layers[] 来为世界里每一个单独的云层建立一个引用。我们用 worldXangleworldYAngle 来表示整个空间的选择变换。

如果我们将每个层都设置成相反的旋转,这会在 viewport 里重新调整他们:我们就有一个布告板了。因为我们旋转 world 先是 X 方向然后是 Y 方向,我们需要反着顺序来旋转每一个层,首先是 Y 方向再是 X 方向,变换的顺序是非常重要的,如果你没有正确的设置顺序,元素的方向就都会不对了。

/* Iterate layers[], update the rotation and apply the inverse transformation currently applied to the world. Notice the order in which rotations are applied. */ function update (){ for( var j = 0; j < layers.length; j++ ) { var layer = layers[ j ]; layer.data.a += layer.data.speed; var t = 'translateX( ' + layer.data.x + 'px ) \ translateY( ' + layer.data.y + 'px ) \ translateZ( ' + layer.data.z + 'px ) \ rotateY( ' + ( - worldYAngle ) + 'deg ) \ rotateX( ' + ( - worldXAngle ) + 'deg ) \ scale( ' + layer.data.s + ')'; layer.style.transform = t; } requestAnimationFrame( update ); }

来回的移动鼠标,你将会看到蓝色的云层现在已经变成垂直的了(他们总是面对这镜头),但是 world 和其他云基体还是三维空间里的对象。

点我来见证神奇的效果

##5.最后总结##

为了达到最后的效果,只需要将用来调试的那些颜色去掉,吧云层 div 用一个 img 贴上云的图片。注意图片得是带有 alpha 通道的 PNG 格式,要不没效果。

点我看看最后的效果

点我看看最后版本

当然,你可以随意换其他你想要的图片:烟雾痕迹,等离子体云,绿叶,飞行的面包机等等。只要把 backgroud-image 更改下就好了。混合不同比例的纹理材质会有神奇的效果。

随机的添加元素是可以的,但是你也可以创建有序的结构,比如树,鸭子形状的云或者复杂的大爆炸等。可以尝试一些 3D 曲线,创建固定的云的运行轨迹,创造一个多人游戏来猜 3D 云的形状等等,充满着无限可能。

NOTE:文中的代码都是简化了的哦,如果想一步一步实际操作的话最好保存下示例效果的链接页面来查看源码。

本文译自 https://www.clicktorelease.com/blog/how-to-make-clouds-with-css-3d#

涉及到一些 3D 透视等专业词汇翻译有误敬请见谅,欢迎指正。

  • CSS

    CSS(Cascading Style Sheet)“层叠样式表”是用于控制网页样式并允许将样式信息与网页内容分离的一种标记性语言。

    199 引用 • 542 回帖 • 1 关注
  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    245 引用 • 1338 回帖 • 1 关注
  • 翻译
    58 引用 • 84 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
请输入回帖内容 ...

推荐标签 标签

  • Notion

    Notion - The all-in-one workspace for your notes, tasks, wikis, and databases.

    10 引用 • 77 回帖 • 1 关注
  • 黑曜石

    黑曜石是一款强大的知识库工具,支持本地 Markdown 文件编辑,支持双向链接和关系图。

    A second brain, for you, forever.

    22 引用 • 214 回帖 • 1 关注
  • Google

    Google(Google Inc.,NASDAQ:GOOG)是一家美国上市公司(公有股份公司),于 1998 年 9 月 7 日以私有股份公司的形式创立,设计并管理一个互联网搜索引擎。Google 公司的总部称作“Googleplex”,它位于加利福尼亚山景城。Google 目前被公认为是全球规模最大的搜索引擎,它提供了简单易用的免费服务。不作恶(Don't be evil)是谷歌公司的一项非正式的公司口号。

    49 引用 • 192 回帖
  • 开源

    Open Source, Open Mind, Open Sight, Open Future!

    411 引用 • 3588 回帖
  • OpenCV
    15 引用 • 36 回帖
  • CentOS

    CentOS(Community Enterprise Operating System)是 Linux 发行版之一,它是来自于 Red Hat Enterprise Linux 依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码,因此有些要求高度稳定的服务器以 CentOS 替代商业版的 Red Hat Enterprise Linux 使用。两者的不同在于 CentOS 并不包含封闭源代码软件。

    239 引用 • 224 回帖 • 1 关注
  • GitBook

    GitBook 使您的团队可以轻松编写和维护高质量的文档。 分享知识,提高团队的工作效率,让用户满意。

    3 引用 • 8 回帖 • 1 关注
  • Angular

    AngularAngularJS 的新版本。

    26 引用 • 66 回帖 • 548 关注
  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖 • 3 关注
  • InfluxDB

    InfluxDB 是一个开源的没有外部依赖的时间序列数据库。适用于记录度量,事件及实时分析。

    2 引用 • 88 关注
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    948 引用 • 1460 回帖 • 1 关注
  • JetBrains

    JetBrains 是一家捷克的软件开发公司,该公司位于捷克的布拉格,并在俄国的圣彼得堡及美国麻州波士顿都设有办公室,该公司最为人所熟知的产品是 Java 编程语言开发撰写时所用的集成开发环境:IntelliJ IDEA

    18 引用 • 54 回帖 • 5 关注
  • RemNote
    2 引用 • 16 回帖 • 7 关注
  • Dubbo

    Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,是 [阿里巴巴] SOA 服务化治理方案的核心框架,每天为 2,000+ 个服务提供 3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。

    60 引用 • 82 回帖 • 612 关注
  • 数据库

    据说 99% 的性能瓶颈都在数据库。

    345 引用 • 742 回帖
  • 单点登录

    单点登录(Single Sign On)是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

    9 引用 • 25 回帖 • 3 关注
  • LeetCode

    LeetCode(力扣)是一个全球极客挚爱的高质量技术成长平台,想要学习和提升专业能力从这里开始,充足技术干货等你来啃,轻松拿下 Dream Offer!

    209 引用 • 72 回帖 • 2 关注
  • H2

    H2 是一个开源的嵌入式数据库引擎,采用 Java 语言编写,不受平台的限制,同时 H2 提供了一个十分方便的 web 控制台用于操作和管理数据库内容。H2 还提供兼容模式,可以兼容一些主流的数据库,因此采用 H2 作为开发期的数据库非常方便。

    11 引用 • 54 回帖 • 668 关注
  • SOHO

    为成为自由职业者在家办公而努力吧!

    7 引用 • 55 回帖 • 3 关注
  • V2Ray
    1 引用 • 15 回帖
  • Gzip

    gzip (GNU zip)是 GNU 自由软件的文件压缩程序。我们在 Linux 中经常会用到后缀为 .gz 的文件,它们就是 Gzip 格式的。现今已经成为互联网上使用非常普遍的一种数据压缩格式,或者说一种文件格式。

    9 引用 • 12 回帖 • 164 关注
  • BAE

    百度应用引擎(Baidu App Engine)提供了 PHP、Java、Python 的执行环境,以及云存储、消息服务、云数据库等全面的云服务。它可以让开发者实现自动地部署和管理应用,并且提供动态扩容和负载均衡的运行环境,让开发者不用考虑高成本的运维工作,只需专注于业务逻辑,大大降低了开发者学习和迁移的成本。

    19 引用 • 75 回帖 • 668 关注
  • Hibernate

    Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库。

    39 引用 • 103 回帖 • 724 关注
  • Mac

    Mac 是苹果公司自 1984 年起以“Macintosh”开始开发的个人消费型计算机,如:iMac、Mac mini、Macbook Air、Macbook Pro、Macbook、Mac Pro 等计算机。

    168 引用 • 597 回帖
  • 链滴

    链滴是一个记录生活的地方。

    记录生活,连接点滴

    173 引用 • 3849 回帖
  • 996
    13 引用 • 200 回帖 • 3 关注
  • 持续集成

    持续集成(Continuous Integration)是一种软件开发实践,即团队开发成员经常集成他们的工作,通过每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。

    15 引用 • 7 回帖