您的位置:首页 > 移动开发

css3学习以及移动端开发基本概念的思考

2016-07-17 14:19 519 查看
html{
height:1000px;
background-color: red;
}
@media screen and (width:2560px){
html{
background-color: blue;
}
}
注意:首先必须弄清楚,我们的width/height值得是浏览器的可视区域的大小(缩小或者放大浏览器会发生变化),而device-width和浏览器的缩放是没有关系的。如我的iMac是

2560*1440,那么当我缩小的时候颜色是红色,而全屏的时候颜色就是蓝色了!然而,如果我查询device-width:2560px,那么不管我如果缩小放大浏览器都是一样的蓝色!

<meta name="viewport" content="width=400">
此时在所有的手机上都是document.document.clientWidth,也就是[layout viewport!!!]都是400px; 在渲染页面之前,浏览器需要知道layout viewport有多大,基于此浏览器可以计算比如20%的具体像素宽度是多少。如果没有指定,那么浏览器就会自己设定一个layout viewport。8个浏览器中有6个会设定为980px,在黑莓中或者ie10中,值为1024px。

html{
height:1000px;
background-color: red;
}
@media screen and (width:2580px){
html{
background-color: blue;
}
}
我们的responsive模块模拟的像素就是真实的css像素,与具体的dpr无关。但是这时候我们在console中确实可以打印真实的dpr的,也就是我们模拟的这个设备的dpr。如果我们模拟的设备没有设定dpr,那么就是2,因为我的设备的dpr就是2!如上面例子,我们把iMac的responsive中的width设置为2580会发现我们的背景色变成了蓝色了!

<meta name="viewport" content="width=device-width, initial-scale=1">
做媒体查询的时候一定要有上面这句代码:

document.documentElement.clientWidth
document.documentElement.clientHeight//获取layout viewport
window.innerWidth/window.innerHeight//获取ideal viewport
screen.width/screen.height反应的是整个电脑的属性,我的笔记本是2560*1600,而且dpr是2,因此打印1280*800(这是在destop的情况下的,不要打开模拟其他机型的情况下,请保持手机按钮是灰色的,否则就是模拟器了,会查询到模拟器的screen!!!!!)。哪怕你在responsive里面模拟不同的dpr,但是因为这时候是css像素同时是desktop,因此不会影响我的机器的screen.width/screen.height的。因为screen.width/screen.height只是和我们的显示器有关,无法模拟但是如果你设置为我们所说的mobile,这时候我们模拟的就是还是css像素,也就是说device-width查询的是css像素。

screen.width//在保持chrome手机按钮是灰色的情况下,返回的一直是显示器的宽度(不是设备像素而是CSS像素)
screen.width//如果手机按钮不是灰色,那么返回的是模拟的设备的screen.width,而且也是CSS像素
要记得这里的media一般用all,screen,print

@media screen and (width:2580px){//screen/all/print,在responsive界面有一个标志,表示的横竖屏的转换
html{
background-color: blue;
}
}
媒体查询和其在桌面环境上的工作方式一样。width/height使用layout viewport做为参照物,并且以CSS像素进行度量,device-width/device-height也是使用CSS像素进行度量。换句话说,width/height是document.documentElement.clientWidth/Height值的镜像,同时device-width/height是screen.width/height值的镜像。(它们在所有浏览器中实际上就是这么做的,即使这个镜像的值不正确。)

document.documentElement.clientWidth/clientHeight===width/height===layout viewport
window.innerWidth/innerHeight===ideal viewport//理想视口CSS宽度和高度,包含滚动条。这时候的initial-scale必须是1!
device-width/device-height==screen.width/screen.height//显示器的CSS宽度和CSS高度
window.pageXOffset和window.pageYOffset,包含了文档水平和垂直方向的滚动距离。所以你可以知道用户已经滚动了多少距离。可以在chrome中打开用iphone6+打开csdn调试

window.pageXOffset
window.pageYOffset


我们首先要弄清楚三个viewport:
ideal viewport(initial-sclae==1):这是我们最关心的viewport,他表示我们的视口到底有多少空间可以用于布局(如iphone6+一直是414,也就是指代的具体的设备宽度。特别注意:即使把initial-scale设置为0.5,那么因为visual viewport width
= ideal viewport width / zoom factor,所以ideal viewport也是不会变化的。这里的zoom指代的就是initial-scale,minum-scale,max-scale等。也就是说三个viewport中,ideal viewport是固定不变的,是通过ideal viewport来计算visual viewport和layout viewport)

visual viewport:他会受到用户缩放的影响,如果用户放大了那么其视口会变小。(如iphone6+的initial-scale设置为0.5,那么visiual viewport=414/0.5=818)
layout viewport:如果用户没有指定就是980px,如果指定了device-width就是设备宽度,如果指定了width=320px那么就是320px(如iphone6+在initial-scale=0.5的情况下就是818)
注意:我们只要记住visual viewport width = ideal viewport width / zoom factor,也就是说我们是通过ideal viewport来计算visual viewport,layout viewport的。如果我们需要解决1px问题,这时候的就需要考虑到zoom了,inital-scale如果小于1那么就能看到一个物理像素宽度的线条了!
如下面的例子:

<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="utf-8" />
<title>linear-gradient()_CSS参考手册_web前端开发参考手册系列</title>
<meta name="viewport" content="initial-scale=0.5">
<style>
*{
margin:0;
padding:0;
}
div{
background: -moz-repeating-linear-gradient(top left -45deg, #ace, #ace 5px, #f96 5px, #f96 10px);
background: -webkit-repeating-linear-gradient(top left -45deg, #ace, #ace 5px, #f96 5px, #f96 10px);
width: ;
height:818px;
}

</style>
</head>
<body>
<div class="test"></div>

</body>
</html>
这时候我们看到如下的效果:



注意:通过修改initial-scale的值,我们的layout viewport,ideal viewport都变化了!

viewport的功能是用来约束你网站中最顶级包含块元素(containing block)<html>的。那么<html>元素的宽度是多少?它的宽度和浏览器窗口宽度一样。这就是为什么你的那个拥有width: 10%属性的侧边栏会占据整个浏览器窗口的10%。所有web开发者都很直观的知道并且在使用它。你可能不知道的是这个行为在理论上是如何工作的。理论上,<html>元素的宽度是被viewport的宽度所限制的。<html>元素使用viewport宽度的100%。viewport,接着,实际上等于浏览器窗口:它就是那么定义的。viewport不是一个HTML结构,所以你不能用CSS来改变它。它在桌面环境下只是拥有浏览器窗口的宽度和高度

//html元素的宽度受到viewport的限制和约束,但是viewport不是一个html结构无法使用CSS来约束它
所以clientWidth/Height在所有情况下都提供viewport的尺寸。但是我们去哪里获取<html>元素本身的尺寸呢?它们存储在document.documentElement.offsetWidth和-Height之中。

document.documentElement.clientWidth//viewport尺寸
document.documentElement.offsetWidth//html尺寸


没有方法直接计算scale,也就是zoom的值。我们要计算visual viewport的宽度,因为他和zoom的值成反比,zoom越大那么visual viewport越小。而且zoom是相对于ideal viewport来说的。

[ideal viewport]= document.documentElement.clientWidth/Height//前提是设置了meta

[html元素的宽度]= document.documentElement.offsetWidth/Height//获取html元素的宽度

[layout viewport]=meta标签中设置的width

visual viewport width = ideal viewport width / zoom factor

zoom factor = ideal viewport width / visual viewport width

[Minimum and maximum zoom factors]=ideal viewport width / layout viewport width。这是因为根据公式zoom factor = ideal viewport width / visual viewport width,visual viewport width<=layout viewport width

initial-scale的作用

首先把页面初始的zoom值设置为一个指定的值,整个值是通过ideal viewport来计算的(通俗的讲就是通过intial-scale来获取visual viewport,然后把visual viewport设置为layout viewport)。因此会产生一个visual viewport width。然后把这个visual viewport width设置为layout viewport
width的值!

<p>
Setting he initial-scale directive actually does two things:
It sets the initial zoom factor of the page to the defined value, calculated relative to the ideal viewport. Thus it generates a visual viewport width.
It sets the layout viewport width to the visual viewport width it just calculated.
So let’s say we have an iPhone in portrait mode and give it an initial-scale=2 without any further instructions. By now, it shouldn’t surprise you that this sets the visual viewport width to 160px (= 320 / 2). That’s how the scaling directives work.
However, it also sets the width of the layout viewport to 160px. So we now have an 160px wide page in minimum zoom. (The visual viewport cannot become larger than the layout viewport, so zooming out is not possible.)
And no, this doesn’t make the slightest bit of sense. If asked for my candid opinion I’d probably mumble something like “completely fucking batshit insane.” Still, there’s no doubt that browsers behave like this.
</p>


好几个例子:

这里查询的是css像素,而我的hello设备是1920*1080的(dpr是3没有影响),因此背景变成红色了

@media screen and (max-width: 1920px) {
body {
background-color:red;
}
}
因为我的mac的device-width是1280,因此这里也是会打印红色的

@media screen and (device-width: 1280px) {
body {
background-color:red;
}
}
GalaxyS5我们的css宽度为360*640,而dpr为3,因此屏幕的像素为1080*1920,而我们的screen.width还是指代的是css像素,因此为360。因此我们可以得出结论:screen.width/screen.height都是指代的css像素,而不是屏幕真实的硬件像素。因此width/device-width的媒体查询都是查询css像素,这一点要弄清楚。如果是在电脑上,responsive就是返回mac的屏幕,否则就是模拟的手机的屏幕信息

@media screen and (device-width: 360px) {
body {
background-color:red;
}
}
这是我们的设备的宽度和高度的比值,iphone6+是9/16的

@media screen and (device-aspect-ratio: 9/16) {
body {
background-color:red;
}
}

很显然两个viewport都是以CSS像素度量的。但是当进行缩放(如果你放大,屏幕上的CSS像素会变少)的时候,visual viewport的尺寸会发生变化,layout viewport的尺寸仍然跟之前的一样(至少我觉得是不一样的,可以通过把nitial-scale进行缩放,然后打印document.documentElement.clientWidth)。

visual viewport宽度 = ideal viewport宽度  / 当前缩放值

当前缩放值 = ideal viewport宽度  / visual viewport宽度

所以当initial-scale=1的时候,我们的ideal viewport==visual viewport、只有当用用户zoom的时候两者才是不一样的【或者由于initial-scale不是设置为1那么因为存在缩放,那么ideal viewport一直是414,而visisual viewport存在变化】。

(1)我们首先设置了initial-scale=1,这时候ideal viewport=visual viewport、如果我们的当前缩放值不是1,那么如果要计算visual viewport可以按照上面的公式来计算。这是关于缩放值的,也就是zoom,这时候我们自己指定了initial-scale。如果我们自己不指定initial-scale(也就是不存在initial-scale/或者device-width来把可视区域宽度设置为ideal viewport),那么会存在一个默认的initial-scale,这个默认的scale是按照当前缩放值
= ideal viewport宽度 / visual viewport宽度来计算的,但是我们无法得到Visual viewport的大小,但是我们知道Visual viewport<=layout viewport,所以我们至少缩小了的值就是=ideal viewport/layout viewport。至于为什么Visual viewport是980,是因为View viewport代表的是可视区域的大小,在iPhone下会满足不会出现滚动条,因此visual viewport就是我们的layout
viewport(很显然这时候不是显示指定的initial-scale也就是不是相对于ideal viewport来说的了)。

(1.1)rem如何解决1px的问题

首先看一个现象,我们把initial-scale设置为1得到的document.documentElement.getBoundingClientRect().width为414px(在6plus下),如果我们把inital-scale设为0.5得到的就是818,那么这个值代表什么意义呢?

var width = docEl.getBoundingClientRect().width;
if (width / dpr > 540) {
width = 540 * dpr;
}
var rem = width / 10;
flexible.rem = win.rem = rem;
这一句代码调用,我们把宽度分为10份,如果在scale=1的时候,1rem=41.4px,在scale=0.5的情况下得到1rem=81.8px,但是很显然scale=0.5的大小是scale=1元素大小的一半(使用px来衡量)。对比下面的图:



这个是scale=0.5的图



这里是scale=1的图,我们看看代码:

<div style='width:100px;height:100px;background-color: #ccc;'></div>
很明显,这里使用的px为单位的元素大小,如果你使用rem为单位,那么你会发现initial-scale=0.5还是1没有任何区别!为啥呢?

<pre name="code" class="javascript"> function refreshRem(){
var width = docEl.getBoundingClientRect().width;
if (width / dpr > 540) {
width = 540 * dpr;
}
var rem = width / 10;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}



在scale=1的时候我们的width=414px,而在scale=0.5的时候我们的width=818px,但是屏幕的大小是没有发生变化的,所以该元素在initial-scale=1/2的情况下占据的宽度为:100/414和100/818,因此显然两者占据的宽度是不一样的!但是如果是rem为单位,那么情况就完全不一样了。
<div style='width:4rem;height:4rem;background-color: #ccc;'>111</div>
这时候我们的如果scale=1那么1rem=41.4px,但是如果是scale=0.5那么我们1rem=81.8px,因此元素占据的屏幕宽度始终是相等的:4*41.4/414=4*81.8/818!但是为什么要这么做呢?答案是:解决1px问题,因为在retina屏幕下1px的css像素会被解析为2的物理像素,这是我们无法容忍的,但是如果我们把scale=0.5那么就能够解决这个问题。

因为1rem=81.8px,也就相当于原来的1rem=41.4px,现在进行了更加细致的划分了,1px已经真正代表一个物理像素了。那么以后如果我需要设置一个200*200px的div那么我只要这么做:

第一步:设计师给我们750px像素的设计稿,而且这个设计稿既是物理像素也是逻辑CSS像素。同时viewport都不需要自己设置了,因为js已经自己运行了!!!

第二步:刚才那个js已经执行后,所以data-dpr=2,同时initial-scale,max-scale等都会设置为0.5,那么这时候我们的initial-scale设置为了0.5那么按照上面的分析1rem=750px

第三步:刚才那个js运行后,计算docEl.getBoundingClientRect().width得到375,所以其css宽度只有375px像素(因为他会取样,这样设置为750px设计稿保证在retina屏幕上显示正常)

第四步:元素的宽度为200/75rem*200/75rem。

总结:我们如果使用了这种方式,那么不能再使用px为单位了,因为scale后无法保证元素的大小是相同的,但是rem能够保证!同时这种方式也会使的1px问题得到解决。详见使用Flexible实现手淘H5页面的终端适配 #17。我们计算了iPhone6下的布局尺寸rem(750的设计稿对应于750的硬件像素),而且1rem=75px(硬件像素),因为我们计算出来的都是rem值,因此在其他设备上会通过js重新计算1rem所对应的硬件像素,从而做到1rem在不同的设备上通过cm计算的尺寸是相同的,只不过是重新计算了一次inital-scale和document.documentElement的值而已!

(2)前端设计给我两套图片,一套是@1X还有一套是@2X,如@1X是200*200,@2X是400*400,但是一定要注意:设计说的是CSS像素,但是因为他们只是考虑dpr是1的情况下的,所以也是物理像素,因此对于下面的div元素就有:

div{
height:200px;
width:200px;
}


如果在retina屏幕上,此时加载的是@2X的图片,也就是400*400的,但是因为dpr为2,所以css像素还是200*200的;然而对于非retina屏幕上,我们的加载的200*200的图片,但是因为dpr为1,因此也是200*200的css像素。那么为什么200*200在retina图上会模糊呢?原因其实很简单,因为加载的图片是200*200,也就是物理像素是200*200的,但是显示的div却显示的物理像素是400*400,因此原来的200*200的像素相当于被拉伸了,因此也就模糊了。一般我们的css遵循下面的规则:

div{
background-image:url(@1X);
}
@media screen(){
background-image:url(@2X);
}


那么很显然后面的图片是前面的图片的4倍大小,但是如何做到页面不回流的。理由很简单,因为后面加载的400*400的图片是物理像素,而dpr为2,因此还是200*200的css像素,因此其原来的div占据的大小是足够的,因此压根就不会回流!!!【所以实现既可以通过rem实现也可以通过px实现】
(3)box-shadow相关(box-shadow优先级低于background和border)

(3.1)inset下面,如果我们改变V距离,这时候如果是正数那么是从上往下的,否则就是从元素的下边往上边的(出现哦)。水平距离也是一样的道理

(3.2)模糊距离就是在水平或者垂直移动的距离上面再次进行二次模糊!不可以为负数,模糊方式是基于高斯模糊的,也就是基于加权平均数的方式来完成的

(3.3)扩展为负数可以用来抵消或者叠加水平或者垂直的距离

(3.4)注意我们的最终的层叠顺序,边框的层叠顺序是比较高的,会覆盖掉我们的box-shadow阴影,但是box-shadow是小于backgroung的

(3.5)我们的调试结果是:如果blur的距离越大,那么圆心都会被blur掉

(3.6)其实模糊不是基于圆心进行模糊的,而是在原来的投影的基础上模糊的,而且是往内部模糊的,也就是越往内部越模糊,红色越淡,所谓的blur表示的就是计算这个元素的颜色的时候使用周围元素的颜色的半径,因此当在外围的时候我们会越来越模糊,因为越在外面本身的颜色已经很淡了,那么模糊后更加淡!

box-shadow:inset 0 2rem 3rem -2rem white,
inset 0 -4rem 4rem -2.5rem black;
首先:第一个投影分析如下,2rem向下移动,-2rem的拓展表示又往上移动了2rem,所以最后的圆心还是在上边框上

这时候往下模糊3rem就是很显然的效果了,而且这种模糊是从圆心往外模糊的,模糊半径会在圆心两侧同时模糊,因此这里才称之为模糊半径。

第二个投影分析如下:-4rem表示往上移动4rem,然后拓展为-2.5rem,因为是负数表示收缩原来的移动,因此

最后只是往上移动了1.5rem,这时候模糊了4rem,所以所有的位置都模糊了

注意:inset时候是从圆心往外部模糊的,而且这里完全不用第一个投影;不对,一定要记住上面的模糊是半径,所以两边各一半才是正确的。而且对于水平方向的渐变来说第一个模糊压根就是不需要的!!!

之所以第一个模糊可以不需要是因为他是白色的,模糊后还是白色,因此当要保持一定宽度是纯白才有用

投影一开始在没有任何重叠的时候和原来的元素在同一个位置上,但是因为后面有了偏移而导致了移动:

div{
width:250px;
height:70px;
box-shadow:-24px -24px 0 -20px red,24px 24px 0 -20px blue;
border:1px solid blue;
}


这个div虽然第一个投影在原来的位置上向左和向上移动了24px,但是因为后面用了阴影外延值导致其阴影实际的大小只有4px。
使用box-shadow作边框有很多要注意的地方。注意使用时的层级关系(前篇有详细讲过),防止阴影出乎意料外的覆盖(毕竟其不影响布局),最后就是要考虑到阴影的大小始终是跟着box的高宽的,所以box宽高宽变化必须被考虑在内.
下面这个彩虹色边框你应该明白boxs-shadow的层级关系,也就是前面的box-shadow的优先级要比后面的优先级高:

div{
box-shadow:20px 15px 0px #3c3,
-20px 15px 0 #36c,
20px -15px 0px #c63,
-20px -15px 0px #cc3;
width:100px;
height:100px;
margin:40px;
border:1px solid red;
}
现在回到下面的复杂的例子:

<style>
.box{
float: left;
background: blue;
width: 100px;
height: 100px;
margin-right: 10px;
padding:0;
}
.box1{
box-shadow: 0 0 0 20px red inset;
}
.box2{
box-shadow: 10px 0 0 20px red inset;
}
.box3{
box-shadow: 10px 0 10px 20px red inset;
}
.box4{
box-shadow: 0 0 10px 40px red inset;
}
</style>
<div class="box box1"></div>
<div class="box box2"></div>
<div class="box box3"></div>
<div class="box box4"></div>
对于第一个box1来说,很显然容易理解。box2来说,首先水平移动10px,所以投影在原来的位置上在左边产生了10px的红色区域,然后再来说扩展20px,因为拓展和水平移动可以叠加,所以现在左边的红色距离是20+10=30px,但是因为上边和下边只是拓展了20px,所以最后的结果就是上下为20px,对于右边来说,因为左边移动了10px,所有右边有10px进入border下面了,所以必须要扩展10px后才会从右边出来,最后的结果就是20-10=10px。因此左边30px,上下20px,右边10px的效果!



注意:上面都是通过扩展和移动的叠加来产生的绚丽效果,如果仅仅是没有通过叠加而设置为0那么这种效果是无法满足的!之所以是这样,是因为要考虑到其他边的扩展情况,如果其他边压根没有扩展那么效果肯定不是我们想要的!下一个问题就是我们为什么要设置负数的扩展呢:
原因:是为了去除上下或左右的模糊,因为进行模糊的时候默认会从投影边开始,即使左右没有发生移动那么模糊的时候也会依据半径进行模糊,那么就不会形成我们需要的效果。

box-shadow:inset 0 2rem 3rem -2rem red;
效果如下:



这时候扩展了-2rem,那么左右的边界就会往里面收2rem,所以当模糊的边界是3rem,其实只有3/2=1.5rem那么左右还是不会出来的,所以只有上下的渐变。如果我们设置为下面的形式:

box-shadow:inset 0 0 3rem 0 red;



很显然,这时候左右都会扩展,而不是我们需要的仅仅上下扩展的情况,这就是为什么需要把左右扩展设置为负数的原因!

(4)text-shadow相关

text-shadow: 1px 1px 0 #f96,-1px -1px 0 #f96;
这个代码可以实现描边效果,因为第一个投影只是在元素的下边和右边有了红色的投影,但是左边和上边还没有投影的,所以就有了后面的第二个投影。因为同时投影的元素在往下移动的时候,虽然只是移动了几个像素,但是其他的像素都会被覆盖掉,因为投影的优先级,也就是层叠顺序是在是太低了,这一点一定要注意!

(5)rgba相关
RGBA存在的问题就是继承问题,解决的方法就是不让其成为父子关系而是设置为兄弟关系,结合position:absolute定位,让其一个作为背景而且z-index比较小,而另外一个作为真正的元素在背景上面z-index很大。对于IE来说就要使用fallback颜色,结合滤镜来实现

(6)border-radius相关
border-radius还有一个大值特性,也就是值很大的时候,只会使用能够渲染的圆角大小渲染(这也是为什么可以用一个很大的border-radius和一个50%的值,注意:如果宽度和高度不一样,这时候border-radius为50%就会按照宽度和高度的50%计算,得到的就是一个椭圆)。因此,要实现一个【正方形】元素的圆角效果(例如网站头像),我们还可以使用一个很大的圆角值,同样是不需要计算的;CSS3圆角除了大值特性,还有一个等比例特性,就是水平半径和垂直半径的比例是恒定不变的,也就是说如果你把border-radius设置为200px,那么水平和垂直就是1:1,如果你设置200/300那么就会按照2:3来设置!
.radius-test3 { width: 100px; height: 200px; border: 50px solid #cd0000; border-radius: 300px; }
上面的CSS水平方向是200px,垂直方向是300px,所以如果要转换成为椭圆只要设置border-radius:100px/150px,但是如果仅仅设置300px,那么表示水平垂直的border-radius要保持1:1,因此水平方向只能为100,垂直方向虽然可以是150,但是由于1:1的约束,最后只能也是100,最后就会形成跑道的效果!!!!
(7)transform,perspective相关
perspective属性用来设置视点,在css3的模型中,视点是在Z轴所在方向上的。
translateX,translateY表现出在屏幕中的上下左右移动,transformZ的直观表现形式就是大小变化,实质是XY平面相对于视点的远近变化(说远近就一定会说到离什么参照物远或近,在这里参照物就是perspective属性)。比如设置了perspective为200px;那么transformZ的值越接近200,就是离的越近,看上去也就越大,超过200就看不到了,因为相当于跑到后脑勺去了,你不可能看到自己的后脑勺。(200-transformZ的值)就是视点和xy平面的距离(初始是屏幕的位置,此时transformZ的值为0)。
需要注意的一点是,整个坐标系中各各坐标轴的相对关系是固定的。正常情况下,视点和我们的眼睛是同一个方向的如同上面所讲。而当执行如rotateX(90deg)等旋转变换时,zy平面旋转,z轴和y轴的指向也会变化90度。此时z轴指向上方(坐标系方向会变化),[视点也是在上方],此时我们从屏幕上看的就不是直观的元素大小变化,而是元素的升降变化,好像站在远处在看【一部电梯】那样(因为视角在Z轴上,所以是上下移动的了)
perspective属性用在容器上时,容器内每个元素的表现形式会不一样。当perspective属性用再容器内每个元素身上时,会根据各自的设置值进行表现。打个比方就是你一个人平视盒子里的10个鸡蛋和十个你每人看1个一模一样鸡蛋。(地址:http://m.blog.csdn.net/article/details?id=39003061
下面这个例子可以用于说明z轴旋转过后元素translateZ变化的具体表现:

<div><img src="images/a9.png"></div><!--正常放置,无变化-->
<div style="perspective:300px"><img src="images/a9.png" style="transform:translateZ(40px)"></div><!--z轴朝着屏幕,图片推向你所在的方向-->
<div style="perspective:300px"><img src="images/a9.png" style="transform:rotateX(90deg)"></div><!--Z轴朝天,视点在Z轴方向上-->
<div style="perspective:300px"><img src="images/a9.png" style="transform:rotateX(90deg) translateZ(60px)"></div><!--Z轴朝天,视点在Z轴方向上,朝视点推进,图片上升-->
<div style="perspective:300px"><img src="images/a9.png" style="transform:rotateX(90deg) translateZ(-60px)"></div><!--Z轴朝天,视点在Z轴方向上,朝视点远离,图片下降-->


perspective:
(1)元素越近,也就是perspective越小,那么如rotate等的作用越明显,反之越不明显



这里是perspective=70的,同时第二个A进行了rotateX=45的效果。



这里是perspective=700,同时第二个A进行了rotateX=45的效果。很显然,perspective越大那么表示长镜头,元素不会变形,越小表示广镜头,元素会变形!
perspective属性对于3D变形来说至关重要。该属性会设置查看者的位置,并将可视内容映射到一个视锥上,继而投到一个2D视平面上。如果不指定透视,则Z轴空间中的所有点将平铺到同一个2D视平面中,并且变换结果中将不存在景深概念。上面的描述可能让人难以理解一些,其实对于perspective属性,我们可以简单的理解为视距,用来设置用户和元素3D空间Z平面之间的距离。而其效应由他的值来决定,值越小,用户与3D空间Z平面距离越近,视觉效果更令人印象深刻;反之,值越大,用户与3D空间Z平面距离越远,视觉效果就很小。

上图的效果完全说明了一切。父节点没有设置perspective的情况下,梅花King的3D旋转效果不明显,而在父节点设置perspective后,梅花King才像个3D旋转

transform-style,这个属性预设为 flat,也就是只要是這个 div 內的子元素,一律都是以扁平 ( flat ) 的方式呈现,所属的变换 transform 也一律都是用 flat 的方式变换,换句话说就是沒有 Z 轴的存在,为了让內容元素都是立体元素,所以我们要將 transform-style
设为 3D。在沒有设定 box 的 translateZ 或 rotate,让 Z 的深度有所变化,摄影机透过 perspective 看出去的位置都是相同的,也造成不论怎么去看这个 box 都是一样的大小。

大概了解之后,来把 box 旋转一下角度,看得应该就会更清楚,当摄影机变成广角,也就是 perspective 变短,整个旋转后变形也会更加明显,大家可以用开发者工具修改 camera 的 perspective 就会明白。

我们加入多一点的 box,并且让这些 box 的位置改变或旋转,看看效果如何,这里比较需要注意的地方,是我们必须要额外在每个 box 加入 position:absolute 的属性,因为 div 本身为 block 属性,会互相挤压,要设定位置为绝对位置,才会正确地放在 space 里头。

例如今天我先让 box 在 X 轴上水平位移 100px 再绕着 Y 轴順时针转 60 度,和先绕 Y 轴順时针转 60 度,再在 X 轴上头水平位移 100px 的结果会完全不同,因为当我先绕了 Y 轴转动,整个 X 轴也会跟著转动,这时候再做水平位移,位置就会像是在深度做变换。[转载]玩轉
CSS 3D: 原理篇

chrome中发现一个问题,那就是perspective一定要在rotateY(或rotateX)的前面。

<div class="camera">
<div class="space">
<div class="box"></div>
</div>
</div>
这是perspective使用的经典模式,我们看看css代码:

.camera{
width:200px;
height:200px;
perspective-origin:center center;
/*视线距离元素的距离是500px,如果元素本身的translateZ到达500就会出现一叶障目的情况。但是如果translatez时负数而且特别大,这时候就会变成一个点,因为元素已经远远离开视线了*/
perspective:500px;
}
.space{
width:100%;
height:100%;
border:1px dashed #000;
/*空间元素必须设置transform-style:preserve-3d*/
transform-style:preserve-3d;
}
.box{
width:100px;
height:100px;
background:#069;
transform:translateX(50px) translateY(50px) translateZ(0px);
/*translateZ如果是负数而且非常小,如-10000就会发现元素已经看不见了*/
}
或者你也可以仔细研读[转载]玩轉 CSS 3D: 原理

(8)rotate部分(只要没有通过origin修改都会是元素的中心):
rotateZ:正数为顺时针,负数为逆时针!

.cube .front  {
transform: translateZ( 100px );
/*这里的translateZ的值时元素宽度的一半,也就是(196+4)/2=100px*/
border:1px solid blue;
}
.cube .back {
/*转动-180deg,之所以是这样是为了设置一个骰子的反向的2,也就是从镜子中看到的2的样式!!!*/
transform: rotateX( -180deg ) translateZ( 100px );
}
.cube .right {
transform: rotateY(   90deg ) translateZ( 100px );
}
/*rotateY时候,正数为逆时针移动,负数那么就是顺时针*/
.cube .left {
transform: rotateY(  -90deg ) translateZ( 100px );
}
.cube .top {
/*rotateX时候,正数为顺时针,负数为逆时针*/
transform: rotateX(   90deg ) translateZ( 100px );
}
.cube .bottom {
transform: rotateX(  -90deg ) translateZ( 100px );
}
注释:rotateX正数为顺时针,rotateY顺时针为负数!

(9)skew部分(只要没有通过origin修改都会是元素的中心,都是在同一个位置):

skewX:逆时针为正数,顺时针为负数(垂直中心线和Y轴的夹角,90°的时候元素变得很长,元素左边到达屏幕最前面,收缩消失,成为一条横线)
skewY:顺时针为正数,逆时针为负数(水平中心线于X轴夹角,90°的时候底边在最前面并收缩成为一条线,成为一条竖线)
记住:[rotate时候Y反常,而skew的时候X反常!rotateY vs skewX]
(10)scale部分:
scaleZ就需要好好琢磨
至于旋转木马的效果原理分析:
虽然每一个图片都是绕着Y轴旋转了一定的角度,但是他们并没有和Y轴中心产生距离。因为我们要让图片移动是为了沿着Z轴移动,而且图片在旋转的时候Z轴也是移动了的,而且Z轴一直在图片的中心的垂直线上。因此移动的距离就是我们按照数学计算出来的距离!
(11)backface-visiblity

div
{
position:relative;
height:60px;
width:60px;
border:1px solid #000;
background-color:yellow;
transform:rotateY(180deg);
-webkit-transform:rotateY(180deg); /* Chrome and Safari */
-moz-transform:rotateY(180deg); /* Firefox */
}
#div1
{
-webkit-backface-visibility:visbile;
-moz-backface-visibility:hidden;
-ms-backface-visibility:hidden;
}
#div2
{
-webkit-backface-visibility:visible;
-moz-backface-visibility:visible;
-ms-backface-visibility:visible;
}

div背面是否可见。而且backface-visibilty作用于空间元素上的:

<div class="container">
<div class="cube backface-visibility-visible">
<div class="side front">1</div>
<div class="side back">2</div>
<div class="side right">3</div>
<div class="side left">4</div>
<div class="side top">5</div>
<div class="side bottom">6</div>
</div>
</div>
也就是作用于cubic元素的backface-visibility!

先让 box 在 X 轴上水平位移 100px 再绕着 Y 轴順时针转 60 度,和先绕 Y 轴順时针转 60 度,再在 X 轴上头水平位移 100px 的结果会完全不同,因为当我先绕了 Y 轴转动,整个 X 轴也会跟著转动,这时候再做水平位移,位置就会像是在深度做变换。

(12)mix-blend-mode和background-blend-mode

mix-blend-mode:该CSS属性作用是让元素内容和这个元素的背景以及下面的元素发生“混合”。而且mix-blend-mode默认情况下是会混合所有比起层叠顺序低的元素的,如果我们希望值混合某一两个元素,而不是全部,该怎么办呢?

可以试试使用CSS3 isolation:isolate

background-blend-mode:这个要更好理解一点,背景的混合模式。可以是背景图片之间的混合,也可以是背景图片和背景色的混合。需要注意的是,只能是background属性中的背景图片和颜色混合,而且只能在一个background属性中。

CSS3 backgrounds多背景IE9+浏览器就开始支持了。因此,你想混合多图,就是要逗号,一个一个写在background属性中就可以了,例如本Demo的两个妹子:

.box {
background: url(mm1.jpg) no-repeat center, url(mm2.jpg) no-repeat center;
}


(13)isolation:isolate阻止混合

.box {
background-color: #0074D9;
}
/*给inner这个元素添加一个isolation:isolate就可以阻止img和后面的box的背景色进行合并。isolation:isolate之所以可以阻断混合模式的进行,
本质上是因为isolation:isolate创建一个新的层叠上下文(stacking context)。没错,之所以起作用,就是单纯地因为创建了新的层叠上下文。本身并没有做什么特殊的事情。
或者我可以这么大胆的说:“isolation:isolate除了创建层叠上下文,其他没有任何鸟用!”而且只要能够创建层叠上下文的元素都是可以阻止mix-blend-mode的行为的。
1.z-index值不为auto的position:relative/position:absolute定位元素。
2.position:fixed,仅限Chrome浏览器,其他浏览器遵循上一条,需要z-index为数值。
3.z-index值不为auto的flex项(父元素display:flex|inline-flex).
4.元素的opacity值不是1.
5.元素的transform值不是none.
6.元素mix-blend-mode值不是normal.
7.元素的filter值不是none.
8.will-change指定的属性值为上面任意一个。
9.元素的-webkit-overflow-scrolling设为touch.(perspective也会产生一个层叠上下文)
*/
.inner {
width: 256px;
height: 256px;
background: url(http://image.zhangxinxu.com/image/study/s/s256/mm1.jpg) no-repeat;
border:1px solid red;
isolation:isolate;/*创建了层叠上下文*/
}
.mode {
position: relative;
right: -100px;
mix-blend-mode: darken;
border:5px solid red;
}
DOM结构如下:

<div class="box">
<div class="inner">
<img src="http://image.zhangxinxu.com/image/study/s/s256/mm2.jpg" class="mode">
</div>
</div>


(14)层叠上下文如何影响元素在Z轴的顺序

display:flex与层叠上下文

.box {}
.box > div { background-color: blue; z-index: 1; }
/* 此时该div是普通元素,z-index无效。 */
.box > div > img {
position: relative; z-index: -1; right: -150px;
/* 注意这里是负值z-index */
}
我们看看DOM结构和分析

<!--
为什么img元素会被box元素的背景色覆盖?
(1)必须弄清楚,我们的层叠顺序对于这一类的父子孙关系的DOM结构(原生的层叠顺序)也是存在的,而不仅仅针对那些通过position定位而覆盖在一起的元素(非原生的层叠顺序)
(2)层叠顺序告诉我们,负值z-index的层叠顺序在Block元素下面,此处的第二层div元素是一个Block元素
(3)如果给box一个background-color那么我们还是通过层叠顺序来分析,因为第二个div元素默认占据了第一个div元素的宽度,而且不是层叠上下文元素因此后来居上原则box元素被覆盖
(4)普通元素的z-index失效,但是普通元素和display:flex/inline-flex结合起来那么我们的z-index又是有效果的。也就是说flex/inline-flex元素要形成层叠上下文是有条件的:条件1是父级需要是display:flex或者display:inline-flex水平,条件2是子元素的z-index不是auto,必须是数值。这时候子元素就会形成堆叠上下文!所以,我们把box添加一个display:inline-flex那么box >div就会形成堆叠上下文了!
-->
<div class="box">
<div>
<img src="./flex.jpg">
</div>
</div>
mix-blend-mode于层叠上下文

.mode {
position: absolute;
mix-blend-mode: darken;
/*绝对定位的元素使用了 mix-blend-mode: darken*/
}
/*box元素会钻到图片下面去*/
.box {
background: blue;
isolation: isolate;
/*isolation:isolate这个声明是mix-blend-mode应运而生的。默认情况下,mix-blend-mode会混合z轴所有层叠在下面的元素,要是我们不希望某个层叠的元素参与混合怎么办呢?
就是使用isolation:isolate。本义是用来隔离mix-blend-mode元素的混合*/
}
.box > img {
position: relative;
z-index: -1;
}
DOM结构部分

<img src="flex.jpg" class="mode">
<div class="box">
<!--
(1)因为这里是img,而且z-index:-1,所以会导致box的蓝色的背景色会覆盖img图片,从而我们看不到图片。但是因为mode元素使用了mix-blend-mode: darken,
所以会和后面的蓝色背景进行混合掉!
(2)如果我们给box添加一个isolation: isolate那么box就会产生一个层叠上下文,所以就会使得其在Z轴上具有较大的优先级,比绝对定位的元素优先级更高[后来居上]。
而且因为box这时候是堆叠上下文,所以其背景的颜色就会比z-index为负数的元素层级更低!
-->
<img src="./width-ani.png">
</div>
大家知道为什么定位元素会层叠在普通元素的上面吗?

其根本原因就在于,元素一旦成为定位元素,其z-index就会自动生效,此时其z-index就是默认的auto,也就是0级别,根据上面的层叠顺序表,就会覆盖inline或block或float元素。而不支持z-index的层叠上下文元素天然z-index:auto级别也就意味着,层叠上下文元素和定位元素是一个层叠顺序的,于是当他们发生层叠的时候,遵循的是“后来居上”准则深入理解CSS中的层叠上下文和层叠顺序(不依赖于Z-index的层叠上下文是auto也就是0级别,而该级别会低于我们的z-index为正数的级别)。

1.位于最低水平的border/background指的是层叠上下文元素的边框和背景色。每一个层叠顺序规则适用于一个完整的层叠上下文元素

2.原图没有呈现inline-block的层叠顺序,实际上,inline-block和inline水平元素是同等level级别。

3.z-index:0实际上和z-index:auto单纯从层叠水平上看,是可以看成是一样的。注意这里的措辞——“单纯从层叠水平上看”,实际上,两者在层叠上下文领域有着根本性的差异。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: