前端 H5 橫屏 獨特處理方案詳解

年糕媽媽技術團隊發表於2018-11-09

前言

隨著公司知識付費業務的發展,產品發現橫屏展示內容的效果和體驗都要比豎屏要來的好。我也感覺確實是這樣的~。剛接到這個需求的時候,稍微想了一下,橫屏這還不簡單直接把整個頁面旋轉90度,不就完事了?結果真正開發的過程中發現並沒有這麼簡單。比如螢幕橫過來手勢滑動的方向沒有變過來等等!於是嘗試去百度了一下最終並沒有搜到滿意的答案。後來決定自己摸索一下,下面是我開發過程中總結了一套做法,有更好的做好的同學也可以分享指正。

效果圖

首先給大家看看效果圖

效果圖一:

前端 H5 橫屏 獨特處理方案詳解
效果圖二:
前端 H5 橫屏 獨特處理方案詳解
效果圖三:
前端 H5 橫屏 獨特處理方案詳解

看上去還是挺高大上的吧~,不過這效果圖是針對app原生設計的,實際H5頁面在微信裡面訪問還會有標題欄和底部返回欄,如下圖h5實際展示效果(android):

前端 H5 橫屏 獨特處理方案詳解

大家可以進去先體驗一下(用微信訪問),專案地址 m.ngmm365.com/mathbox/ind… 頁面載入完畢之後,點選試學按鈕即可進入橫屏頁面。

專案需求及分析

專案需求

1、需求一:效果圖一的星球要能支援左右滑動,滑動過程中還要能支援3D(遠近縮放)的效果

2、需求二:效果圖二會有多節課需要能支援左右滑動

3、需求三:效果圖三內容要剛好佔一屏

分析

1、分析需求一:星球切換部分使用swiper來實現。

2、分析需求二:好像沒什麼難的,使用系統預設的滾動條就可以支援滾動了。

3、分析需求三:嗯~,佔一屏還不簡單!直接按效果圖尺寸寫就好了。

實踐總結

公司使用的是vue技術棧,所以下面的元件也是基於vue來寫的。懂了原理我相信不管用什麼框架去實現都不會有太大問題,所以不懂vue的同學也不用慌。

問題及解決方案

1、現在首先要做的第一件事情就是怎樣先讓頁面橫過來?

h5沒有橫屏屬性那隻能通過旋轉頁面的元素來達到橫屏的效果了transform: rotate(90deg);,所以我們要首先要有一個橫屏容器元件。然後把對應頁面的內容都放在這個容器裡面頁面是不是就橫過來了?

橫屏容器元件程式碼如下:

<template>
    <section v-horizontal-screen @touchmove.prevent>
        <!-- 頁面具體內容 -->
        <slot></slot>
    </section>
</template>
<script>
export default {
	directives: {
        'horizontal-screen': {
            bind(el, binding, vnode){
                let self = vnode.context;
                
                function reset(init){
                    
                    let width = document.documentElement.clientWidth,
                        height = document.documentElement.clientHeight;
                    //在豎屏狀態我們通過新增transform:rotate(90deg),來讓這個頁面橫過來
                    if(window.orientation == null || window.orientation === 180 || window.orientation === 0){//豎屏狀態
                        el.style.webkitTransform = el.style.transform = `rotate(90deg)`;
                        el.style.width = `${height}px`;
                        el.style.height = `${width}px`;
                        el.style.webkitTransformOrigin = el.style.transformOrigin = `${width / 2}px center`;
                        //如果已經處於橫屏狀態就不做其他處理了
                    }else if(window.orientation === 90 || window.orientation === -90){//橫屏狀態
                        el.style.webkitTransform = el.style.transform = `rotate(0)`;
                        el.style.width = `${width}px`;
                        el.style.height = `${height}px`;
                    }
                }
                reset(true);

                let timer = null;
                el.fn = function(e) {
                    clearTimeout(timer);
                    timer = setTimeout(reset, 300);
                }
                
                window.addEventListener('resize', el.fn, false);

                if("onorientationchange" in window){
                    window.addEventListener('orientationchange', el.fn, false);
                }
            },
            unbind(el, binding, vnode){
                window.removeEventListener('resize', el.fn, false);
                window.removeEventListener('orientationchange', el.fn, false);
            }
        },
    }
}
</script>
複製程式碼

現在頁面已經橫過來,接下來先讓星球能夠左右切換吧。是不是直接加入swiper外掛就可以了?

嘗試引入swiper外掛,然後執行起來的時候會發現,手勢反了,上下滑動的時候內容才會左右滑動。因為現在我們只是把頁面橫過來了,x軸和y軸並沒有變。當我們上下滑動的時候實際上改變的是x軸,也就是正常頁面的左右滑動。帶著好奇心查了一下swiper的api看看是否有支援這種操作引數。結果沒有查到,只好自己嘗試去修改原始碼了。下面是修改程式碼後的部分截圖:

前端 H5 橫屏 獨特處理方案詳解

增加了一個引數isReverse,如果為true則x軸和y軸反一反。當然isReverse還要判斷一下特殊情況比如本來app就已經橫屏了其實就沒必要x軸y軸互反了。這樣我們的內容又可以正常跟著手指方向滑動了。

前面暫定需求二的滾動方式採用預設滾動條來解決,結果實現出來之後發現手指滑動方向也是反。原生的手勢反了,改怎麼調整呢?網上查了一通沒有找到答案,關鍵這問題也不是很好描述!因為之前有使用過iscroll,可以模擬系統滾動條的效果。嘗試加入到程式碼中,發現和swiper一樣也會出現手指滑動方向反的情況。最後也只能自己去原始碼中增加一個引數了。整體對比iscroll改動的程式碼要比swiper少的多。只需要改一個地方就可以了,截圖如下:

前端 H5 橫屏 獨特處理方案詳解

需求一和需求二都解決了,接下來來解決“最簡單”的第三個需求。其實按照設想不用做任何其它操作,只需要按照效果圖的尺寸寫就好了。

頁面寫好了,在電腦上跑也沒什麼問題。決定部署到測試環境用手機跑一下看看。結果發現右側內容超出螢幕範圍了。是因為在微信中有標題欄和底部欄,導致內容超出了螢幕範圍。聯想一下其實在不同長寬比的手機上 其實即使不存在標題欄和底部欄 其實也會有類似情況。這該怎麼解決呢?最後決定在橫屏容器元件內部再加入一個自動縮放元件,最後頁面的框架圖長下面這樣。當然自動縮放元件只在需要的地方加入。

前端 H5 橫屏 獨特處理方案詳解
接下來一起看看比例縮放容器的程式碼吧。

<template>
	<div class="scale-wrap" :style="scaleWrapStyle">

		<slot></slot>

	</div>
</template>
<script>
//這塊寬高比例根據效果圖來設定
const DesignWidth = 375;
const DesignHeight = 667;
const DesignRatio = DesignWidth / DesignHeight;

function getScale(){

    let width = document.documentElement.clientWidth,
        height = document.documentElement.clientHeight;

    if(window.orientation == null || window.orientation === 180 || window.orientation === 0){//豎屏狀態
    	
    }else {
    	[width, height] = [height, width];
    }

    let ratio = width / height;

    let scale;

    //通過計算實際手機寬高比和效果圖的寬高比得出最終的所讓比例
    if(ratio > DesignRatio){
    	scale = height / (width / DesignRatio);
    }else if(ratio < DesignRatio){
    	scale = width / (height * DesignRatio);
    }else{
    	scale = 1;
    }

    return scale;

}

export default {
    data(){
        return {
        
        }
    },
    computed: {
        scaleWrapStyle(){
            let scale = getScale();
            let scaleStr = `scale(${scale})`;
            return { 'transform': scaleStr, '-webkit-transform': scaleStr };
        }
    },
}
</script>
<style lang="less" scoped>
.scale-wrap{
	width: 100%;
	height:100%;
	display: flex;
	transform-origin: left center;
}
</style>
複製程式碼

到此為止我們已經實現了對橫屏需求處理的一套方案~

【當然如果你遇到前面這些方案無法解決的橫屏問題也可以發出來討論一下。看看有沒有好的辦法去解決。】

最後如果文件對你有幫助,就給個贊吧~

相關程式碼

github.com/taoxhsmile/…

署名

by:Tao

相關文章