3D坐标轴
前端中的3D坐标轴和我们印象中的有点不一样,我们可以这样记,屏幕水平(左右)方向为X轴,垂直(上下)方向为Y轴,面向穿过你自己为Z轴,我是这样记的,感觉比较好记
景深,舞台,灭点和景深基点
要在前端页面上实现3D效果,我们必须明确以下几个概念
景深(perspective):是指相机对焦点前后相对清晰的成像范围
太抽象了,简单的理解就是我们肉眼离显示器的距离,景深越大,元素离我们越近,效果越不好,景深作用于所有后代元素,并且景深会叠加
舞台(transform-style):应用景深的元素称为"舞台元素",舞台元素的所有后代元素都会受影响,作用在于营造有层级的3D舞台,不可继承
灭点:指的是立体图形两边的延伸线所产生的相交点,透视点的消失点。
百度的图片,看一下吧
我们只需要知道,景深越大,灭点越远,元素变形越不明显,反之亦然
景深基点(perspective-origin):用来控制视角的位置,简单来说就是你眼睛的位置
backface-visibility属性:用于设置元素背面是否为可见
3D变换
同2D的基本一致,就是多了一个Z轴的变换
- 3D旋转
roateX(角度) rotateY() rotateZ()
rotate3d(x,y,z) 按照原点到点(x,y,z)的射线转
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
html,body{
height: 100%;
overflow: hidden;
}
#wrap{
width: 400px;
height: 400px;
border: 1px solid;
position: absolute;
left: 50%;
top: 50%;
margin-top: -200px;
margin-left: -200px;
perspective: 120px;
}
#inner{
width: 200px;
height: 200px;
background: pink;
border: 1px solid;
border-radius: 50%;
position: absolute;
left: 50%;
top: 50%;
margin-top: -100px;
margin-left: -100px;
text-align: center;
font: 30px/200px "微软雅黑";
transition: 2s;
}
#wrap:hover>#inner{
/* transform: rotateY(360deg); */
transform: rotate3d(1,1,1,360deg);
}
</style>
</head>
<body>
<div id="wrap">
<div id="inner">
月下Ctrlc
</div>
</div>
</body>
</html>
- 3D平移
translateX() translateY() translateZ()
translate3d(x,y,z) z轴上偏移量不能写百分比,因为元素没有厚度
例子 就在上面那个例子的基础上改就行了 - 3D缩放
scaleZ() 意义在影响Z轴上的平移,一个东西离你近了看起来就大了
scaleZ(2) translateZ(100px) 就相当于 translateZ(200px)
scale3d(x,y,z)
例子自己对应修改吧
3D实例
- 立方体(第一种实现)
先展开,后旋转
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
#wrap{
width: 400px;
height: 400px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
border: 1px solid;
perspective: 100px;
transform-style: preserve-3d;
}
#wrap>.box{
width: 100px;
height: 100px;
position: absolute;
left: 50%;
top: 50%;
margin-top: -50px;
margin-left: -50px;
text-align: center;
font: 50px/100px "微软雅黑";
transform-style: preserve-3d;
transform-origin: center center -50px;
transition: 3s;
}
#wrap>.box>div{
width: 100px;
height: 100px;
background: pink;
position: absolute;
border-radius: 50%;
}
#wrap>.box>div:nth-child(5){
top: -100px;
transform-origin: bottom;
transform: rotateX(90deg);
}
#wrap>.box>div:nth-child(6){
bottom: -100px;
transform-origin: top;
transform: rotateX(-90deg);
}
#wrap>.box>div:nth-child(3){
left: -100px;
transform-origin: right;
transform: rotateY(-90deg);
}
#wrap>.box>div:nth-child(4){
right: -100px;
transform-origin: left;
transform: rotateY(90deg);
}
#wrap>.box>div:nth-child(2){
transform: translateZ(-100px) rotateX(180deg);
}
#wrap>.box>div:nth-child(1){
z-index: 1;
}
#wrap:hover>.box{
transform: rotateX(360deg);
}
</style>
</head>
<body>
<div id="wrap">
<div class="box">
<div>前</div>
<div>后</div>
<div>左</div>
<div>右</div>
<div>上</div>
<div>下</div>
</div>
</div>
</body>
</html>
- 立方体(第二种实现)
直接旋转
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
#wrap{
width: 400px;
height: 400px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
border: 1px solid;
perspective: 100px;
}
#wrap>.box{
width: 100px;
height: 100px;
position: absolute;
left: 50%;
top: 50%;
margin-top: -50px;
margin-left: -50px;
text-align: center;
font: 50px/100px "微软雅黑";
transform-style: preserve-3d;
transform-origin: center center -50px;
transition: 3s;
}
#wrap>.box>div{
width: 100px;
height: 100px;
background: pink;
position: absolute;
transform-origin: center center -50px;
/* 设置背面为不可见 */
backface-visibility: hidden;
}
#wrap>.box>div:nth-child(5){
transform: rotateX(90deg);
}
#wrap>.box>div:nth-child(6){
transform: rotateX(270deg);
}
#wrap>.box>div:nth-child(4){
transform: rotateY(90deg);
}
#wrap>.box>div:nth-child(3){
transform: rotateY(270deg);
}
#wrap>.box>div:nth-child(2){
transform: rotateY(180deg) rotate(180deg);
}
#wrap>.box>div:nth-child(1){
}
#wrap:hover>.box{
transform: rotateX(360deg);
}
</style>
</head>
<body>
<div id="wrap">
<div class="box">
<div>前</div>
<div>后</div>
<div>左</div>
<div>右</div>
<div>上</div>
<div>下</div>
</div>
</div>
</body>
</html>
- 多棱柱(封装成函数)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
html,body{
height: 100%;
overflow: hidden;
}
#wrap{
width: 400px;
height: 400px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
border: 1px solid;
perspective: 500px;
}
#wrap>.box{
/* 这里可以改变宽度,获取不同宽度的棱柱 */
/* 记得下面的居中也要改一下 */
width: 300px;
height: 300px;
position: absolute;
left: 50%;
top: 50%;
margin-top: -150px;
margin-left: -150px;
text-align: center;
font: 50px/300px "微软雅黑";
transform-style: preserve-3d;
/* 过渡只给transform,不然基点也会过渡,出现不好的效果,打你脸上 */
transition: transform 3s;
}
#wrap>.box>div{
width: 100%;
height: 100%;
background: pink;
position: absolute;
/* 设置背面为不可见 */
backface-visibility: hidden;
}
#wrap:hover>.box{
transform: rotateY(360deg);
}
</style>
</head>
<body>
<div id="wrap">
<div class="box"></div>
</div>
</body>
<script type="text/javascript">
window.onload = function(){
createLZ(10);
}
function createLZ(n){
var boxNode = document.querySelector("#wrap>.box");
var styleNode = document.createElement("style");
var degOut = 360 / n; //外角
var degIn = 180 - 360/n; //内角
var length = boxNode.offsetWidth;
var divHTML = "";
var cssHTML = "";
for(let i = 0; i < n; i++){
divHTML += `<div>${i+1}</div>`;
cssHTML += `#wrap>.box>div:nth-child(${i+1}){transform: rotateY(${i*degOut}deg)}`;
}
cssHTML += `#wrap>.box{transform-origin: center center ${-(length/2)*Math.tan(degIn*Math.PI/360)}px;}`;
cssHTML += `#wrap>.box>div{transform-origin: center center ${-(length/2)*Math.tan(degIn*Math.PI/360)}px;}`;
boxNode.innerHTML = divHTML;
styleNode.innerHTML = cssHTML;
document.head.appendChild(styleNode);
}
</script>
</html>
动画
-
API
1.1 动画内的属性
animation-name 指定动画的名称,默认值为none,表示无关键帧
animation-duration 指定一个动画的执行周期,默认为0,负值无效,低版本的浏览器会将负值解析为0
animation-timing-function 运动形式,可以指定贝塞尔函数,我讲不清楚,大家自己百度吧,贝赛尔
animation-direction 运动的方向,指定参数为reverse的时候,反转的是关键帧和运动形式 -
动画外的属性
2.1 animation-delay:指定动画开始之前的延迟 -
imation-iteration-count 指定动画的执行次数,动画内的范畴,不影响动画外的属性,重复的是关键帧
-
animation-fill-mode 确定元素在动画外的状态
动画外的状态指的是from之前to之后
参数:
backwards from之前的状态与from的状态保持一致
forwards to之后的状态与to的状态保持一致
both backwards和forwards两者加起来的效果
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
#wrap{
position: absolute;
width: 300px;
height: 300px;
border: 1px solid;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}
#wrap>.inner{
position: absolute;
left: 50%;
top: 50%;
margin-left: -50px;
margin-top: -50px;
height: 100px;
width: 100px;
background: pink;
text-align: center;
font: 20px/100px "微软雅黑";
animation-name: move;
animation-duration: 2s;
animation-timing-function: linear;
animation-direction: normal;
animation-delay: 3s;
animation-iteration-count: 3;
/* 确定元素在动画外的状态 */
/* 动画外的状态指的是from之前to之后 */
/*
backwards: from之前的状态与form的状态保持一致
forwards: to之后的状态与to的状态保持一致
both: 两者都保持
*/
animation-fill-mode: both;
}
@keyframes move{
from{transform: translateY(-100px);}
to{transform: translateY(100px);}
}
#wrap:hover>.inner{
animation-play-state: paused;
}
</style>
</head>
<body>
<div id="wrap">
<div class="inner">
月下Ctrlc
</div>
</div>
</body>
</html>
- 关键帧 (定义动画的形式)
@keyframes animationName {
keyframes-selector {
CSS-style;
}
}
animationName 定义动画的名称 必须写
keyframes-selector 必须写,动画持续时间的百分比 from(0%) to(100%)
CSS-style CSS声明
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
#wrap{
position: absolute;
width: 300px;
height: 300px;
border: 1px solid;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}
#wrap>.inner{
position: absolute;
left: 50%;
top: 50%;
margin-left: -50px;
margin-top: -50px;
height: 100px;
width: 100px;
background: pink;
text-align: center;
font: 20px/100px "微软雅黑";
animation-name: move;
animation-duration: 4s;
animation-timing-function: ease-in;
animation-direction: alternate;
animation-delay: 2s;
animation-iteration-count: 3;
animation-fill-mode: both;
}
@keyframes move{
/* 百分比平分的是动画执行一次的周期animation-duration */
/* 运动形式animation-timing-function体现在每个区间 */
/* ease-in就表示没0~25,25~50等区间都是采用ease-in的运动形式 */
0%{transform: translateY(-100px);}
25%{transform: translateY(-50px);}
50%{transform: translateY(0px);}
100%{transform: translateY(50px);}
}
#wrap:hover>.inner{
animation-play-state: paused;
}
</style>
</head>
<body>
<div id="wrap">
<div class="inner">
月下Ctrlc
</div>
</div>
</body>
</html>
- 应用
兔斯基(图片轮播)
使用动画依次从左到右依次展示图片,因为视觉暂留的原理,看起来就是动画
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
*{
margin: 0;
padding: 0;
}
html,body{
height: 100%;
overflow: hidden;
}
#tsj{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
width: 48px;
height: 48px;
background: url(01_3D&动画/img/animation.png) no-repeat;
/* start看不见第一帧,end看不见最后一帧 */
animation: move .8s steps(12,end) infinite;
}
@keyframes move{
from{background-position: 0 0;}
to{background-position: -576px 0;}
}
</style>
</head>
<body>
<div id="tsj">
</div>
</body>
</html>
开机动画(上下跳动的文字,延迟设为不同便可实现)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- 移动端开发记得加这个标签 -->
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no"/>
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
html,body{
height: 100%;
overflow: hidden;
}
#wrap{
height: 100%;
background: black;
position: relative;
}
#wrap>.inner{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
white-space: nowrap;
}
#wrap>.inner>span{
position: relative;
/* animation: jump 1s linear infinite alternate; */
}
@keyframes jump{
from{top: 0px}
to{top: -12px;}
}
</style>
</head>
<body>
<div id="wrap">
<div class="inner">
<span>玲</span>
<span>珑</span>
<span>骰</span>
<span>子</span>
<span>安</span>
<span>红</span>
<span>豆</span>
<span>入</span>
<span>骨</span>
<span>相</span>
<span>思</span>
<span>知</span>
<span>不</span>
<span>知</span>
</div>
</div>
</body>
<script type="text/javascript">
window.onload = function(){
var spanNodes = document.querySelectorAll("#wrap>.inner>span");
var colors = ['red','orange','yellow','green','blue','pink','purple','red','orange','yellow','green','blue','pink','purple'];
for(let i = 0; i < spanNodes.length; i++){
spanNodes[i].style.animation = `jump .3s ${i*50}ms linear infinite alternate`;
spanNodes[i].style.color = colors[i];
}
}
</script>
</html>
开机动画(3D版本)
data.js里面都是本地图片的地址
网络在谷歌浏览器上切换
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
html,body{
height: 100%;
overflow: hidden;
}
#wrap{
height: 100%;
position: relative;
perspective: 200px;
}
#wrap>.inner{
position: relative;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
transform-style: preserve-3d;
}
#wrap>.inner>img{
width: 30%;
margin-top: -45px;
animation: xuanzhuan 2s linear infinite;
}
#wrap>.inner>img,#wrap>.inner>p{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%) rotateY(0deg);
}
@keyframes xuanzhuan{
from{transform: translate(-50%,-50%) rotateY(0deg);}
to{transform: translate(-50%,-50%) rotateY(360deg);}
}
</style>
</head>
<body>
<div id="wrap">
<div class="inner">
<img src="img/load/logo3.png" >
<p>已加载0%</p>
</div>
</div>
</body>
<script src="js/data.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
window.onload = function(){
var arr = [];
var flag = 0;
var pNode = document.querySelector("#wrap>.inner>p");
//获取所有的图片地址链接数组
for(item in imgData){
arr = arr.concat(imgData[item]);
}
//模拟发ajax请求获取数据
//因为图片都在本地
//所以将浏览器Network conditions调为slow 3G 观看 效果才明显
for(let i = 0; i < arr.length; i++){
var img = new Image();
img.src = arr[i];
img.onload = function(){
flag++;
pNode.innerHTML = `已加载${Math.round(flag/arr.length*100)}%`;
}
img.onerror = function(){
console.log("加载失败!");
}
}
// console.log(arr);
}
</script>
</html>
好了,我的分享就到这里了,谢谢大家