Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSS 3D Panorama - 淘宝造物节技术剖析 #2

Open
JChehe opened this issue Dec 3, 2016 · 5 comments
Open

CSS 3D Panorama - 淘宝造物节技术剖析 #2

JChehe opened this issue Dec 3, 2016 · 5 comments

Comments

@JChehe
Copy link
Owner

JChehe commented Dec 3, 2016

本文首发于 凹凸实验室

封面

前言

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 对元素大小的影响:
    perspective
    根据 相似三角形 的性质可计算出被前移的元素最终在屏幕上显示的实际大小

    另外,对于在“眼睛”背后的元素(即元素的 z 轴坐标值大于 perspective 的值),浏览器是不会将其渲染出来的。

  • 对于 transform-style,该属性指定其子元素是处于 3D 场景还是 2D 场景。对于 2D 场景,子元素位于元素本身的平面内,即使旋转也不会穿透其他子元素;对于 3D 场景,子元素以真实 3D 空间进行定位展示。

    另外,由于 transform-style 是非继承属性,对于中间节点需再次指定。

  • 对于 transform 属性:下图整理了 rotate3d、translate3d 的变换方向:
    transform
    需要注意的是: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

可由下图看出,将水平的 4 张图片合成后就是一张全景图:
此处输入图片的描述

以上就是我们通过 CSS3 Transform 相关属性实现的可交互全景效果了。当然,交互效果可以是拖拽,也可以是重力感应等。

全景图素材的制作

将全景图制作分为设计类与实景类:

设计类

要制作类似 《淘宝造物节》 的全景页面,设计稿有以下要求。

注:下面提及的具体数据均基于《造物节》,可根据自身要求进行调整(若发现欠缺,欢迎作出补充)。

整体背景设计图如下(2580*1170px,被分成 20 等份):
淘宝造物节整体效果图

基本要求:

  1. 水平方向上需要首尾相连;
  2. 因为效果图最终需要切成 N 等份,所以设计图的宽度要能被 N 整除
  3. 不要遗漏上下两面。

为了增强立体感,可添加能形成视差效果的小物体(与背景图不同的运动速度、延迟时间),如:
物体小元素1
物体小元素2
小物体元素(虚线用于参考,造物节中共有 21 个小物体)

如上图虚线所示,每个小物体也会被等分成 M 份,且每份宽度应与背景元素宽度相等。

实景类

如果想制作实景的全景效果,可以看看 Google 街景:

Google 街景 推荐的设备如下:
Google 街景推荐的设备

如上图,最实惠的方式就是最后一个选项——Google 街景 APP,该应用提供了全景相机功能。但正如图片介绍所说,这是需要练习的,因此对操作要求比较高。

补充:
上周六(2016.8.20)参加了 TGDC 的分享会,嘉宾分享了他们处理全景的方式:

  1. 利用 RICOH THETA S 等专业设备拍出全景图
  2. 导出静态图像
  3. 利用设备专门提供的 APP 或 krpamo tools、pano2vr、Glsky box 等工具将静态图像切分为 6 张图
  4. 利用 Web 技术制作可交互的全景图

其中 Web 技术有以下 3 种可选方式:

  • CSS3(本文所提及的方式)
  • Three.js
  • krpano(为全景而生,低级浏览器则回退到 Flash),查看教程

现场快速制作的 会议现场全景

可见,优秀硬件设备的出现,大大减少了后期处理的时间,而 Web 则提供了一个优秀的展现平台。


最后

随着终端设备的软硬件不断完善与提高,Web 在 3D 领域也不甘落后,如果你玩腻了 2D 的 H5 或者想为用户提供更加新颖真实的体验,全景也许是一种选择。

最后,如有不清晰或不明白的地方,可以留言,我会尽可能解决的。谢谢谢~

@JChehe JChehe added bug and removed bug labels Dec 3, 2016
@zjp123
Copy link

zjp123 commented Nov 11, 2019

你好 有源码可以参考下吗

@JChehe
Copy link
Owner Author

JChehe commented Nov 12, 2019

你好 有源码可以参考下吗

@zjp123 没有哦,弄丢了。网上好像有插件实现。

@xiao252
Copy link

xiao252 commented Apr 11, 2020

全景图载入的时候有种路径动画的效果那部分是怎么实现的呢?

@JChehe
Copy link
Owner Author

JChehe commented Apr 11, 2020

全景图载入的时候有种路径动画的效果那部分是怎么实现的呢?

@xiao252 是指加载进度条完成后,组成棱柱的每片方块从小到大的效果吗?

  1. 棱柱作为一个整体进行旋转
  2. 同时,组成棱柱的每片方块按序错开一定间隔时间,执行从 scale(0) -> scale(1) 的过渡

@xiao252
Copy link

xiao252 commented Apr 11, 2020

全景图载入的时候有种路径动画的效果那部分是怎么实现的呢?

@xiao252 是指加载进度条完成后,组成棱柱的每片方块从小到大的效果吗?

  1. 棱柱作为一个整体进行旋转
  2. 同时,组成棱柱的每片方块按序错开一定间隔时间,执行从 scale(0) -> scale(1) 的过渡

哦 ,明白了 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants