1.有些移動(dòng)設(shè)備不支持這個(gè) window.orientation 這個(gè)屬性,,導(dǎo)致沒法檢測(cè)。
2.有些設(shè)備橫屏和豎屏并不一定代表橫屏就是寬度最大,,豎屏就是高度大的,,橫屏和豎屏的定義是有廠商規(guī)定那個(gè)正面的屏幕方向。
由此帶來的問題,,無法檢測(cè)到到底哪個(gè)方向才是我們期望知道的橫屏和豎屏,,可能導(dǎo)致我們的顯示效果出錯(cuò),當(dāng)然這個(gè)還是不多的,,但不能排除,。
下面分享實(shí)際中有哪些方法實(shí)現(xiàn)以及他們的優(yōu)缺點(diǎn),最后會(huì)給出一個(gè)最佳解決方案,。
CSS Media Queries通過媒體查詢的方式,,我們可以通過以下方法來實(shí)現(xiàn)根據(jù)橫豎屏不同的情況來適配樣式:
1.內(nèi)聯(lián)樣式@media screen and (orientation: portrait) {
// 豎屏
}
@media screen and (orientation: landscape) {
// 橫屏
}
2.外聯(lián)樣式<!-- 豎屏 -->
<link rel="stylesheet" media="all and (orientation:portrait)" href="..." />
<!-- 橫屏 -->
<link rel="stylesheet" media="all and (orientation:landscape)" href="..." /> window.matchMedia()除此之外,CSS Object Model(CSSOM)Views 規(guī)范增加了對(duì) JavaScript 操作 CSS Media Queries 的原生支持,,它在 window 對(duì)象下增加了 matchMedia() 方法,,讓我們能夠通過腳本的方式來實(shí)現(xiàn)媒體查詢。
window.matchMedia()
方法接受一個(gè) Media Queries 語句的字符串作為參數(shù),,返回一個(gè) MediaQueryList 對(duì)象,。該對(duì)象有 media 和 matches 兩個(gè)屬性:
media:返回所查詢的 Media Queries 語句字符串 matches:返回一個(gè)布爾值,表示當(dāng)前環(huán)境是否匹配查詢語句 同時(shí),,它還包含了兩個(gè)方法,,用來監(jiān)聽事件:
addListener(callback):綁定回調(diào) callback 函數(shù) removeListener(callback):注銷回調(diào) callback 函數(shù) 那么,通過 window.matchMedia()
的方法,,我們可以這樣判斷橫豎屏:
那么,,通過 window.matchMedia()
的方法,我們可以這樣判斷橫豎屏:
var mql = window .matchMedia("(orientation: portrait)" );
function onMatchMeidaChange (mql ) {
if (mql.matches) {
}else {
}
}
onMatchMeidaChange(mql);
mql.addListener(onMatchMeidaChange);
通過Can I Use - matchMeida 可以知道,,該API在移動(dòng)端得到良好的支持,,并無兼容性 問題。
window.innerHeight/window.innerWidthThe ‘orientation’ media feature is ‘portrait’ when the value of the ‘height’ media feature is greater than or equal to the value of the ‘width’ media feature. Otherwise ‘orientation’ is ‘landscape’. —— CSS/Mediaqueries/orientation
在 CSS Media Queries 中,,Orientation 屬性有兩個(gè)值:
portrait,,指的是當(dāng) height 大于等于 width 的情況 landscape,,指的是當(dāng) height 小于 width 的情況 所以,還有一種最為常見的方法是通過比較頁面的寬高,,當(dāng)頁面的高大于等于寬時(shí)則認(rèn)為是豎屏,,反之則為橫屏。
function detectOrient () {
if (window .innerHeight >= window .innerWidth) {
}else {
}
}
detectOrient();
window .addEventListener('resize' ,detectOrient);
window.orientation在 ios 平臺(tái)以及大部分 Android 手機(jī)都有支持 window.orientation
這個(gè)屬性,,它返回一個(gè)與默認(rèn)屏幕方向偏離的角度值:
0:代表此時(shí)是默認(rèn)屏幕方向 90:代表順時(shí)針偏離默認(rèn)屏幕方向90度 -90:代表逆時(shí)針偏離默認(rèn)屏幕方向90度 180:代表偏離默認(rèn)屏幕方向180度 在 iOS 的開發(fā)者文檔(iOS Developer Library - Handling Orientation Events )是這樣明確定義的:
switch (window .orientation) {
case 0 :
displayStr += "Portrait" ;
break ;
case -90 :
displayStr += "Landscape (right, screen turned clockwise)" ;
break ;
case 90 :
displayStr += "Landscape (left, screen turned counterclockwise)" ;
break ;
case 180 :
displayStr += "Portrait (upside-down portrait)" ;
break ;
}
也就是如下圖所示:
(圖來自William Malone - DETECT IOS DEVICE ORIENTATION WITH JAVASCRIPT )
在實(shí)際應(yīng)用中,,對(duì)于 iphone 和大部分 Android 是沒有180度的手機(jī)豎屏翻轉(zhuǎn)的情況的,但是 iPad 是存在的,。所以,,簡(jiǎn)化下代碼,我們可以綁定orientationchange
事件來判斷橫豎屏:
function detectOrient () {
if (Math .abs(window .orientation) === 90 ) {
} else {
}
}
detectOrient();
window .addEventListener('orientationchange' ,detectOrient);
影響判斷的問題所在 1.對(duì)window.orientation屬性值的不一致在 iOS 平臺(tái),,對(duì) window.orientation
屬性值是無異議的,,規(guī)范當(dāng)中有明確規(guī)定每個(gè)值對(duì)應(yīng)的情況。但是對(duì)于 Android 平臺(tái),,就有不一致的特殊情況出現(xiàn),。
A misconception about window.orientation 中作者 Matthew Gifford 就有提到部分 Android 機(jī)型(該文章中測(cè)試用的 Toshiba Thrive 機(jī)型)返回的情況是與期望情況是相反的;除此之外,,在 StackOverflow 上也有反饋過這樣的問題(例如,,window.orientation returns different values in iOS and Android 中提到的 Samsung Tab 2 機(jī)型)。
其實(shí),,Matthew Gifford 認(rèn)為這并不是 BUG(筆者也認(rèn)同),,按照Compatibility Standard - 4.2 window.orientation API 規(guī)范中的定義,0
值指的是 natural ,、 default 的屏幕方向,所以如果生廠商對(duì) natural ,、 default 狀態(tài)是用戶應(yīng)當(dāng)手持設(shè)備方向?yàn)闄M屏,,那么 0
值對(duì)應(yīng)為 landscape 的橫屏方向了。 針對(duì)這種不一致情況的出現(xiàn),,對(duì)于追求完美的開發(fā)者來說,,通過 window.orientation
的方法來判斷橫豎屏則變得有點(diǎn)不可靠的。
2.軟鍵盤的彈出是否除了 window.orientation
的其它方法都是可靠的呢,? 然而,,實(shí)際上是事與愿違的。在 Android 下,,如果頁面中出現(xiàn)軟鍵盤彈出的情況(存在有 Input 的元素)時(shí),,頁面有時(shí)會(huì)因?yàn)檐涙I盤的彈出而導(dǎo)致頁面回縮,即頁面的寬度(豎屏?xí)r)或者高度(橫屏?xí)r)被改變,。 無論是 CSS Media Queries 還是 window.matchMedia()
方法,,還是根據(jù) window.innerWidth
,、window.innerHeight
的頁面寬高比對(duì)方法來實(shí)現(xiàn)的橫豎屏判斷方法,都會(huì)因此受到影響,,出現(xiàn)判斷失誤的情況( Samsung SCH-i699 機(jī)型,,在豎屏?xí)r由于軟鍵盤彈出導(dǎo)致頁面高度小于寬度,被錯(cuò)誤地判定為橫屏),。 所以,,在這樣的情況下,這幾種方式也變得不可靠,。
探討最佳實(shí)現(xiàn)方式假如屏幕分辨率固定值為:screen.width
和 screen.height
若獲取 當(dāng)前頁面的寬(document.documentElement.clientWidth
),,等于屏幕分辨率的寬(screen.width
),則可認(rèn)定當(dāng)前屬于豎屏 ,。 若獲取 當(dāng)前頁面的寬(document.documentElement.clientWidth
),,等于屏幕分辨率的高(screen.height
),則可認(rèn)定當(dāng)前屬于橫屏 ,。 對(duì)應(yīng)的代碼如下(更新后的代碼),,demo體驗(yàn) :
// 判斷橫豎屏
var utils = {
debounce: function(func,delay){
var timer = null;
return function(){
var context = this,
args = arguments;
clearTimeout(timer);
timer = setTimeout(function(){
func.apply(context,args);
},delay);
}
}
}
var detectRes = document.getElementById('J_detectRes');
var detectData = document.getElementById('J_detectData');
function detectOrient() {
var storage = localStorage; // 不一定要使用localStorage,其他存儲(chǔ)數(shù)據(jù)的手段都可以
var data = storage.getItem('J-recordOrientX');
var cw = document.documentElement.clientWidth;
var _Width = 0,
_Height = 0;
if(!data) {
sw = window.screen.width;
sh = window.screen.height;
// 2.在某些機(jī)型(如華為P9)下出現(xiàn) srceen.width/height 值交換,,所以進(jìn)行大小值比較判斷
_Width = sw < sh ? sw : sh;
_Height = sw >= sh ? sw : sh;
storage.setItem('J-recordOrientX',_Width + ',' + _Height);
}else {
var str = data.split(',');
_Width = str[0];
_Height = str[1];
}
if(cw == _Width) {
// 豎屏
return;
}
if(cw == _Height){
// 橫屏
return;
}
}
// 3.函數(shù)去抖處理
window.onresize = utils.debounce(detectOrient,300);
detectOrient(); 1.橫豎屏切換時(shí),,screen.width
與screen.height
的值可能會(huì)改變隨著橫豎屏幕的切換,screen.width
與screen.height
在大部分機(jī)型上會(huì)維持不變,,而在一些機(jī)型上如@Jc,、@百思不得姐夫 提出的華為 P9 微信內(nèi)置瀏覽器(6.5.4版本)、Chrome桌面端瀏覽器模擬器中會(huì)出現(xiàn)值交換的現(xiàn)象,。
例如,,在Chome上 iPhone 6 模擬器中,豎屏?xí)rscreen.width
與screen.height
等于375px,、667px,,而橫屏?xí)r,sreen.width
與screen.height
等于 667px ,、 375px,,兩者屬性值出現(xiàn)了值交換現(xiàn)象。
這個(gè)問題很容易解決,,雖然出現(xiàn)了值交換,,但是值大小還是不變的,那么我們可以先通過比較大小來判斷出屬性值較小的是screen.width
,,而屬性值較大的是screen.height
,,然后再用來與document.documentElement.clientWidth/clientHeight
進(jìn)行比較,從而判斷出橫豎屏。
2.Meta Viewport的設(shè)置會(huì)影響document.documentElement.clientWidth/clientHeight
Peter-Paul Koch 的《兩個(gè) Viewport 的故事》 的一文中提出的關(guān)于 Viewport 的理論被認(rèn)為是業(yè)界的主流論調(diào),,它指出 Layout Viewport 的尺寸可以通過document.documentElement.clientWidth/clientHeight
進(jìn)行度量,。而通過設(shè)置 Meta Viewport (也就是 viewport meta 標(biāo)簽)是可以改變 Layout Viewport 的尺寸。
所以,,Meta Viewport的屬性設(shè)置如何是會(huì)影響到document.documentElement.clientWidth/clientHeight
的值,,這就是一部分讀者迷惑到”為什么會(huì)我測(cè)量document.documentElement.clientWidth/clientHeight
的值與screen.width/height
的值不相同?“的原因所在,。
因此,,在這里也補(bǔ)充一點(diǎn),在筆者提出的方法中,,有個(gè)忘記跟大家說明的前提——頁面設(shè)置了以下屬性以保證頁面的適配:
1
<meta name ="viewport" content ="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;" />
這句語句的設(shè)置就保證了頁面是始終適配屏幕的,,在橫豎屏切換的場(chǎng)景中document.documentElement.clientWidth/clientHeight
必然與screen.width/height
其中一值相等,并且這也是本文提出的橫豎屏檢測(cè)方法的核心,。
3.resize事件的多次觸發(fā)筆者是通過綁定監(jiān)聽resize事件來響應(yīng)執(zhí)行橫豎屏檢測(cè)方法的,,而在實(shí)際應(yīng)用中確實(shí)出現(xiàn)了resize事件觸發(fā)兩次的情況。
雖然并沒有影響到事件的判斷結(jié)果,,但是這也算個(gè)值得優(yōu)化的點(diǎn),,而且問題也不大,我們只要通過函數(shù)去抖( Debounce Function ) 辦法來進(jìn)行簡(jiǎn)單的解決就好,。
詳情參考:凹凸實(shí)驗(yàn)室 探討判斷橫豎屏的最佳實(shí)現(xiàn)