CSS - 2D、3D 转换动画

一、 2D 转换(transform)

转换(transform):可以实现元素的位移、旋转、缩放等效果

2D 转换: 改变标签在二维平面上的位置和形状

::: warning

如果 既有位移,又有旋转、缩放时,需要先写位移 再写其他,否则方向会跑偏

1
2
3
/* 位移 旋转 缩放 */

transform: translate(50% 50%) rotate(180deg) scale(1.2em)

:::

::: details

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>demo</title>
<style>
* {
padding: 0;
margin: 0;
}
.demo {
margin: 0 auto;
margin-top: 20px;
width: 300px;
}
.box {
margin: 0 auto;
width: 300px;
height: 200px;
font-size: 20px;
background-color: bisque;
}
.box img {
width: 300px;
transition: 1s;
}

.box:hover img {
/* 先写位移,再做旋转 放大 */
transform: translateY(100px) rotate(360deg) scale(2);
}
</style>
</head>
<body>
<div class="demo">
<p>2D 转换</p>
<div class="box">
<img src="https://img.pupper.cn/img/202111291344063.jpg" alt="">
</div>
</div>
</body>
</html>

:::

1. 2D 转换 —- 移动(translate)

语法:

1
2
3
transform: translate(x, y);
transform: translateX(n);
transform: translateY(n);

::: note

  1. translate 不会影响其他元素的位置;

  2. translate 中,如果使用百分比,则是移动 自身元素大小 的百分比;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /* 垂直水平居中 */

    div {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 200px;
    hight: 200px;

    margin-top: -100px;
    margin-left: -100px;

    /* 等价于 */

    transfrom: translate(-50%, -50%);
    }
  3. translate 对 行内元素 没有效果

:::

::: details

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>CSS3 过渡效果</title>
<style>
* {
padding: 0;
margin: 0;
}
.demo {
margin: 100px auto;
width: 300px;
}
.demo div {
float: left;
width: 100px;
height: 100px;
}
.demo div:first-child {
background-color: aquamarine;
transition: 0.5s;
}
.demo div:last-child {
background-color: bisque;
margin-left: 100px;
transition: 0.5s;
}
.demo div:first-child:hover {
transform: translateY(-50px);
}
.demo div:last-child:hover {
transform: translateX(-100px);
}

</style>
</head>
<body>
<div class="demo">
<div>鼠标滑过,元素移动</div>
<div>鼠标滑过,元素移动</div>
</div>
</body>
</html>

:::

2. 2D 转换 —- 旋转(rotate)

语法:

1
transform: rotate(度数)

::: note

  1. rotate 里面 度数的单位为 deg,如:rotate(45deg)
  2. 度数为正时,顺时针旋转,度数为负时,逆时针旋转
  3. 默认旋转中心点为元素的中心点

:::

::: details

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>demo</title>
<style>
* {
padding: 0;
margin: 0;
}
.demo {
margin: 0 auto;
margin-top: 20px;
width: 500px;
height: 500px;
font-size: 30px;
}
.demo img {
/* 过度需要写在元素本身,不能写在 hover 中 */
transition: 1s;
}
.demo img:hover {
transform: rotate(360deg);
}
</style>
</head>
<body>
<div class="demo">
鼠标滑过,图片旋转
<img src="https://img.pupper.cn/img/202111291344063.jpg" alt="">
</div>
</body>
</html>

:::

3. 设置旋转中心点(transform-origin)

语法:

1
transform-origin: x y;	

::: note

  1. x 和 y 需要用 空格 隔开
  2. x 和 y 默认旋转中心点 是 元素中心点 (50% 50%)
  3. 可以给 x 和 y 设置 像素 或 方位名词 (top bottom left right center)
  4. 旋转中心需要设置在元素本身的样式中,不能设置在 hover 中

:::

::: details

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>demo</title>
<style>
* {
padding: 0;
margin: 0;
}
.demo {
margin: 0 auto;
margin-top: 20px;
width: 300px;
}
.box {
margin: 0 auto;
width: 300px;
height: 200px;
font-size: 20px;
background-color: bisque;
overflow: hidden;
}
.box img {
width: 300px;
/* 过度需要写在元素本身,不能写在 hover 中 */
transition: 0.5s;
transform-origin: left bottom;
transform: rotate(180deg);
}
.box:hover img {
transform: rotate(0deg);
}
</style>
</head>
<body>
<div class="demo">
<p>鼠标滑过,图片出现</p>
<div class="box">
<img src="https://img.pupper.cn/img/202111291344063.jpg" alt="">
</div>
</div>
</body>
</html>

:::

4. 2D 转换 —- 缩放(scale)

语法:

1
transform: scale(x, y);

::: note

  1. x 和 y 用 逗号 隔开
  2. transform: scale(1, 1) : 相当于没有放大
  3. transform: scale(3) : 如果一个值时,默认两个值一样,等价于 scale(3, 3)
  4. 可以设置缩放中心点,不影响其他盒子

:::

::: details

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>demo</title>
<style>
* {
padding: 0;
margin: 0;
}
.demo {
margin: 0 auto;
margin-top: 20px;
width: 300px;
}
.box {
margin: 0 auto;
width: 300px;
height: 200px;
font-size: 20px;
background-color: bisque;
overflow: hidden;
}
.box img {
width: 300px;
transition: 0.5s;
}
.box:hover img {
transform: scale(1.3);
}
</style>
</head>
<body>
<div class="demo">
<p>鼠标滑过,图片放大</p>
<div class="box">
<img src="https://img.pupper.cn/img/202111291344063.jpg" alt="">
</div>
</div>
</body>
</html>

:::

二、 动画

动画需要 先定义 再 调用

属性描述
@keyframes规定动画
animation所有动画属性的简写,除了 animation-play-state属性
animation-name动画名称(必须)
animation-duration动画时间,默认为 0 (必须)
animation-timing-function动画的速度曲线,默认是 easelinear(匀速)、steps(步长)
animation-delay动画何时开始,默认为0
animation-iteration-count动画播放次数,默认为1,还有 infinite(无限)
animation-direction是否逆向播放,默认为 normal, alternate(逆播放)
animation-play-state是否正在运行或暂停,默认 running, 还有 paused
animation-fill-mode动画结束状态,保持(forwards),回到起始(backwards)

1. 定义动画(keyframes)

语法:

1
2
3
4
5
6
7
8
@keyframes 动画名称 {
0% {
动画样式
}
100% {
动画样式
}
}

::: note

动画序列:

  1. 0% 是动画开始,100% 是动画完成
  2. 动画定义在 @keyframes
  3. 使用百分比来控制动画发生的时间,或用关键字 fromto,等同于 0%100%

:::

2. 调用动画(animation-name)

语法:

1
2
3
4
5
6
7
选择器 {
/* 调用动画 */
animation-name: 动画名称

/* 持续时间 */
animation-duration: 持续时间
}

3. 简写

::: note

简写规则:

animation: 动画名称 持续时间 运动曲线 何时开始 播放次数 是否方向 起始或结束状态

1
animation: myfirst 5s linnear 2s infinite alternate;

:::

1
2
3
4
5
6
7
8
9
.box img {
/* 普通写法 */
/* animation-name: demo;
animation-duration: 10s;
animation-iteration-count: infinite; */

/* 简写 */
animation: demo 10s infinite;
}

::: details

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>demo</title>
<style>
* {
padding: 0;
margin: 0;
}
.box {
width: 500px;
height: 500px;
}
.box img {
width: 300px;
}
@keyframes demo {
0% {
transform: translate(0,0);
}
20% {
transform: translateX(500px) rotate(720deg);
}
50% {
transform: translate(500px, 500px);
}
70% {
transform: translate(0, 500px) rotate(-720deg);
}
100% {
transform: translate(0,0);
}
}
.box img {
/* 普通写法 */
/* animation-name: demo;
animation-duration: 10s;
animation-iteration-count: infinite; */

/* 简写 */
animation: demo 10s infinite;
}

</style>
</head>
<body>
<div class="demo">
<div class="box">
<img src="https://img.pupper.cn/img/202111291344063.jpg" alt="">
</div>
</div>
</body>
</html>

:::

::: details

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>demo</title>
<style>
* {
padding: 0;
margin: 0;
}
@keyframes xiong {
0% {
background-position: 0 0;
}
100% {
background-position: -1600px 0;
}
}
@keyframes zhong {
0% {
left: 0;
}
100% {
left: 50%;
transform: translateX(-50%);
}
}
.demo {
background-color: #ccc;
width: 100%;
height: 1000px;
}
.box {
position: absolute;
width: 200px;
height: 100px;
background: url(https://img.pupper.cn/img/202112021756966.png) no-repeat;
animation: xiong 0.7s steps(8) infinite, zhong 3s forwards;
}

</style>
</head>
<body>
<div class="demo">
<div class="box">
</div>
</div>
</body>
</html>

:::

三、3D 转换

三维坐标系:

  • x 轴 : 水平向右(向右为正值,向左为负值)
  • y 轴 : 垂直向下(向下为正值,向上为负值)
  • z 轴 : 垂直向屏幕(向外为正值,向内为负值)

透视 (perspective)

透视(perspective):也叫 视距,可以将3D效果在 网页中显示出来,视距越小,图像越大,视距越大,图像越小

::: warning

透视(perspective) 需要写在被观察元素的 父盒子 上;

视距:眼睛到屏幕的距离,视距越小,图像越大;

z 轴: 物体距离屏幕的距离,z 轴越大,图像越大;

:::

1. 3D 移动(translate3d)

语法:

1
2
3
4
5
transform: translateX(x)
transform: translateY(x)
transform: translateZ(x)

transform: translate3d(x, y, z)

注意事项:

  1. x 轴 的单位一般都是 px
  2. x,y,z 不能省略,如果没有就写 0 ;
  3. 3d 效果需要搭配 透视(perspective) 才能显示效果;

2. 3D 旋转(rotate3d)

3D旋转遵守 “左手法则”,大拇指指向旋转轴的方向,四指指向旋转(正值)的方向

语法:

1
2
3
4
5
6
transform: rotateX(100deg);
transform: rotateY(100deg);
transform: rotateZ(100deg);

transform: rotate3d(x, y, z, deg)
如:transform: rotate3d(1, 1, 0, 180deg)

::: details

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>demo</title>
<style>
* {
padding: 0;
margin: 0;
}

.box {
width: 900px;
height: 300px;
margin: 100px auto;
perspective: 500px;

}
.box div {
/* float: left; */
width: 280px;
height: 200px;
margin-top: 60px;
background: url(https://img.pupper.cn/img/202111291344063.jpg) center;
transition: all 1s;
}
.box div:first-child:hover {
transform: translateX(100px) rotateX(360deg);
}
.box div:nth-child(2):hover {
transform: translateY(100px) rotateY(360deg);
}
.box div:last-child:hover {
transform: translateZ(100px) rotateZ(360deg);
}

</style>
</head>
<body>
<div class="demo">
<div class="box">
<div></div>
<div></div>
<div></div>
</div>
</div>
</body>
</html>

:::

3. 3D呈现(transform-style)

transfrom-style : 控制 子元素 是否开启三维立体环境

语法:

1
transform-style: flat(不开启,默认) | preserve-3d(开启)

注意:

  1. transfrom-style 是写在 父元素 中的,但是会影响子元素

::: details

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>demo</title>
<style>
* {
padding: 0;
margin: 0;
}
.box {
position: relative;
width: 200px;
height: 200px;
margin: 100px auto;
perspective: 500px;
transform-style: preserve-3d;
transition: all 1s;
}
.box div {
position: absolute;
width: 200px;
height: 200px;
margin: 0 auto;
line-height: 200px;
background-color: beige;
}
.box div:last-child {
background-color: aquamarine;
transform: rotateX(60deg);
}
.box:hover {
transform: rotateY(360deg);
}

.box2 {
position: relative;
margin: 0 auto;
width: 200px;
height: 200px;
perspective: 400px;
transform-style: preserve-3d;
transition: all .4s;
}
.box2 div {
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
background-color: blueviolet;
text-align: center;
line-height: 200px;
color: #fff;
font-size: 30px;
z-index: 10;
}
.box2 div:last-child {
background-color: brown;
transform: rotateY(180deg);
z-index: 1;
}
.box2:hover {
transform: rotateY(180deg);
}

</style>
</head>
<body>
<div class="demo">
<div class="box">
<div></div>
<div></div>
</div>
<div class="box2">
<div>这是正面</div>
<div>这是背面</div>
</div>
</div>
</body>
</html>

:::

案例一

::: details

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>demo</title>
<style>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
list-style: none;
}

.box {
height: 50px;
width: 800px;
margin: 100px auto;
/* 元素呈现3D效果需要添加 视距 */
perspective: 500px;
}

.box ul li {
position: relative;
margin: 0 5px;
float: left;
height: 50px;
width: 150px;
text-align: center;
line-height: 50px;
/* 控制 子元素维持 3D 环境 */
transform-style: preserve-3d;
transition: all .5s;

}

.box ul li p {
width: 150px;
position: absolute;
left: 0;
top: 0;
background-color: aqua;
color: #fff;
font-size: 20px;
}

.box ul li p:first-child {
background-color: blueviolet;
transform: translateZ(25px);
}

.box ul li p:last-child {
transform: translateY(25px) rotateX(-90deg);
}

.box ul li:hover {
transform: rotateX(90deg);
}
</style>
</head>

<body>
<div class="demo">
<div class="box">
<ul>
<li>
<p>我是菜鸟</p>
<p>我爱学习</p>
</li>
<li>
<p>我是菜鸟</p>
<p>我爱学习</p>
</li>
<li>
<p>我是菜鸟</p>
<p>我爱学习</p>
</li>
<li>
<p>我是菜鸟</p>
<p>我爱学习</p>
</li>
<li>
<p>我是菜鸟</p>
<p>我爱学习</p>
</li>
</ul>
</div>
</div>
</body>

</html>

:::

案例二

::: details

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<!DOCTYPE html>
<html lang="ch-NG">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
perspective: 3000px;
}
.demo {
position: relative;
width: 1000px;
height: 600px;
margin: 50px auto;
transform-style: preserve-3d;
}
.demo div {
position: absolute;
left: 350px;
top: 150px;
width: 300px;
height: 300px;

}
/* 先旋转后移动 */
.demo div:nth-child(1) {
background: url(https://img.pupper.cn/img/202112041024160.jpg) no-repeat;
background-size: 100%;
transform: translateZ(500px);
}
.demo div:nth-child(2) {
background: url(https://img.pupper.cn/img/202112041024160.jpg) no-repeat;
background-size: 100%;
transform: rotateY(60deg) translateZ(500px) ;
}
.demo div:nth-child(3) {
background: url(https://img.pupper.cn/img/202112041024160.jpg) no-repeat;
background-size: 100%;
transform: rotateY(120deg) translateZ(500px) ;
}
.demo div:nth-child(4) {
background: url(https://img.pupper.cn/img/202112041024160.jpg) no-repeat;
background-size: 100%;
transform: rotateY(180deg) translateZ(500px);
}
.demo div:nth-child(5) {
background: url(https://img.pupper.cn/img/202112041024160.jpg) no-repeat;
background-size: 100%;
transform: rotateY(240deg) translateZ(500px) ;
}
.demo div:nth-child(6) {
background: url(https://img.pupper.cn/img/202112041024160.jpg) no-repeat;
background-size: 100%;
transform: rotateY(300deg) translateZ(500px);
}
@keyframes demo {
0% {
transform: rotateY(0deg);
}
100% {
transform: rotateY(360deg);
}
}
.demo {
animation: demo 10s linear infinite;
}
.demo:hover {
animation-play-state: paused;
}
</style>
</head>
<body>
<div class="demo">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</body>
</html>

:::