H5-移動端響應式佈局vw,vh替代方案(可靈活搭配)
前言
經典方式
先簡要的介紹下現在多數開發者使用的方式,由於這些方式大家或多或少都使用過,而且網上也有非常詳細的教程,我就不再重複造輪子,只是簡單的列舉下。
1.css3 @media
http://www.w3cways.com/1180.html
這種方式適配起來良好,但是還是無法相容所有的螢幕,而且各個樣式都用@media寫幾套,很麻煩,也不利於後期擴充套件維護。
2.px+百分百+flex
像目前接觸過的一些前端框架ionic、bootstrap、amaze、mui等,對於已經確定無需隨螢幕變化的元素,一般用px固定大小,對於寬度的處理用百分比或flex來靈活處理。
這種方式處理起來,大體還好,但是對於小屏iphone5s和大屏iphone6 plus,某些佈局只能折中處理。最大的缺陷是, 一些佈局無法通過百分比或者flex設定的時候,佈局的樣式和字型的大小都無法隨螢幕的變化而響應式變化。
3.對於viewport的處理
- 固定高度,寬度自適應
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
- 固定寬度,動態生成viewport
var fixScreen = function () {
var metaEl = doc.querySelector('meta[name="viewport"]'),
metaCtt = metaEl ? metaEl.content : '',
matchScale = metaCtt.match(/initial\-scale=([\d\.]+)/),
matchWidth = metaCtt.match(/width=([^,\s]+)/);
if ( metaEl && !matchScale && ( matchWidth && matchWidth[1 ] != 'device-width') ) {
var width = parseInt(matchWidth[1]),
iw = win.innerWidth || width,
ow = win.outerWidth || iw,
sw = win.screen.width || iw,
saw = win.screen.availWidth || iw,
ih = win.innerHeight || width,
oh = win.outerHeight || ih,
ish = win.screen.height || ih,
sah = win.screen.availHeight || ih,
w = Math.min(iw,ow,sw,saw,ih,oh,ish,sah),
scale = w / width;
if ( ratio < 1) {
metaEl.content += ',initial-scale=' + ratio + ',maximum-scale=' + ratio + ', minimum-scale=' + scale;
}
}
}
4.根節點固定fontSize,佈局單位用rem
動態生成根節點的fontSize:
<script type="text/javascript">
/*
* 如果頁面的寬度超過了640px,那麼頁面中 html
* 的font-size恆為100px,否則,頁面中html的
* font-size的大小為: 100 * (當前頁面寬度 / 640)
*字型大小或者其他div寬高度 * html的font=該元素的實際大小px (htmlFont*(fontRem || divRem)=realPx)
*/
(function(doc,win){
var docEle=doc.documentElement;
var resizeEvt = 'orientationchange' in win ? 'orientationchange' : 'resize';
var recalc = function(){
var clientWidth = docEle.clientWidth;
if(!clientWidth)
return;
if(clientWidth>=640){
docEle.style.fontSize='100px';
}else{
docEle.style.fontSize=100*(clientWidth/640) + 'px';
}
};
if(!doc.addEventListener)
return;
win.addEventListener(resizeEvt,recalc,false);
doc.addEventListener('DOMContentLoaded',recalc,false);
})(document,window);
</script>
頁面上字型和佈局單位都採用rem,該方式基本可以適應大部分的佈局,使用過程中發現一個缺陷,由於,fontSize是根據螢幕的寬度自動生成的,那麼在橫屏的時候,由於設定的元素的高度rem是基於寬度計算的fontSize來的,fontSize會變大,就會造成元素的高度被拉的很高。我們希望是這樣的:佈局元素的寬度始終基於螢幕寬度百分比變化,佈局元素的高度始終基於螢幕高度百分比變化。有沒有方法實現呢?答案是有,看下面的一種方式。
5.css3百分比單位vw,vh
1vw等於可視區寬度(螢幕的可視區域即佈局區域)的百分之一,1vh等於可視區高度的百分之一
<div style="width:20vw;height:40vh;"></div>
表示當前元素在任何螢幕下都是div佔當前螢幕寬度的20%,當前高度的40%。剛好滿足我們的需求,但是你會發現很少有有開發者採用這個單位。很不幸,這個單位有部分瀏覽器和手機並不支援。我們有沒有替代的解決方案呢?就是下面今天打算重點講的一個方案。
angularjs指令計算百分方式
由於vw,vh不被完全支援的原因,有了這個替代的解決方案,目前專案中正在使用,效果很不錯。
先上程式碼:
angular.module('starter.directives', [])
.directive('adaptive',function(){
function link($scope, element, attrs) {
var dom=element[0];
var wPercent = attrs.w;
var hPercent = attrs.h;
var pPercent = attrs.p ? attrs.p.split(",") : null;
var mPercent = attrs.m ? attrs.m.split(",") : null;
var screenW = Math.min(document.documentElement.clientWidth,window.innerWidth);
var screenH = Math.min(document.documentElement.clientHeight,window.innerHeight);
setWAndH();
function setWAndH(){
if(wPercent){ //寬度
dom.style.width=whatUnitValue(wPercent)+"px";
}
if(hPercent){ //高度
dom.style.height=whatUnitValue(hPercent)+"px";
}
if(pPercent){//padding
dom.style.paddingTop=whatUnitValue(pPercent[0])+"px";
dom.style.paddingRight=whatUnitValue(pPercent[1])+"px";
dom.style.paddingBottom=whatUnitValue(pPercent[2])+"px";
dom.style.paddingLeft=whatUnitValue(pPercent[3])+"px";
}
if(mPercent){//margin
dom.style.marginTop=whatUnitValue(mPercent[0])+"px";
dom.style.marginRight=whatUnitValue(mPercent[1])+"px";
dom.style.marginBottom=whatUnitValue(mPercent[2])+"px";
dom.style.marginLeft=whatUnitValue(mPercent[3])+"px";
}
}
function whatUnitValue(value){
if(value.indexOf("px")!=-1){//原始單位
return parseFloat(value);
}
if(value.indexOf("vw")!=-1){//佔比螢幕寬度
return parseFloat(value)/100*screenW;
}
if(value.indexOf("vh")!=-1){//佔比螢幕高度
return parseFloat(value)/100*screenH;
}
return parseFloat(value)/100*screenW;//預設佔比螢幕寬度
}
window.onresize=function(){
screenW = Math.min(document.documentElement.clientWidth,window.innerWidth);
screenH = Math.min(document.documentElement.clientHeight,window.innerHeight);
setWAndH();
};
}
return {
restrict: 'A',
link: link
};
})
使用:
<div style="border:solid 1px red;float:left;" adaptive data-w="20vw" data-h="10vh"></div>
<div style="border:solid 1px red;float:left;" adaptive data-w="20vw" data-h="100px"></div>
<div style="border:solid 1px red;float:left;" adaptive data-w="20vw" data-h="100px" data-m="10vh,10vw,10px,10vw"></div>
<div style="border:solid 1px red;float:left;" adaptive data-w="20vw" data-h="100px" data-p="10vh,10vw,10px,10vw"></div>
第一個div,寬度始終佔螢幕寬度的20%,高度佔螢幕高度的10%;第三個div的margin上、右、下、左分別佔螢幕高度10%,寬度10%,10個畫素,螢幕寬度10%。可以看到,完全可以和px靈活搭配。vw就表示是佔螢幕的寬度,vh表示是佔螢幕的高度。
程式碼很簡單,就不做過多解釋。用html5的data屬性新增我們自己的資料,通過指令封裝。在指令裡面拿到對應的屬性的值,乘螢幕的寬高度,算出實際值,再設定到元素上面。實際上就是一個變相的vw、vh的實現。然後有一個監聽螢幕大小變化的函式,比如切屏時候,元素的位置和大小也會隨著變化。這種方式非常靈活,不影響你使用其他的方案,你可以單獨對某一個、某幾個有特殊需求的元素進行處理都可以。
有什麼好的建議或其他方案歡迎留言討論。說一點自己專案中使用的建議:確定要用固定px大小的,儘量用定值px,比如標題欄高度等;寬度佈局時候儘量使用百分比或flex或者用指令實現的這種方式;高度需要自適應時候用指令這種方式非常方便;在處理字型大小的時候,如果產品沒有特殊要求,可不對字做響應式處理,有特殊要求可以採用上面的rem的方式。