内容简介:本节内容来自于小册WebGL 入门与实践第24节。前面介绍了 3D 变换的原理和算法实现,并通过一些简单的 demo 演示了变换效果,但这些 demo 都是使用 WebGL 技术渲染。本节我们暂时不使用 WebGL,而是改用前端同学最熟悉的 CSS 技术来实现 3D 效果,并进一步了解 CSS 中的 3D 属性和 WebGL 中 3D 概念的异同之处。下面是 CSS3 中的几个很重要的 3D 属性:
本节内容来自于小册WebGL 入门与实践第24节。
前面介绍了 3D 变换的原理和算法实现,并通过一些简单的 demo 演示了变换效果,但这些 demo 都是使用 WebGL 技术渲染。本节我们暂时不使用 WebGL,而是改用前端同学最熟悉的 CSS 技术来实现 3D 效果,并进一步了解 CSS 中的 3D 属性和 WebGL 中 3D 概念的异同之处。
CSS 中的 3D 属性
下面是 CSS3 中的几个很重要的 3D 属性:
- transform:对 DOM 进行变换,相当于 WebGL 中对模型进行的变换。
- transform-origin:设置变换的中心点。
- perspective-origin:视点,相当于 WebGL 中摄像机的 X、Y 轴坐标。
- perspective:视距,启用该属性相当于在 WebGL 中设置摄像机和 DOM 元素之间在 Z 轴方向上的距离,设置该属性不等于 0 时会自动启用透视投影效果。
- transform-style:是否启用 3D 变换。
- backface-visibility:背面是否可见。
本节我们主要讲述 CSS 中的变换属性 transform
,变换分为 基本变换
和 矩阵变换
,基本变换大家都比较熟悉了,本节不做过多介绍,我们主要介绍 矩阵变换
和 组合变换
。
变换:transform
transform
是大家最常用的一个属性,我们经常会使用它实现一像素的边框和以及容器或者内容的水平、垂直居中,又或者利用它实现强制 GPU 渲染,提升动画性能。
transform 分为 2D 和 3D 变换,3D 变换只是在 2D 的基础上增加了 Z 轴方向的变换。
一般情况下,如果对一个 DOM 施加变换,那么变换的中心往往是 DOM 的中心位置,类比到 WebGL 中,也就是模型的中心,我们可以把 CSS 中的 DOM 看做 WebGL 中的模型。
transform 包含四个基本变换属性值: translate
、 rotate
、 skew
、 scale
,对应的 3D 变换属性值为 translate3d
、 rotate3d
、 scale3d
。
注意,skew 没有对应的 3D 变换设置。
基本变换大家应该都很熟悉了,后面重点要讲解的是 matrix
、 matrix3d
的计算与使用,以及 组合变换
的使用技巧。
当然,下面我们还是先回顾一下 transform
的基本用法。
平移
平移的使用方法:
- translate(tx, ty)
- translate3d(tx, ty, tz)
- translateX(tx)
- translateY(ty)
- translateZ(tz)
将 dom 元素分别沿着 X、Y、Z 轴向平移 30 px。
/* 分别沿 X 轴和 Y 轴平移 30 px。*/ transform: translate(30px, 30px); /* 分别沿 X 轴、 Y 轴、Z 轴平移 30 px。*/ transform: translate3d(30px, 30px, 30px); /* 沿 X 轴平移 30 px。*/ transform: translateX(30px); /* 沿 Y 轴平移 30 px。*/ transform: translateY(30px); /* 沿 Z 轴平移 30 px。*/ transform: translateZ(30px); 复制代码
旋转
旋转用法也比较简单,在此不做过多描述。
- rotate(angle),绕 Z 轴旋转。
- rotate3d(x, y, z, angle)。绕轴
axis = {x:x, y:y, z:z}
旋转指定角度angle
。 - rotateX(angle),绕 X 轴旋转。
- rotateY(angle),绕 Y 轴旋转。
- rotateZ(angle),绕 Z 轴旋转。
/*绕 X 轴旋转 45 deg。*/ transform: rotate(45deg); /* 将第一个参数设置为 1, 代表绕 X 轴旋转 45 deg。*/ transform: rotate3d(1, 0, 0, 45deg); /* 将第二个参数设置为 1, 代表绕 Y 轴旋转 45 deg。*/ transform: rotate3d(0, 1, 0, 45deg); /* 将第三个参数设置为 1, 代表绕 Y 轴旋转 45 deg。*/ transform: rotate3d(0, 0, 1, 45deg); /* 将三个参数都设置为 1, 代表绕 Y 轴旋转 45 deg。*/ transform: rotateZ(45deg); 复制代码
绕任意轴的旋转。
关于旋转,我想说明一下 rotate3d
的使用方式,它接收一个 轴向量
和一个 角度
,代表绕 轴向量
旋转某个 角度
。
举个例子来说,我们想让模型绕轴 axis= {x: 1,y: 1,z: 1}进行旋转,那么用rotate3d表示如下:
.box{ animation: rotate 3s infinite linear; } @keyframes rotate{ 0% { transform:rotate3d(1, 1, 1, 0deg); } 100% { transform:rotate3d(1, 1, 1, 360deg); } } 复制代码
变换参照点 transform-origin
根据上图的例子,你会发现,默认的旋转是绕着模型的中心位置进行的,这个位置是浏览器默认的。但事实上,CSS 仍然提供了对变换中心的设置功能,通过设置 transform-origin
来实现。
- transform-origin 包含 X、Y、Z 轴坐标的设置。
- transform-origin 接收百分比数值时,是以自身尺寸为基准的。
比如,我们让一个 DOM 元素沿着上边沿进行进行旋转,只需要将 transform-origin 的 Y 轴分量设置为 0% 或者 0 即可。
transform-origin: 50% 0%; transform: rotateX(90deg); 复制代码
这个概念比较简单,但是很灵活。利用它我们能实现很多有意思的 3D 效果,比如 CSS 版的 魔方
。
缩放
缩放的使用方法也很简单。
- scale(sx, sy),沿 X 轴方向缩放 sx 倍,沿 Y 轴方向缩放 sy 倍。
- scale(sx),沿 X 轴方向和 Y 轴方向缩放 sx 倍。
- scale3d(sx, sy, sz),分别沿 X、Y、Z 轴方向缩放 sx、sy、sz 倍。
- scaleX(sx),沿 X 轴方向缩放 sx 倍。
- scaleY(sy),沿 Y 轴方向缩放 sy 倍。
代码示例:
/* 沿 X 轴和 Y 轴 放大两倍。*/ transform: scale(2); /* 沿 X 轴方向当大 3 倍,沿 Y 轴方向放大 2 倍。*/ transform: scale(3, 2); /* 分别在 X 、Y、 Z 轴方向放大 2倍、3倍、4倍。*/ transform: scale3d(2, 3, 4); /* 在 X 轴方向放大两倍。*/ transform: scaleX(2); /* 在 Y 轴方向放大两倍。*/ transform: scaleY(2); 复制代码
斜切
斜切的使用方法:
- skew(xAngle, yAngle),DOM 元素沿着 X 方向切变 xAngle 度,沿 Y 轴方向切变 yAngle 度。
- skewX(xAngle),沿 X 轴方向切变 xAngle 度。
- skewY(yAngle),沿 Y 轴方向切变 yAngle 度。
斜切可以理解为将 DOM 元素沿 X 轴或者 Y 轴拉伸,切变会改变物体的形状。
代码示例:
/* 沿 X 轴切变 30 度。*/ transform: skew(30deg); /* 沿 X 轴切变 30 度,沿 Y 轴切变 40 度。*/ transform: skew(30deg, 40deg); /* 沿 X 轴方向切变 30 度。*/ transform: skewX(30deg); /* 沿 Y 轴方向切变 40 度。*/ transform: skewY(40deg); 复制代码
以上就是 transform
的常见用法,接下来我们开始讲重点了: 组合变换
和 matrix
组合变换
组合变换
就是在 transform 的属性值中附加多个变换效果,而不只是单一的变换。
比如下面这个变换样式:
transform: rotateX(60deg) rotateY(60deg); 复制代码
这个样式的作用是先让 DOM 元素绕着 X 轴旋转 60 度,注意此时 DOM 元素的坐标系改变了,再绕变换后的坐标系的 Y 轴旋转 60 度。
需要谨记的是:
动态坐标系变换 静态坐标系变换
至于多个变换是基于动态坐标系进行构思,还是基于静态的世界坐标系进行构思,取决于每个人的理解习惯,但最终的变换效果都是一样的。
在欧拉角章节我们也讲过了多个矩阵相乘时,从前往后和从后往前理解变换所基于的坐标系是不同的。transform 多个变换理解顺序和前面所讲的保持一致。
再举个比较明显的例子,我们先让 DOM 旋转 60 度,然后将其沿 X 轴平移 200 像素,大家觉得 DOM 会按照怎样的轨迹变换?
我们看一下:
transform: rotateX(60) translateX(200px); 复制代码
为了更方便观察 3D 组合变换的效果,我将图片外层容器的 视点
设置在了右上方:
.imgWrapper{ perspective: 300px; margin-top:300px; position: relative; perspective-origin: 100% -100px; } 复制代码
关于视点 perspective-origin
和 视距 perspective
我们放在下一节讲述。
从前往后理解变换,需要按照 模型坐标系
理解:
下图,白色坐标轴是图片默认的坐标系,当图片绕 X 轴旋转 60 度后,坐标系变成红色坐标轴的指向。
接着沿当前图片坐标系的红色 X' 轴平移 200 像素,此时,应该朝向屏幕里侧和右侧移动。
从后往前理解多个变换,需按照世界坐标系理解
请注意, CSS
中的 世界坐标系
就是 施加变换的 DOM 节点(本例为图片)最开始的坐标系,即图中的白色坐标轴。
- 首先沿着 X 轴平移 200 像素。
- 接着绕世界坐标系的 Y 轴旋转 60 度。
可以看出,无论我们按照哪种坐标系理解,最终变换效果都是一样的,看下图:
这就是 组合变换
要注意的地方,了解了组合变换的规律,我们才能做出很有意思的特效,比如照片墙:
照片墙的核心原理就是先将图片沿着 Z 轴方向移动一定距离,之后绕世界坐标系的Y 轴旋转指定角度。
transform: rotateY(30deg) 200px; 复制代码
当然,这只是核心原理,事实上我们还需要做如下几步:
- 让图片能够显示出 3D 效果,这一步需要让图片父容器的
transform-style
属性设置为preserve-3d
。 - 为父容器加上透视属性
perspective
,设置为透视投影,并调整合适的视距 ,这样才能实现近大远小的效果。
这几个属性我们下节细讲,先贴下照片墙的效果:
具体的实现我们在讲视点和视距属性时再分析。
又比如 3D 盒效果:
这是一个立方体盒子,每个面上都对应一张图片,当然,你也可以根据自己的需要往各个面上放置自己的内容。
立方体盒子的原理也是利用组合变换实现的:
- 前面:沿着 Z 轴往前(朝向屏幕外)平移指定像素。
- 后面,沿着 Z 轴往后(朝向屏幕里)平移指定像素。
- 上面,沿着 Y 轴往上平移,接着绕 X 轴旋转 90 度。
- 下面,沿着 Y 轴往下平移,接着绕 X 轴旋转 90 度。
- 左面,沿着 X 轴往左平移,接着绕 Y 轴旋转 90 度。
- 右面,沿着 X 轴往右平移,接着绕 Y 轴旋转 90 度。
当然,为了实现 3D 效果,也要在图片的父容器上设置 transform-style
为 preserve-3d
才可以。
这里主要展示组合变换顺序的理解与强大,不对实现做分析, 实现过程
留在下一节和 视点
以及 视距
一起介绍。
我们还是先把变换讲完,接下来介绍 transform
的另一个重要用法, matrix
和 matrix3d
。
matrix
transform 除了提供一些基本变换,还提供了 matrix
和 matrix3d
属性值,这两个属性值是做什么的呢?
matrix
是矩阵的意思,transform 是变换属性,所以 matrix 就是对 DOM 执行变换的矩阵,这和我们前面讲的 WebGL 的变换矩阵概念相同。
根据前面的学习,我们知道 2 维平面的变换矩阵,是一个 3 阶矩阵,包含 9 个数字:
按照我们之前章节坐标系变换的原理分析:
- x0、x1 代表变换之后的 X 轴基向量在原坐标系中的表示。
- y0、y1 代表变换之后的 Y 轴基向量在原坐标系中的表示。
- tx、ty 代表坐标原点的偏移量。
如果没有看之前章节的话,可能不太理解基向量的含义以及坐标系的概念,大家可以去看一下,理解一下坐标系变换的原理。
你会看到第三行的数值是固定的 0 0 1
,所以,浏览器为了简化赋值,规定 transform 中的 matrix 只接受 3 阶矩阵的前两行参数,共 6 个数字。
请记住,matrix 的参数顺序对应上面的矩阵元素如下:
transform: matrix(x0, x1, y0, y1, tx, ty); 复制代码
前面章节我们推导过基本变换的矩阵表示:
- 平移
沿 X 轴平移 tx 像素,沿 Y 轴平移 ty 像素:
- 缩放
沿 X 轴方向缩放 sx 倍, 沿 Y 轴缩放 sy 倍:
也就是说基本变换我们都可以用 matrix
来表示。
- 斜切 沿着 X 轴倾斜 α 度,沿着 Y 轴倾斜 θ 度:
- 旋转
绕 Z 轴旋转 θ 角度:
- 绕 Z 轴旋转 60 度。
用 rotateZ 表示上面的旋转很简单,我们看下用 matrix 如何表示:
将这两个数字代入 matrix 公式,得出变换样式为:
transform: matrix(0.5, 0.866, -0.866, 0.5, 0, 0); 复制代码
你会发现无论我们是用 rotateZ 表示,还是用 matrix 表示,变换效果都是一样的。
matrix3d
matrix3d,顾名思义,代表 3 维变换,它是一个 4 阶矩阵,需要 16 个数字来表示。
可以看到,每一个基向量都增加了一个 Z 轴方向分量 x2、y2、z2、tz。
事实上,3D 的基本变换都可以用 matrix 或者 matrix3d 来表示,但是一些复杂变换只能使用 matrix 或者 matrix3d 来实现。
matrix 对我们来说有什么用呢?
这是一个很关键的问题,大家会觉得,基本变换的用法更容易理解,更方便书写,matrix 需要的参数太多,而且参数值需要计算,更糟糕的是,我们往往不知道怎么计算,那 matrix 有什么用呢?
这是个好问题,但我想说的是 matrix 能完成基本变换不能完成的变换,能做出基本变换完成不了的效果。
这时你就该考虑使用 matrix 了。
紧跟而来的问题是,matrix 如何求得呢?
我们前面章节讲述了 matrix 矩阵的求法,一旦你确定了需要的变换,你就可以计算变换后的基向量,然后将基向量的各个分量代入矩阵的各个位置即可求出变换矩阵,有了变换矩阵,也就有了 matrix 所需要的各个元素,将矩阵转化成 matrix 或者 matrix3d 所需要的字符串就轻而易举了。
- 镜像效果
镜像效果,采用基本变换是实现不了的,只能借助于矩阵。 比如左右镜像,左右镜像无非就是 Y 轴基向量不变,X 轴坐标对调,原坐标与新坐标关系如下:
所以有:
将这些值,代入 matrix公式中,可以得出变换:
transform: matrix(-1,0,0,1,0,0); 复制代码
我们看下效果:
当然大家也可以举一反三,比如上下镜像:
transfom: matrix(1,0,0,-1,0,0) 复制代码
- 绕任意轴旋转。
绕任意轴的旋转,除了可以使用 rotate3d 来实现,还可以使用 matrix3d。这个旋转矩阵该如何计算呢?
还记得基本变换章节我们推导出的绕固定轴旋转的矩阵方法 axisRotation
吗?
axisRotation(axis, angle)
,其中 axis 是一个三维向量,angle 是一个弧度值。
这里我不准备举绕基本轴旋转的例子,因为它们不需要matrix3d 出手,我们想绕 {x:1, y: 1, z: 1}
的倾斜轴旋转,上面的 axisRotation 方法该出场了,它会为我们提供变换矩阵,但是我们还需要将变换矩阵转化为 css 样式,我们先封装一个矩阵转 css 的方法:
function matrix2css(mt){ var transformStyle = 'matrix('; if(mt.length == 16){ css = 'matrix3d(' } for(let i =0; i< mt.length; i++){ transformStyle += mt[i]; if(i !== mt.length - 1){ transformStyle += ',' }else{ transformStyle +=')' } } return transformStyle; } 复制代码
接着可以通过 axisRotation 方法计算出变换矩阵了,比如旋转 90 度。
let mt = matrix.axisRotation({x:1, y:1, z:1}, Math.PI / 180 * 90) 复制代码
在这里我用绕轴向量 axis = {x:1, y:1, z:1} 不停旋转的动画演示:
function render(){ if(!playing){ return; } angle ++; mt = matrix.axisRotation({x: 1,y: 1,z: 1}, Math.PI / 180 * angle); let css = matrix2css(mt); $$('.box')[0].style.transform = css; requestAnimationFrame(render); } document.body.addEventListener('click',function(){ playing = !playing; render(); }) 复制代码
是不是很简单呢?
总结
transform 中的基本变换都可以使用 matrix 和 matrix3d 来表示,只有当基本变换表示不了我们的变换时,我们才考虑使用 matrix 或者 matrix3d 。
可见,即使不做 WebGL 开发,我们之前学到的内容也会对普通开发者大有帮助,掌握变换原理,配合 CSS3 中的 3D 属性,一样可以做出很酷炫的 3D 动画效果。
回顾
本节讲述了 CSS 中的 3D 变换,以及它们与 WebGL 变换的相同之处。总的来说,基本原理都是一样的,之前封装的数学矩阵库,不仅可以用在 WebGL 领域,也可以用在 css 领域。
下一节,我们讲述 CSS 中 3D 变换 的其它几个重要属性, 变换类型(transform-style)
、 视点(perspective-origin)
、 视距(perspective)
。
小册
这一系列的内容来自于小册 WebGL 3D 入门与实践,如果大家对进阶知识感兴趣,可以到小册中去学习: 小册:WebGL 3D 入门与实践 。
小册内容除了包含 WebGL 相关的基础练习,还包括 3D 图形概念与相关数学的原理与推导,旨在帮助大家建立图形学的技术轮廓。这部分图形学知识独立于 WebGL,除了可以适用于 WebGL,还适用于 OpenGL 等。
当然,本小册主要目的还是帮助 Web 前端同学学习 3D 技术,除了介绍适用 WebGL 实现 3D 效果以外,还对 CSS3 中的 3D 技术相关属性进行了深入剖析,并演示了与数学库的结合使用,希望能够让前端同学不仅仅局限于一般的二维平面开发,也能够在 3D 开发上更近一步~
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Spring MVC 教程,快速入门,深入分析
- Vue入门学习之技术分享-2(深入理解Vue组件)
- WebGL 3D 入门系列:绘制渐变三角形 --- 深入理解缓冲区
- 一起入门gradle自定义插件编写(二) - 深入理解build.gradle
- 深入浅出 spring-data-elasticsearch 系列 – 概述及入门(二)
- 【1】JavaScript 基础深入——数据类型深入理解与总结
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。