CSS 3D Panorama - 淘宝造物节技术剖析
JChehe opened this issue · 5 comments
本文首发于 凹凸实验室
前言
3D 全景并不是什么新鲜事物了,但以前我们在 Web 上看到的 3D 全景一般是通过 Flash 实现的。若我们能将 CSS3 Transform
的相关知识运用得当,也是能实现类似的效果。
准备
在实现 CSS3 3D 全景之前,我们先理清部分 CSS3 Transform 相关的属性:
- perspective: 指定观察者与
z=0
平面的距离,使具有三维变换的元素产生透视效果。(默认值:none,值只能是绝对长度,即负数是非法值)。 - perspective-origin:指定观察者在哪个位置查看物体(基于同时指定了
perspective
的元素,默认值:50% 50%,即正视物体。值为 50% 100% 时,即仰视物体)。 - transform-style:为当前元素的子元素提供 2D 还是 3D 的空间。另外,该属性是非继承的。
- transform:修改 CSS 可视化模型的坐标空间,包括 平移(translate)、旋转(rotate)、缩放(scale) 和 扭曲(skew)。
- transform-origin:元素 transform 的原点(默认值为 50% 50% 0)。
下面我们对上述的一些点进行更深入的分析:
-
对于
perspective
对元素大小的影响:
根据 相似三角形 的性质可计算出被前移的元素最终在屏幕上显示的实际大小另外,对于在“眼睛”背后的元素(即元素的
z
轴坐标值大于perspective
的值),浏览器是不会将其渲染出来的。 -
对于
transform-style
,该属性指定其子元素是处于 3D 场景还是 2D 场景。对于 2D 场景,子元素位于元素本身的平面内,即使旋转也不会穿透其他子元素;对于 3D 场景,子元素以真实 3D 空间进行定位展示。另外,由于
transform-style
是非继承属性,对于中间节点需再次指定。 -
对于
transform
属性:下图整理了 rotate3d、translate3d 的变换方向:
需要注意的是:transform 中的变换属性是有顺序的,如translateX(10px) rotate(30deg)
与rotate(30deg) translateX(10px)
是不等价的。另外,如果 scale 值为负数,则该方向会 180 度翻转;
再另外,transform 可能会导致元素(字体)模糊,如 translate 的数值存在小数、通过 translateZ 或 scale 放大元素等等。每个浏览器都有其不同的表现。
实现
理清了 CSS Transform 相关的知识点后,下面就讲讲如何实现 CSS 3D 全景 :
想象一下,当我们站在十字路口中间,身体旋转 360°,这个过程中所看到的画面就是一幅以你为中心的全景图。其实,当焦距不变时,我们就等同于站在一个圆柱体的中心。
但是,虚拟世界与现实世界最大的不同点是:没有东西是连续的,即所有东西都是离散的。例如,你无法在屏幕上显示一个完美的圆。你只能以一个正多边形表示圆:边越多,圆就越“完美”。
同理,在三维空间中,每个 3D 模型都是一个多面体(即 3D 模型由不可弯曲的平面组成)。当我们讨论一个本身就是多面体(如立方体)的模型时并不足以为奇,但对于其它模型,如球体,就需要意识这个原理。
淘宝造物节的活动页 是一个基于 CSS 3D 实现的全景页面。它将全景图分割成 20 等份,为一个正 20 棱柱。需要注意的是:我们要确保每个元素的正面是指向棱柱中心。所以要计算好每等份的旋转角度值后,再将元素向外(即 Z 轴方向)平移 r
px。对于立方体的 r
就是 边长/2
,而对于其他正棱柱呢?
举例:对于正九棱柱,每个元素的宽为 210px
,对应的圆心角为 40°
,即如下图:
图片来自:https://desandro.github.io/3dtransforms/docs/carousel.html
正九棱柱的俯视图
由此可得到一个通用函数,只需传入含有元素的宽度和元素数量的对象,即可得到 r
值:
function calTranslateZ(opts) {
return Math.round(opts.width / (2 * Math.tan(Math.PI / opts.number)))
}
calTranlateZ({
width: 210,
number: 9
}); // 288
另外,为了让下文易于理解,我们约定 HTML 的结构:
#view(perspective: 420px)
#cube(transform-style: preserve-3d)
.face // 组成立方体的面
正棱柱构建完成后,就需要将我们的“眼睛”放置在正棱柱内。由于浏览器不会渲染在“眼睛”后的元素(与 .face 元素
是否设置 backface-visibility: hidden;
无关),且保证 .face 元素
的正面均指向正棱柱中心,这样就形成 360° 被环绕的效果了。
根据上述知识,笔者粗略地模仿了“造物节”的效果:https://css3dpanorama-1251477229.cos.ap-guangzhou.myqcloud.com/index.html
另外,只需 6 幅图即可实现一个常见的无死角全景图效果:https://css3dpanorama-1251477229.cos.ap-guangzhou.myqcloud.com/street.html
以上就是我们通过 CSS3 Transform 相关属性实现的可交互全景效果了。当然,交互效果可以是拖拽,也可以是重力感应等。
全景图素材的制作
将全景图制作分为设计类与实景类:
设计类
要制作类似 《淘宝造物节》 的全景页面,设计稿有以下要求。
注:下面提及的具体数据均基于《造物节》,可根据自身要求进行调整(若发现欠缺,欢迎作出补充)。
整体背景设计图如下(2580*1170px,被分成 20 等份):
基本要求:
- 水平方向上需要首尾相连;
- 因为效果图最终需要切成 N 等份,所以设计图的宽度要能被 N 整除;
- 不要遗漏上下两面。
为了增强立体感,可添加能形成视差效果的小物体(与背景图不同的运动速度、延迟时间),如:
小物体元素(虚线用于参考,造物节**有 21 个小物体)
如上图虚线所示,每个小物体也会被等分成 M 份,且每份宽度应与背景元素宽度相等。
实景类
如果想制作实景的全景效果,可以看看 Google 街景:
Google 街景 推荐的设备如下:
如上图,最实惠的方式就是最后一个选项——Google 街景 APP,该应用提供了全景相机功能。但正如图片介绍所说,这是需要练习的,因此对操作要求比较高。
补充:
上周六(2016.8.20)参加了 TGDC 的分享会,嘉宾分享了他们处理全景的方式:
- 利用 RICOH THETA S 等专业设备拍出全景图
- 导出静态图像
- 利用设备专门提供的 APP 或 krpamo tools、pano2vr、Glsky box 等工具将静态图像切分为 6 张图
- 利用 Web 技术制作可交互的全景图
其中 Web 技术有以下 3 种可选方式:
- CSS3(本文所提及的方式)
- Three.js
- krpano(为全景而生,低级浏览器则回退到 Flash),查看教程
现场快速制作的 会议现场全景。
可见,优秀硬件设备的出现,大大减少了后期处理的时间,而 Web 则提供了一个优秀的展现平台。
最后
随着终端设备的软硬件不断完善与提高,Web 在 3D 领域也不甘落后,如果你玩腻了 2D 的 H5 或者想为用户提供更加新颖真实的体验,全景也许是一种选择。
最后,如有不清晰或不明白的地方,可以留言,我会尽可能解决的。谢谢谢~
你好 有源码可以参考下吗
全景图载入的时候有种路径动画的效果那部分是怎么实现的呢?