最近比较关注疫情,平时也比较多看腾讯新闻。我发现腾讯疫情方面的新闻是单独的一个网页做的,而且还能很好的适配移动端和PC端,做到了两者很好的兼容,于是我花了些时间来弄明白到底怎样做能达到同样的效果,经过我的一番研究,总结腾讯在移动端适配的方案就是:
vw单位和transform:scale()缩放结合的产物
那看我慢慢道来,不过在这之前,先要弄清楚几个问题:
1.vw单位是什么?
相对于视口的宽度,我们把整个视口宽均分成了100个单位的vw,也就是说1vw,相当于1%可视窗口宽。
vw单位在安卓浏览器4.4+和chrome26.0+都开始被支持。要是放到了几年前,假如我们要使用vw单位来做移动端的适配,很可能会选择放弃。但是现在安卓基本上很少能见到4.4以下的系统了,所以vw单位是目前非常好的移动端web应该首要选择的单位。
2.transform:scale()
transform是CSS3新增的属性,它的值scale()定义了2D缩放的转换。scale接收两个参数,分别是x轴y轴,值为数字。例如:transform: scale(2, 3)的意思就是将元素的宽变成原来的2倍,高变成原来的3倍。
除此之外,我们还需要明确一点的就是:缩放的基准点是哪里?默认情况下,缩放会以50%宽和50%的高为基准点,说白了就是元素的几何中心点为基准进行缩放,而不是左上角的顶点(坐标原点)。所以设置基准点的也会有相应的属性:transform-origin。比如:transform-origin: 20% 40% 的意思就是转换的基准点在x轴的20%和y轴的40%交点处;又如:transform-origin:center top 相当于transform: 50% 0
3.腾讯方案为什么要将这两者结合起来使用?
你可能会想,我只使用vw单位就能适配移动端了,这其实也没错,vw单位在移动端确实能很好适配各种分辨率的屏幕。但是,假如ui给出的设计稿宽是750px,那么在移动端确实也没有什么问题,但放到了PC端去访问这个页面,整个页面会被拉的很宽,那么就不能保证跟设计稿呈现出统一的样式了,因为我们需要的最终结果就是页面元素的宽高比例要保持跟设计稿是一样的,页面可以整体进行缩放。
所以,腾讯的方案就是:首先设置了根元素的宽为100vw,当视口宽超过750px并不断增大的时候,我们使用transform: scale()不断去缩小整个根元素,让根元素始终维持在750px的宽度。当然,当视口小于750px并不断减小的时候,我们则移除缩放属性,让整个页面随着窗口整体缩小即可。
那么,这个scale的值怎么计算得到?假如UI给我们的设计稿是750px,那么 750/当前窗口宽 就得到了scale的值。
4.应该注意的地方
4.1 css中应该设置html, body 高度为100%,以免页面元素竖向多出margin或者padding设置的值;
4.2 所有涉及到宽高属性的值都应该使用vw;
4.3 为什么要设置transform-origin: center top?因为,我们希望缩放的基准点,特别是Y轴应该是基于y轴的顶点,这样就不会导致根元素在Y轴上也跟着缩放从而导致页面混乱
5.具体的代码
<!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>flexible</title>
<style>
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
}
body {
overflow-x: hidden;
overflow-y: scroll;
}
#app {
background-color: #ccc;
border: 1px solid #666;
min-height: 100%;
width: 100vw;
box-sizing: border-box;
}
#app > p {
font-size: 4.26vw;
padding: 5vw 1vw;
}
</style>
</head>
<body>
<div id="app">
<p>用来测试内容</p>
<p>用来测试内容</p>
<p>用来测试内容</p>
<p>用来测试内容</p>
<p>用来测试内容</p>
<p>用来测试内容</p>
<p>用来测试内容</p>
<p>用来测试内容</p>
<p>用来测试内容</p>
<p>用来测试内容</p>
<p>用来测试内容</p>
<p>用来测试内容</p>
<p>用来测试内容</p>
<p>用来测试内容</p>
<p>用来测试内容</p>
<p>用来测试内容</p>
</div>
<script>
function Flexible(obj) {
var doc = document
var win = window
var rn = obj.root ? doc.querySelector(obj.root) : doc.body.children[0]
var sz = obj.size ? (obj.size.toString().indexOf('px') !== -1 ? obj.size.slice(0, -2) : obj.size) : 750
this.sw = 0
this.scale = function () {
this.sw = doc.documentElement.clientWidth || doc.body.offsetWidth
if (this.sw > sz) {
rn.style.transform = 'scale(' + sz / this.sw + ')'
rn.style.transformOrigin = 'center top'
} else {
rn.style = ''
}
}
this.init = function () {
this.scale()
var that = this
if (win.addEventListener) {
win.addEventListener('resize', function () {
that.scale()
})
} else {
win.attachEvent('onresize', function () {
that.scale()
})
}
}
this.init()
}
// 此处进行调用,传递一个对象,其中root表示绑定的根元素,size为设计稿宽度
new Flexible({
root: '#app',
size: 750
})
</script>
</body>
</html>
6.线上演示:码云线上演示地址