1. 程式人生 > >H5-移動端響應式佈局vw,vh替代方案(可靈活搭配)

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的方式。