모바일 페이지 개발을 위한 렘 레이아웃 적응의 원리
적응이란 무엇이며 왜 적응해야 하는가
우리가 받는 디자인 도면은 일반적으로 640, 750, 1080 해상도입니다. 디자인을 기반으로 하여 이제는 해상도, 논리 픽셀, 뷰포트가 다른 다양한 모바일 단말기가 있으므로 모든 장치에서 페이지가 잘 표시되도록 하려면 이러한 장치에 맞게 통합 처리해야 합니다. 이 프로세스를 모바일 단말기 적응이라고 합니다.
알아야 할 몇 가지 개념:
물리적 픽셀(물리적 픽셀)
물리적 픽셀은 디스플레이(휴대폰 화면)에서 가장 작은 물리적 디스플레이 단위로, 우리의 일반적인 해결 방법으로 이해될 수 있습니다.
디바이스 독립적 픽셀(밀도 독립적 픽셀)
디바이스 독립적 픽셀(밀도 독립적 픽셀이라고도 함)은 컴퓨터 좌표계의 한 점으로 생각할 수 있습니다. 프로그램에 의해 제어될 수 있는 지점입니다. 사용된 가상 픽셀(예: CSS 픽셀)은 관련 시스템에 의해 실제 픽셀로 변환됩니다. 이는 우리가 말하는 시각적 뷰포트의 크기로 이해될 수 있습니다. >
그래서 물리적 픽셀과 장치 독립적 픽셀의 차이에는 일정한 대응 관계가 있는데, 이것이 바로 다음에 이야기할 장치 픽셀 비율입니다. 장치 픽셀 비율(장치 픽셀 비율)장치 픽셀 비율(줄여서 dpr)은 물리적 픽셀과 장치 독립 픽셀 간의 대응 관계를 정의합니다. 해당 값은 다음 공식에 따라 얻을 수 있습니다. 장치 픽셀 비율 = 물리적 픽셀 / 장치 독립 픽셀 // 특정 방향, x 방향 또는 y 방향 장치 픽셀 비율은 장치 제작 시에도 설정되며, 자바스크립트에서는 윈도우를 통해 얻을 수 있습니다. .devicePixelRatio를 현재 장치의 dpr로 설정합니다. 뷰포트 PC 뷰포트는 도구 모음과 스크롤 막대를 제외한 브라우저 창 내의 콘텐츠 영역을 나타냅니다. 모바일 브라우저의 뷰포트 여러 상황에서: ef3dc936bac0e7a2f208bd84813d0c0f 콘텐츠별로 설정되는 뷰포트, 최대값을 지정하는 레이아웃 뷰포트입니다. 너비는 document.documentElement.clientWidth를 통해 얻을 수 있습니다. 우리가 보는 브라우저 창과 웹 페이지 영역의 크기는 CSS 픽셀(장치 논리 픽셀)로 표시되는 시각적 뷰포트라고 합니다. remrem은 문서 및 요소 html을 기준으로 하는 css3의 길이 단위입니다. 예를 들어 html 글꼴 크기=100px를 설정한 다음 1rem=100px를 사용할 수 있습니다. 이 기본 값을 사용하여 크기를 설정합니다. 일반적으로 사용되는 솔루션: 고정 높이, 적응형 너비(백분율, em) rem 레이아웃 사용 다음은 Taobao 홈페이지에서 rem을 사용하는 NetEase의 솔루션 NetEase의 접근 방식을 요약한 것입니다. 1) 크기 조정 없이 시각적 뷰포트, 즉 이상적인 뷰포트에 맞게 레이아웃을 설정합니다.<meta name="viewport"content="initial-scale=1,maximum-scale=1, minimum-scale=1”>2) 디자인 도안의 해상도를 기준으로 폰트 크기를 100px 기준으로 했을 때 디자인 시안의 너비가 640이라면 본문 요소의 너비를 설정할 수 있습니다. 너비: 6.4rem(640/100), 레이아웃 뷰포트를 320으로 설정하면 글꼴 크기는 html=deviceWidth / 6.4입니다. 3) document.documentElement.clientWidth를 통해 deviceWidth를 가져옵니다. 4) 페이지 돔이 준비되면 html 글꼴 크기를 설정합니다.
document.documentElement.style.fontSize =document.documentElement.clientWidth / 6.4 + ‘px’5) mediaQuery를 통해 글꼴 크기를 설정합니다. 오류가 너무 크기 때문에 글꼴 크기로 rem을 사용할 수 없습니다. 640 디자인 초안을 예로 들면 최종 설정 html 글꼴 크기 코드는 다음과 같습니다. 레이아웃 시 디자인 초안에 표시된 크기를 100으로 나눈 값입니다. rem.
var deviceWidth = document.documentElement.clientWidth; if(deviceWidth > 640) deviceWidth = 640; document.documentElement.style.fontSize = deviceWidth / 6.4 + 'px';여기서 if(deviceWidth > 640) deviceWidth = 640;은 deviceWidth가 640보다 크면 물리적 해상도가 이미 1280보다 크기 때문입니다(dpr에 따라 다름). PC 웹사이트를 방문하세요.
淘宝的做法:
原理
1) 通过dpr设置缩放比,实现布局视口大小,
var scale = 1 / devicePixelRatio; document.querySelector('meta[name="viewport"]').setAttribute('content','initial-scale='+ scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
2) 动态计算html的font-size
document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + ‘px’;
这里的意思是,clientWidth / 10 得到是布局视口下的rem基准值(以iphone6为例 1rem=75px),那么设计稿正好也是 750,所以对应的关系 clientWidth / 10==设计稿的尺寸/x, 那么x=设计稿的尺寸/rem基准值。如果是iphone6 plus rem基准值等于clientWidth / 10 等于124.2,那么x=750/124.2。
关于具体的实现 淘宝提供了一个开源的方案lib-flexible:https://github.com/amfe/lib-flexible
具体逻辑 :
1)判断head中是否设置了viewport,如果有设置,按照已有viewport 设置缩放比;
if (metaEl) { console.warn('将根据已有的meta标签来设置缩放比例'); var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/); if (match) { scale = parseFloat(match[1]); dpr = parseInt(1 / scale); } }
2)如果没有设置meta viewport,判断是否设置dpr,如果有,通过dpr计算缩放scale。
var content = flexibleEl.getAttribute('content'); if (content) { var initialDpr = content.match(/initial\-dpr=([\d\.]+)/); var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);//maximum 设置最大值,与initial的值比较,取最小值; if (initialDpr) { dpr = parseFloat(initialDpr[1]); scale = parseFloat((1 / dpr).toFixed(2)); } if (maximumDpr) { dpr = parseFloat(maximumDpr[1]); scale = parseFloat((1 / dpr).toFixed(2)); } }
3)如果 dpr &scale都没有设置,那么就通过设备的dpr设置起缩放 scale,
if (!dpr && !scale) {//meta[name="viewport"]&&meta[name="flexible"]都不存在。 var isAndroid = win.navigator.appVersion.match(/android/gi); var isIPhone = win.navigator.appVersion.match(/iphone/gi); var devicePixelRatio = win.devicePixelRatio; if (isIPhone) { // iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案 if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) { dpr = 3; } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){ dpr = 2; } else { dpr = 1; } } else { // 其他设备下,仍旧使用1倍的方案 dpr = 1; } scale = 1 / dpr; }
4)得到scale之后 ,如果meta 的viewport不存在,那么就创建一meta[name=“viewport”],将scale配置进去。
metaEl = doc.createElement('meta'); metaEl.setAttribute('name', 'viewport'); metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no'); if (docEl.firstElementChild) { docEl.firstElementChild.appendChild(metaEl); }
5)动态改写html的font-size
var width = docEl.getBoundingClientRect().width;//获取html的宽度 if (width / dpr > 540) {//判断屏幕逻辑像素大于540时,取540 width = 540 * dpr; } var rem = width / 10; docEl.style.fontSize = rem + 'px'; flexible.rem = win.rem = rem;
总结:
使用rem布局,实质都是通过动态改写html的font-size基准值,来实现不同设备下的良好统一适配;
网易与淘宝不同 的地方是 ,网易将布局视口设置成了 视觉视口,淘宝将布局视口设置成了物理像素大小,通过 scale缩放嵌入了 视觉视口中;
容器元素的字体大小都不使用rem,需要额外的media查询;