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

像图片一样布局 #9

Open
xiangpaopao opened this issue Jan 4, 2015 · 4 comments
Open

像图片一样布局 #9

xiangpaopao opened this issue Jan 4, 2015 · 4 comments

Comments

@xiangpaopao
Copy link
Owner

移动端经常会有这样的展示页面,页面中有一些独立的元素,并且伴随各种交互效果、转场动画。

img img

针对页面布局的话,大概有这样的需求:

  • 不滚动页面,所有元素均在一屏之类展示
  • 像图片那样缩放元素---宽度改变时高度按比例变化
  • 背景图需要把浏览器窗口撑满
  • 往往项目是独立的,需要快速上线,开发周期短

这种形式页面在正常的文档流中布局显然不太合适,比较传统的做法是这样:

  • 布局以绝对定位为主
  • 用百分比配合px控制宽高、位置
  • 背景图用background-size:cover撑满
  • 用css媒体查询来适应多种屏幕
  • css搞不定的请js帮忙

布局一个单屏展示页

比如这样一个640*960的设计稿,分背景和按钮两层,要求背景全屏(可以不显示全)按钮定位在精确的位置上。

img

传统做法

一般会把背景图用background-size:cover显示在一个宽高撑满屏幕的容器上以适应各种屏幕, 这个按钮怎么定位呢,一般是会这样做吧position:absolute;bottom:18%;

模拟一下iPhone5微信中的显示
img

iPhone4微信中的显示
img

可以看到iPhone4中按钮错位,原因是当屏幕尺寸改变时,背景图与按钮的尺寸与位置会遵循不同的规则变化。这个例子中的效果貌似还能接受,可有些场景对定位的要求可能比这更高。

解决这个问题一般会用媒体查询针对屏幕做适配吧

@media screen and (max-height: 500px) {
  .btn{
    bottom:12%;
  }
}

Demo 在这里 http://jsbin.com/zugape/edit

如果页面中元素多、定位要求精确,这种做法是比较暴力的。下面是支付宝十年晒单页面中针对不同尺寸屏幕做的适配

/* <=640 */
@media screen and (min-width: 319px) and (max-width: 321px) {
    ...
}
/* 720 */
@media screen and (min-width: 359px) and (max-width: 361px) {
    ...
}
/* 1024*768, 2056*1536 */
@media screen and (min-width: 383px) and (max-width: 385px) {
    ...
}
/* 800 */
@media screen and (min-width: 399px) and (max-width: 401px) {
    ...
}
/* 1080 */
@media screen and (min-width: 539px) and (max-width: 541px) {
    ...
}
/* 1200 */
@media screen and (min-width: 599px) and (max-width: 601px) {
    ...
}
/* 1440 */
@media screen and (min-width: 719px) and (max-width: 721px) {
    ...
}
/* 1600 */
@media screen and (min-width: 799px) and (max-width: 801px) {
    ...
}
/* for small screen */
@media screen and (max-height: 500px) {
    ...
}
/* for iPhone6 Plus */
@media only screen and (device-width: 414px) and (-webkit-min-device-pixel-ratio: 3),
only screen and (device-width: 375px) and (-webkit-min-device-pixel-ratio: 3){
    ...
}

总结下这种方式的缺点:

  • 要把设计稿中的PX在大脑中换算成%
  • 百分比控制宽高是相对于父元素,计算繁琐
  • 用媒体查询针对各种尺寸的屏幕做兼容工作繁琐

图片的自适应规则

一张图片如果给他设置<img src="pic" style="max-width: 100%;max-height:100%” />的话,无论视口大小,他都会显示在视口里面,如图
img img

类似的还有图片作为背景显示时的background-size属性。
background-size: contain:缩放图像的最大值,其宽度和高度都能放入内容区域
img

background-size: cover:缩放图像的最小值,其宽度和高度都能放入内容区域
img

当你改变图片的宽度或高度时,图片的另一边会自动按比例缩放,如果div有这种能力多好啊!

vw、vh--为移动而生

vw、vh是CSS3中出现的新的长度单位,vw 相对于视窗的宽度,视窗宽度是100vw;vh 相对于视窗的高度,视窗高度是100vh。
其中视窗指的是浏览器实际显示区域,即window.innerWidth/window.innerHeight的大小。
如 想一个元素的宽是视口宽的一半,只需设置width:50vw

与百分比的异同

乍一看好像和百分比类似,并没发现多少优势,在进行一些尝试之后我总结了一下他们的异同

百分比 vw、vh
相对父级元素 相对视口
横向/纵向相对父级横向/纵向 vw/vh相对视口宽/高(即写vh就是相对视口高、写vw就是相对视口宽,不必横向与横向纵向与纵向对应)
对字体大小无效 对字体大小有效

Tips:其实margin-top/bottom以百分比为单位时他的参照对象并不是父元素的height而是width。详情阅读 doyoe margin系列文章

这些特性可以实现一些之前用CSS难以解决的问题。

相对视口100%高度

由于百分比高度是相对父级的,之前如果想让文档流中的某容器相对视口高度100%,只用css的话得从html>body>element一层层下来都设置height:100%,而现在只需给元素设置height:100vh
height 100% Demo http://jsbin.com/risegu/edit

响应式文字 && 像图片一样布局

先上Demo
百分比方式 http://jsbin.com/vemudo/edit
vw方式 http://jsbin.com/huwido/edit
这两个Demo建议把浏览器模拟成移动设备的样子,拉伸浏览器窗口宽度观察效果。

img img

可以看到当浏览器宽度改变时,百分比Demo中只有元素的宽度发生了变化,而统一使用vw做单位的Demo中文字的大小、元素的高度与宽度均按缩放比例发生了变化,这正是我们想要的“像图片一样布局”的效果。

vmin 与 vmax

还有vw、vh延生出来的单位vmin、vmax。
vmin:关于视口高度和宽度两者的最小值
vmax:关于视口高度和宽度两者的最大值

我觉得这是一组神奇的单位,但是暂时对这个使用场景的理解有限,能想到的场景是一个正方形,height:100vmin;width:100vmin时,得到的是这个视口中能显示满的一个最大正方形(http://jsbin.com/baheko);height:100vmax;width:100vmax时得到的是能把这个视口全部显示满的最小正方形(http://jsbin.com/tamasi)。

然后在codepen上找到这样一个demo,他让16:9的视频窗口在视口中自适应 http://codepen.io/CreativeJuiz/pen/KzkgL

其他适用场景有待发掘

vw、vh的问题

vw vh 在桌面端浏览器兼容性已经是比较好了,不过在移动端貌似还需要等待,国产安卓浏览器的话UC到现在都不支持真不让人省心。
img

另外对张鑫旭的视区相关单位vw, vh..简介以及可实际应用场景 中他的结论:vw vh只适用于非定位元素;vh高度值的内部元素不支持百分比%高度 有些疑问,我在测试中并未发现这样的情况,这篇文章写于2012年,不知道是不是这样的情况只发生在当时的浏览器。Demo http://jsbin.com/dineka/edit

rem方案

回头来看我们的布局,vw vh的方案因为兼容性问题只能放弃,重新分析下我们的需求:
其实我们需要那么一个全局的相对单位...
我们可以通过修改这个单位来控制整个布局...
我们可以通过判断屏幕的比例来缩放整个页面以展示全部内容...

然后想到类似手机淘宝的动态rem的方案:所有布局元素单位使用rem,由于rem是相对于根元素的,所以不同屏幕按一定规则控制的font-size 即可兼容各屏幕。

var root = document.getElementsByTagName('html')[0],
    NATIVE_W = 640;
function updateSize() {
    var w = window.innerWidth;
    var cw = w / (NATIVE_W / 100);  
    root.style.fontSize = cw + 'px';
}
window.onload = updateSize;
window.onresize = updateSize;

这样的页面一般会有设计稿的,我们需要设置rem和设计稿中px的转换比例。
1rem = 1px 这样固然方便,但是有些浏览器有最小字体的限制。
所以我想让 1rem = 100px。
我们的设计稿一般为640 x 960,这个尺寸是针对iPhone4,设备像素比是2,那么我们实际需要html的font-size为50px。
这样的话假如设计稿中一个元素尺寸为240px x 200px,
那么这个元素需要在屏宽为320px的设备上显示的尺寸是120px x 100px,而我们需要他在布局时设置的宽高是2.4rem x 2rem。
设计稿宽640px 也就是整个页面应为 6.4rem。 所以对应不同宽度的设备时用window.innerWidth(视口宽) / 6.4 得到的就是对应此设备的font-size。
布局时,把设计稿上的尺寸除以100然后把px单位换成rem。

怎么解决比较短的屏幕显示不完全的情况呢,可以先判断一下视口的比例,当视口高宽比低于设计稿宽高比时,缩小html节点的font-size这样就能把内容显示全了

var root = document.getElementsByTagName('html')[0],
    NATIVE_W = 640,
    NATIVE_H = 960;

function htmlSize() {
    var cw = 50,
        w = window.innerWidth,
        h = window.innerHeight;  
    if ((w / h) > (NATIVE_W / NATIVE_H)) {
        cw = h / (NATIVE_H / 100);
    } else {
        cw = w / (NATIVE_W / 100);
    }
    root.style.fontSize = cw + 'px';
}
window.onload = htmlSize;
window.onresize = htmlSize;

Demo http://jsbin.com/rupole/edit
可以试下缩小、拉大窗口布局也会像图片那样相应变化,并且无论窗口是什么样的尺寸,元素都会出现在比较合理的位置,当然,一些特殊情况仍然要用媒体查询来适配。

然后在布局时,其实只要把这段js放在页面中,就rem当成px来用就行了,不需要多考虑啥,这个方案的优点就是构建页面成本低,跨屏显示效果也能满意。

calc

另外有些元素我们想他垂直方向上针对中心定位而不是顶部或底部,比如想让一个小球居中之前大概是这样:

#cir{
  width:200px;
  height:200px;
  border-radius:100px;
  background:#888;
  position:absolute;
  left:50%;
  top:50%;
  margin-left:-100px;
  margin-top:-100px;
}

其实CSS3中有这样一个表达式:calc()(CSS里居然有这玩意,我第一次看到时怀疑是不是SASS或LESS里的),他可以给元素的border、margin、pading、font-size和width等属性动态计算值,例如:top:calc(50% - 1rem),这在这种展示页面的布局中是相当有用的,上面的css可以改写成:

#cir{
  width:200px;
  height:200px;
  border-radius:100px;
  background:#888;
  position:absolute;
  left:calc(50% - 100px);
  top:calc(50% - 100px);
}

Demo http://jsbin.com/gasato/edit

关于更多calc的介绍建议阅读大漠的这篇博文 http://www.w3cplus.com/css3/how-to-use-css3-calc-function.html

遗憾的是这玩意在桌面端浏览器中支持的已经比较好了,不过在移动端的表现令人堪忧,安卓原生浏览器从4.4开始支持,而最新版的安卓UC仍然不支持。

img

@honestcheng
Copy link

“以UC为代表的国产浏览器貌似就打算支持。” 是打算支持还是不打算支持,根据上下文理解貌似是不打算支持的意思

@xiangpaopao
Copy link
Owner Author

@honestcheng 已修正 真细心 我要是姑娘跟定你了

@edwinna
Copy link

edwinna commented Jan 8, 2015

下次分享就你了

@hahamy
Copy link

hahamy commented Apr 30, 2015

做了一些微信场景页,活动页,苦恼于这问题,找不到完美的方法。看了你这篇文章,觉得比较靠谱的就是rem,但rem的方案,在高宽比低的时候(现在设计稿一般为16:9),比如4s里,页内元素宽度会变小很多

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

4 participants