在移動設備上進行網(wǎng)頁的重構(gòu)或開發(fā),首先得搞明白的就是移動設備上的viewport了狐赡,只有明白了viewport的概念以及弄清楚了跟viewport有關(guān)的meta標簽的使用骗爆,才能更好地讓我們的網(wǎng)頁適配或響應各種不同分辨率的移動設備谴麦。
一踏揣、viewport的概念
通俗的講靶溜,移動設備上的viewport就是設備的屏幕上能用來顯示我們的網(wǎng)頁的那一塊區(qū)域眯杏,在具體一點夜焦,就是瀏覽器上(也可能是一個app中的webview)用來顯示網(wǎng)頁的那部分區(qū)域,但viewport又不局限于瀏覽器可視區(qū)域的大小岂贩,它可能比瀏覽器的可視區(qū)域要大茫经,也可能比瀏覽器的可視區(qū)域要小巷波。在默認情況下,一般來講卸伞,移動設備上的viewport都是要大于瀏覽器可視區(qū)域的抹镊,這是因為考慮到移動設備的分辨率相對于桌面電腦來說都比較小,所以為了能在移動設備上正常顯示那些傳統(tǒng)的為桌面瀏覽器設計的網(wǎng)站荤傲,移動設備上的瀏覽器都會把自己默認的viewport設為980px或1024px(也可能是其它值垮耳,這個是由設備自己決定的),但帶來的后果就是瀏覽器會出現(xiàn)橫向滾動條遂黍,因為瀏覽器可視區(qū)域的寬度是比這個默認的viewport的寬度要小的终佛。下圖列出了一些設備上瀏覽器的默認viewport的寬度。
二雾家、css中的1px并不等于設備的1px
在css中我們一般使用px作為單位铃彰,在桌面瀏覽器中css的1個像素往往都是對應著電腦屏幕的1個物理像素,這可能會造成我們的一個錯覺芯咧,那就是css中的像素就是設備的物理像素牙捉。但實際情況卻并非如此,css中的像素只是一個抽象的單位敬飒,在不同的設備或不同的環(huán)境中邪铲,css中的1px所代表的設備物理像素是不同的。在為桌面瀏覽器設計的網(wǎng)頁中无拗,我們無需對這個津津計較带到,但在移動設備上,必須弄明白這點蓝纲。在早先的移動設備中阴孟,屏幕像素密度都比較低晌纫,如iphone3税迷,它的分辨率為320x480,在iphone3上锹漱,一個css像素確實是等于一個屏幕物理像素的箭养。后來隨著技術(shù)的發(fā)展,移動設備的屏幕像素密度越來越高哥牍,從iphone4開始毕泌,蘋果公司便推出了所謂的Retina屏,分辨率提高了一倍嗅辣,變成640x960撼泛,但屏幕尺寸卻沒變化,這就意味著同樣大小的屏幕上澡谭,像素卻多了一倍愿题,這時,一個css像素是等于兩個物理像素的。其他品牌的移動設備也是這個道理潘酗。例如安卓設備根據(jù)屏幕像素密度可分為ldpi杆兵、mdpi、hdpi仔夺、xhdpi等不同的等級琐脏,分辨率也是五花八門,安卓設備上的一個css像素相當于多少個屏幕物理像素缸兔,也因設備的不同而不同日裙,沒有一個定論。
還有一個因素也會引起css中px的變化惰蜜,那就是用戶縮放阅签。例如,當用戶把頁面放大一倍蝎抽,那么css中1px所代表的物理像素也會增加一倍政钟;反之把頁面縮小一倍,css中1px所代表的物理像素也會減少一倍樟结。關(guān)于這點养交,在文章后面的部分還會講到。
在移動端瀏覽器中以及某些桌面瀏覽器中瓢宦,window對象有一個devicePixelRatio屬性碎连,它的官方的定義為:設備物理像素和設備獨立像素的比例,也就是 devicePixelRatio = 物理像素 / 獨立像素驮履。css中的px就可以看做是設備的獨立像素鱼辙,所以通過devicePixelRatio,我們可以知道該設備上一個css像素代表多少個物理像素玫镐。例如倒戏,在Retina屏的iphone上,devicePixelRatio的值為2恐似,也就是說1個css像素相當于2個物理像素杜跷。但是要注意的是,devicePixelRatio在不同的瀏覽器中還存在些許的兼容性問題矫夷,所以我們現(xiàn)在還并不能完全信賴這個東西葛闷,具體的情況可以看下這篇文章。
devicePixelRatio的測試結(jié)果:
三双藕、PPK的關(guān)于三個viewport的理論
ppk大神對于移動設備上的viewport有著非常多的研究(第一篇淑趾,第二篇,第三篇)忧陪,有興趣的同學可以去看一下扣泊,本文中有很多數(shù)據(jù)和觀點也是出自那里驳概。ppk認為,移動設備上有三個viewport旷赖。
首先顺又,移動設備上的瀏覽器認為自己必須能讓所有的網(wǎng)站都正常顯示,即使是那些不是為移動設備設計的網(wǎng)站等孵。但如果以瀏覽器的可視區(qū)域作為viewport的話稚照,因為移動設備的屏幕都不是很寬,所以那些為桌面瀏覽器設計的網(wǎng)站放到移動設備上顯示時俯萌,必然會因為移動設備的viewport太窄果录,而擠作一團,甚至布局什么的都會亂掉咐熙。也許有人會問弱恒,現(xiàn)在不是有很多手機分辨率都非常大嗎,比如768x1024棋恼,或者1080x1920這樣返弹,那這樣的手機用來顯示為桌面瀏覽器設計的網(wǎng)站是沒問題的吧?前面我們已經(jīng)說了爪飘,css中的1px并不是代表屏幕上的1px义起,你分辨率越大,css中1px代表的物理像素就會越多师崎,devicePixelRatio的值也越大默终,這很好理解,因為你分辨率增大了犁罩,但屏幕尺寸并沒有變大多少齐蔽,必須讓css中的1px代表更多的物理像素,才能讓1px的東西在屏幕上的大小與那些低分辨率的設備差不多床估,不然就會因為太小而看不清含滴。所以在1080x1920這樣的設備上,在默認情況下顷窒,也許你只要把一個div的寬度設為300多px(視devicePixelRatio的值而定)蛙吏,就是滿屏的寬度了源哩⌒回到正題上來,如果把移動設備上瀏覽器的可視區(qū)域設為viewport的話励烦,某些網(wǎng)站就會因為viewport太窄而顯示錯亂谓着,所以這些瀏覽器就決定默認情況下把viewport設為一個較寬的值,比如980px坛掠,這樣的話即使是那些為桌面設計的網(wǎng)站也能在移動瀏覽器上正常顯示了赊锚。ppk把這個瀏覽器默認的viewport叫做layout viewport治筒。這個layout viewport的寬度可以通過document.documentElement.clientWidth來獲取。
然而舷蒲,layout viewport的寬度是大于瀏覽器可視區(qū)域的寬度的耸袜,所以我們還需要一個viewport來代表 瀏覽器可視區(qū)域的大小,ppk把這個viewport叫做visual viewport牲平。visual viewport的寬度可以通過window.innerWidth來獲取堤框,但在Android 2, Oprea mini 和 UC 8中無法正確獲取。
現(xiàn)在我們已經(jīng)有兩個viewport了:layout viewport和visual viewport纵柿。但瀏覽器覺得還不夠蜈抓,因為現(xiàn)在越來越多的網(wǎng)站都會為移動設備進行單獨的設計,所以必須還要有一個能完美適配移動設備的viewport昂儒。所謂的完美適配指的是沟使,首先不需要用戶縮放和橫向滾動條就能正常的查看網(wǎng)站的所有內(nèi)容;第二渊跋,顯示的文字的大小是合適腊嗡,比如一段14px大小的文字,不會因為在一個高密度像素的屏幕里顯示得太小而無法看清拾酝,理想的情況是這段14px的文字無論是在何種密度屏幕叽唱,何種分辨率下,顯示出來的大小都是差不多的微宝。當然棺亭,不只是文字,其他元素像圖片什么的也是這個道理蟋软。ppk把這個viewport叫做ideal viewport镶摘,也就是第三個viewport——移動設備的理想viewport。
ideal viewport并沒有一個固定的尺寸岳守,不同的設備擁有有不同的ideal viewport凄敢。所有的iphone的ideal viewport寬度都是320px,無論它的屏幕寬度是320還是640湿痢,也就是說涝缝,在iphone中,css中的320px就代表iphone屏幕的寬度譬重。
但是安卓設備就比較復雜了拒逮,有320px的,有360px的臀规,有384px的等等滩援,關(guān)于不同的設備ideal viewport的寬度都為多少,可以到http://viewportsizes.com去查看一下塔嬉,里面收集了眾多設備的理想寬度玩徊。
再總結(jié)一下:ppk把移動設備上的viewport分為layout viewport租悄、visual viewport和ideal viewport三類,其中的ideal viewport是最適合移動設備的viewport恩袱,ideal viewport的寬度等于移動設備的屏幕寬度泣棋,只要在css中把某一元素的寬度設為ideal viewport的寬度(單位用px),那么這個元素的寬度就是設備屏幕的寬度了畔塔,也就是寬度為100%的效果外傅。ideal viewport 的意義在于,無論在何種分辨率的屏幕下俩檬,那些針對ideal viewport 而設計的網(wǎng)站萎胰,不需要用戶手動縮放,也不需要出現(xiàn)橫向滾動條棚辽,都可以完美的呈現(xiàn)給用戶技竟。
四、利用meta標簽對viewport進行控制
移動設備默認的viewport是layout viewport屈藐,也就是那個比屏幕要寬的viewport榔组,但在進行移動設備網(wǎng)站的開發(fā)時,我們需要的是ideal viewport联逻。那么怎么才能得到ideal viewport呢搓扯?這就該輪到meta標簽出場了。
我們在開發(fā)移動設備的網(wǎng)站時包归,最常見的的一個動作就是把下面這個東西復制到我們的head標簽中:
該meta標簽的作用是讓當前viewport的寬度等于設備的寬度锨推,同時不允許用戶手動縮放。也許允不允許用戶縮放不同的網(wǎng)站有不同的要求公壤,但讓viewport的寬度等于設備的寬度换可,這個應該是大家都想要的效果,如果你不這樣的設定的話厦幅,那就會使用那個比屏幕寬的默認viewport沾鳄,也就是說會出現(xiàn)橫向滾動條。
這個name為viewport的meta標簽到底有哪些東西呢确憨,又都有什么作用呢译荞?
meta viewport 標簽首先是由蘋果公司在其safari瀏覽器中引入的,目的就是解決移動設備的viewport問題休弃。后來安卓以及各大瀏覽器廠商也都紛紛效仿吞歼,引入對meta viewport的支持,事實也證明這個東西還是非常有用的玫芦。
在蘋果的規(guī)范中浆熔,meta viewport 有6個屬性(暫且把content中的那些東西稱為一個個屬性和值),如下:
width設置layout viewport的寬度桥帆,為一個正整數(shù)医增,或字符串"width-device"
initial-scale設置頁面的初始縮放值,為一個數(shù)字老虫,可以帶小數(shù)
minimum-scale允許用戶的最小縮放值叶骨,為一個數(shù)字,可以帶小數(shù)
maximum-scale允許用戶的最大縮放值祈匙,為一個數(shù)字忽刽,可以帶小數(shù)
height設置layout viewport的高度,這個屬性對我們并不重要夺欲,很少使用
user-scalable是否允許用戶進行縮放跪帝,值為"no"或"yes", no 代表不允許,yes代表允許
這些屬性可以同時使用些阅,也可以單獨使用或混合使用伞剑,多個屬性同時使用時用逗號隔開就行了。
此外市埋,在安卓中還支持? target-densitydpi? 這個私有屬性黎泣,它表示目標設備的密度等級,作用是決定css中的1px代表多少物理像素
target-densitydpi值可以為一個數(shù)值或 high-dpi 缤谎、 medium-dpi抒倚、 low-dpi、 device-dpi 這幾個字符串中的一個
特別說明的是坷澡,當 target-densitydpi=device-dpi 時托呕, css中的1px會等于物理像素中的1px。
因為這個屬性只有安卓支持频敛,并且安卓已經(jīng)決定要廢棄target-densitydpi這個屬性了镣陕,所以這個屬性我們要避免進行使用? 。
五姻政、把當前的viewport寬度設置為 ideal viewport 的寬度
要得到ideal viewport就必須把默認的layout viewport的寬度設為移動設備的屏幕寬度呆抑。因為meta viewport中的width能控制layout viewport的寬度,所以我們只需要把width設為width-device這個特殊的值就行了汁展。
下圖是這句代碼在各大移動端瀏覽器上的測試結(jié)果:
可以看到通過width=device-width鹊碍,所有瀏覽器都能把當前的viewport寬度變成ideal viewport的寬度,但要注意的是食绿,在iphone和ipad上侈咕,無論是豎屏還是橫屏,寬度都是豎屏時ideal viewport的寬度器紧。
這樣的寫法看起來誰都會做耀销,沒吃過豬肉,誰還沒見過豬跑啊~铲汪,確實熊尉,我們在開發(fā)移動設備上的網(wǎng)頁時罐柳,不管你明不明白什么是viewport,可能你只需要這么一句代碼就夠了狰住。
可是你肯定不知道
這句代碼也能達到和前一句代碼一樣的效果张吉,也可以把當前的的viewport變?yōu)?ideal viewport。
呵呵催植,傻眼了吧肮蛹,因為從理論上來講,這句代碼的作用只是不對當前的頁面進行縮放创南,也就是頁面本該是多大就是多大伦忠。那為什么會有 width=device-width 的效果呢?
要想清楚這件事情稿辙,首先你得弄明白這個縮放是相對于什么來縮放的昆码,因為這里的縮放值是1,也就是沒縮放邓深,但卻達到了 ideal viewport 的效果未桥,所以,那答案就只有一個了芥备,縮放是相對于 ideal viewport來進行縮放的冬耿,當對ideal viewport進行100%的縮放,也就是縮放值為1的時候萌壳,不就得到了 ideal viewport嗎亦镶?事實證明,的確是這樣的袱瓮。下圖是各大移動端的瀏覽器當設置了 后是否能把當前的viewport寬度變成 ideal viewport 的寬度的測試結(jié)果缤骨。
測試結(jié)果表明 initial-scale=1 也能把當前的viewport寬度變成 ideal viewport 的寬度,但這次輪到了windows phone 上的IE 無論是豎屏還是橫屏都把寬度設為豎屏時ideal viewport的寬度尺借。但這點小瑕疵已經(jīng)無關(guān)緊要了绊起。
但如果width 和 initial-scale=1同時出現(xiàn),并且還出現(xiàn)了沖突呢燎斩?比如:
width=400表示把當前viewport的寬度設為400px虱歪,initial-scale=1則表示把當前viewport的寬度設為ideal viewport的寬度,那么瀏覽器到底該服從哪個命令呢栅表?是書寫順序在后面的那個嗎笋鄙?不是。當遇到這種情況時怪瓶,瀏覽器會取它們兩個中較大的那個值萧落。例如,當width=400,ideal viewport的寬度為320時找岖,取的是400陨倡;當width=400, ideal viewport的寬度為480時宣增,取的是ideal viewport的寬度玫膀。(ps:在uc9瀏覽器中矛缨,當initial-scale=1時爹脾,無論width屬性的值為多少,此時viewport的寬度永遠都是ideal viewport的寬度)
最后箕昭,總結(jié)一下灵妨,要把當前的viewport寬度設為ideal viewport的寬度,既可以設置 width=device-width落竹,也可以設置 initial-scale=1泌霍,但這兩者各有一個小缺陷,就是iphone述召、ipad以及IE 會橫豎屏不分朱转,通通以豎屏的ideal viewport寬度為準。所以积暖,最完美的寫法應該是藤为,兩者都寫上去,這樣就 initial-scale=1 解決了 iphone夺刑、ipad的毛病缅疟,width=device-width則解決了IE的毛病:
六遍愿、關(guān)于meta viewport的更多知識
1存淫、關(guān)于縮放以及initial-scale的默認值
首先我們先來討論一下縮放的問題,前面已經(jīng)提到過沼填,縮放是相對于ideal viewport來縮放的桅咆,縮放值越大,當前viewport的寬度就會越小坞笙,反之亦然岩饼。例如在iphone中,ideal viewport的寬度是320px羞海,如果我們設置 initial-scale=2 忌愚,此時viewport的寬度會變?yōu)橹挥?60px了,這也好理解却邓,放大了一倍嘛硕糊,就是原來1px的東西變成2px了,但是1px變?yōu)?px并不是把原來的320px變?yōu)?40px了,而是在實際寬度不變的情況下简十,1px變得跟原來的2px的長度一樣了檬某,所以放大2倍后原來需要320px才能填滿的寬度現(xiàn)在只需要160px就做到了。因此螟蝙,我們可以得出一個公式:
visual viewport寬度 = ideal viewport寬度? / 當前縮放值
當前縮放值 = ideal viewport寬度? / visual viewport寬度
ps: visual viewport的寬度指的是瀏覽器可視區(qū)域的寬度恢恼。
大多數(shù)瀏覽器都符合這個理論,但是安卓上的原生瀏覽器以及IE有些問題胰默。安卓自帶的webkit瀏覽器只有在 initial-scale = 1 以及沒有設置width屬性時才是表現(xiàn)正常的场斑,也就相當于這理論在它身上基本沒用;而IE則根本不甩initial-scale這個屬性牵署,無論你給他設置什么漏隐,initial-scale表現(xiàn)出來的效果永遠是1。
好了奴迅,現(xiàn)在再來說下initial-scale的默認值問題青责,就是不寫這個屬性的時候,它的默認值會是多少呢取具?很顯然不會是1脖隶,因為當 initial-scale = 1 時慨蓝,當前的layout viewport寬度會被設為 ideal viewport的寬度隔嫡,但前面說了胁勺,各瀏覽器默認的 layout viewport寬度一般都是980啊踩寇,1024啊窑业,800啊等等這些個值士修,沒有一開始就是 ideal viewport的寬度的智什,所以 initial-scale的默認值肯定不是1持灰。安卓設備上的initial-scale默認值好像沒有方法能夠得到榨乎,或者就是干脆它就沒有默認值怎燥,一定要你顯示的寫出來這個東西才會起作用,我們不管它了蜜暑,這里我們重點說一下iphone和ipad上的initial-scale默認值铐姚。
根據(jù)測試,我們可以在iphone和ipad上得到一個結(jié)論肛捍,就是無論你給layout viewpor設置的寬度是多少隐绵,而又沒有指定初始的縮放值的話,那么iphone和ipad會自動計算initial-scale這個值拙毫,以保證當前l(fā)ayout viewport的寬度在縮放后就是瀏覽器可視區(qū)域的寬度依许,也就是說不會出現(xiàn)橫向滾動條。比如說缀蹄,在iphone上峭跳,我們不設置任何的viewport meta標簽膘婶,此時layout viewport的寬度為980px,但我們可以看到瀏覽器并沒有出現(xiàn)橫向滾動條蛀醉,瀏覽器默認的把頁面縮小了悬襟。根據(jù)上面的公式,當前縮放值 = ideal viewport寬度? / visual viewport寬度拯刁,我們可以得出:
當前縮放值 = 320 / 980
也就是當前的initial-scale默認值應該是 0.33這樣子脊岳。當你指定了initial-scale的值后,這個默認值就不起作用了垛玻。
總之記住這個結(jié)論就行了:在iphone和ipad上割捅,無論你給viewport設的寬的是多少,如果沒有指定默認的縮放值夭谤,則iphone和ipad會自動計算這個縮放值棺牧,以達到當前頁面不會出現(xiàn)橫向滾動條(或者說viewport的寬度就是屏幕的寬度)的目的巫糙。
2朗儒、動態(tài)改變meta viewport標簽
第一種方法
可以使用document.write來動態(tài)輸出meta viewport標簽,例如:
document.write('<meta name="viewport" content="width=device-width,initial-scale=1">')
第二種方法
通過setAttribute來改變
varmvp=document.getElementById('testViewport');
安卓2.3自帶瀏覽器上的一個bug
alert(document.documentElement.clientWidth);//彈出600参淹,正常情況應該彈出320
alert(document.documentElement.clientWidth);//彈出320醉锄,正常情況應該彈出600
測試的手機ideal viewport 寬度為320px,第一次彈出的值是600,但這個值應該是第行meta標簽的結(jié)果啊浙值,然后第二次彈出的值是320恳不,這才是第一行meta標簽所達到的效果啊,所以在安卓2.3(或許是所有2.x版本中)的自帶瀏覽器中开呐,對meta viewport標簽進行覆蓋或更改烟勋,會出現(xiàn)讓人非常迷糊的結(jié)果。
七筐付、結(jié)語
說了那么多廢話卵惦,最后還是有必要總結(jié)一點有用的出來。
首先如果不設置meta viewport標簽瓦戚,那么移動設備上瀏覽器默認的寬度值為800px沮尿,980px,1024px等這些较解,總之是大于屏幕寬度的畜疾。這里的寬度所用的單位px都是指css中的px,它跟代表實際屏幕物理像素的px不是一回事印衔。
第二啡捶、每個移動設備瀏覽器中都有一個理想的寬度,這個理想的寬度是指css中的寬度奸焙,跟設備的物理寬度沒有關(guān)系瞎暑,在css中徒溪,這個寬度就相當于100%的所代表的那個寬度。我們可以用meta標簽把viewport的寬度設為那個理想的寬度金顿,如果不知道這個設備的理想寬度是多少臊泌,那么用device-width這個特殊值就行了,同時initial-scale=1也有把viewport的寬度設為理想寬度的作用揍拆。所以渠概,我們可以使用
來得到一個理想的viewport(也就是前面說的ideal viewport)。
為什么需要有理想的viewport呢嫂拴?比如一個分辨率為320x480的手機理想viewport的寬度是320px播揪,而另一個屏幕尺寸相同但分辨率為640x960的手機的理想viewport寬度也是為320px,那為什么分辨率大的這個手機的理想寬度要跟分辨率小的那個手機的理想寬度一樣呢筒狠?這是因為猪狈,只有這樣才能保證同樣的網(wǎng)站在不同分辨率的設備上看起來都是一樣或差不多的。實際上辩恼,現(xiàn)在市面上雖然有那么多不同種類不同品牌不同分辨率的手機雇庙,但它們的理想viewport寬度歸納起來無非也就 320、360灶伊、384疆前、400等幾種,都是非常接近的聘萨,理想寬度的相近也就意味著我們針對某個設備的理想viewport而做出的網(wǎng)站竹椒,在其他設備上的表現(xiàn)也不會相差非常多甚至是表現(xiàn)一樣的。