吸頂處理兼容iOS和Android

問題拋出

當頁面上滑時,頂部當導航需要進行吸頂處理。滑動過程使用scroll事件監聽,當滾動的高度到底一定時,便設置樣式 absolute:fixed;top:0px;以吸頂處理。

以上處理在安卓中時完全沒有問題的,但是在iOS中缺會出現嚴重的樣式問題:頁面滾動過程中並不吸頂。

解決思路

針對iOS做兼容處理。iOS支持一個特殊的定位方式:absolute:sticky;//粘性定位。

sticky 定位

sticky 英文字面意思是粘,粘貼,所以可以把它稱之爲粘性定位。
position: sticky; 基於用戶的滾動位置來定位。
粘性定位的元素是依賴於用戶的滾動,在 position:relative 與 position:fixed 定位之間切換。
它的行爲就像 position:relative; 而當頁面滾動超出目標區域時,它的表現就像 position:fixed;,它會固定在目標位置。
元素定位表現爲在跨越特定閾值前爲相對定位,之後爲固定定位。
這個特定閾值指的是 top, right, bottom 或 left 之一,換言之,指定 top, right, bottom 或 left 四個閾值其中之一,纔可使粘性定位生效。否則其行爲與相對定位相同。

div.sticky {
    position: -webkit-sticky; /* Safari */
    position: sticky;
    top: 0;
    background-color: green;
    border: 2px solid #4CAF50;
}

解決辦法

判斷設備類型

const judgeUserAgent = function () {
  if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
    return 'ios'
  } else if (/(Android)/i.test(navigator.userAgent)) {
    return 'android'
  } else {
    return 'pc'
  }
}

安卓的吸頂處理

positionSticky (el, top = 0, zIndex = 10) {
  const ot = el.offsetTop - top
  const fn = () => {
    const st = document.body.scrollTop || document.documentElement.scrollTop
    const fixStyle = `position: fixed;top: ${top}px;left: 0px;z-index: ${zIndex};`
    el.setAttribute('style', st > ot ? fixStyle : '')
  }
  fn()
  document.addEventListener('scroll', fn)
}

注意:安卓的fixed定位需要在滾動時沒有問題,但是從relative定位切換到fixed定位後,會脫離文本流,使得切換fixed樣式時頁面會出現閃爍的情況。
解決辦法:在需要的進行fixed定位的dom部分外邊使用一個div將其包裹起來,並賦予相應的高度。

<div style="height:50px;">
	<!-->需要進行定位的dom部分<-->
	<div ref="postionDom">
		<!-->內容<-->		
	</div>
</div>
// vue中mounted鉤子
mounted () {
	const el = this.$refs.postionDom
	this.positionSticky(el, 0, 10)
}

iOS的吸頂處理

positionSticky (el, top = 0, zIndex = 10) {
	if (window.screen.height === 812 && window.screen.width === 375) { // 在iphonex中打開
	  top += 20
	}
	const fixStyle = `position: -webkit-sticky;position: sticky;top: ${top}px;left: 0px;z-index: ${zIndex};`
	el.setAttribute('style', fixStyle)
	return
}
<!-->需要進行定位的dom部分<-->
<div ref="postionDom">
	<!-->內容<-->		
</div>
// vue中mounted鉤子
mounted () {
	const el = this.$refs.postionDom
	this.positionSticky(el, 0, 10)
}

代碼整合

注意:兼容處理依然需要將不同設備類型的代碼整合起來,這樣易於調用與維護。
最終代碼

const judgeUserAgent = function () {
  if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
    return 'ios'
  } else if (/(Android)/i.test(navigator.userAgent)) {
    return 'android'
  } else {
    return 'pc'
  }
}

const positionSticky = function(el, top = 0, zIndex = 10) {
  const userAgent = judgeUserAgent()
  if (userAgent === 'ios') {
  	// 雖然iOS中不需要在dom外邊包裹一層,但是爲了兼容android。
  	// 注意,sticky定位元素不能在其外層的dom元素使用div進行包裹起來,否則sticky會失效。定位到parentNode剛好也避過這個坑
    el = el.parentNode
    if (window.screen.height === 812 && window.screen.width === 375) { // 在iphonex中打開
      top += 20
    }
    const fixStyle = `position: -webkit-sticky;position: sticky;top: ${top}px;left: 0px;z-index: ${zIndex};`
    el.setAttribute('style', fixStyle)
    return
  }
  const ot = el.offsetTop - top
  const fn = () => {
    const st = document.body.scrollTop || document.documentElement.scrollTop
    const fixStyle = `position: fixed;top: ${top}px;left: 0px;z-index: ${zIndex};`
    el.setAttribute('style', st > ot ? fixStyle : '')
  }
  fn()
  document.addEventListener('scroll', fn)
}
<div style="height:50px;">
	<!-->需要進行定位的dom部分<-->
	<div ref="postionDom">
		<!-->內容<-->		
	</div>
</div>
// vue中mounted鉤子
mounted () {
	const el = this.$refs.postionDom
	this.positionSticky(el, 0, 10)
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章