スクロール特性の分析 | JD Cloud テクニカルチーム

最近、jsをネイティブに使用してスライドコンポーネントを開発する必要があるプロジェクトがあり、dom要素のさまざまな属性が頻繁に使用されますが、その中でも高さの各種属性とtop属性は類似した名前が多く、意味が混同されやすいです。そこで、よく使う知識を特別にまとめ、記事の最後では簡単なモバイルスクロールコンポーネントの実装に挑戦します。

高さとトップを理解するには、ボックス モデルから始める必要があります。まず、css3 で定義されたボックス モデルについて理解しましょう。ページ上で dom 要素が占める実際の領域は、次の図で示すことができます。

 卵に例えると、外側から内側まで

オレンジ色の部分~外縁マージン:卵の殻

黒色部分~境界線:卵殻膜

緑色の領域 - 内側の余白のパディング: 卵白

白い部分 内容量:卵黄

私たちが本当に気にするのは卵の内容であり、卵の最も栄養価の高い部分です。コンテンツの周りに非常に多くのレイヤーがラップされているため、ページ内の DOM 構造の全体的な密度が感じられ、高密度のテキストや画像に悩まされることはありません。ボックス モデルをより適切に説明するために、それらを説明する他のいくつかのインターフェイスが Web 仕様で定義されています。これらが今日説明する主役です。

 

上の図では、ネストされた関係を持つ dom 要素の 2 つの層を記述しています。ここのコンテンツ領域は前の図とは異なることに注意してください。ここのコンテンツ領域も独立した dom 要素です (つまり、独自のマージンもあります)簡略化のため、子要素は特に描画されません)、子要素の全体の高さが非常に高く、親要素の高さを超えているため、完全に描画することはできません。つまり、両者はローリング関係を構成します。次のプロパティを説明してみましょう。

一、クライアントの身長

読み取り専用プロパティ。clientHeight は実際には垂直スクロール バーの高さです。一般に、垂直スクロール バーは上下の境界線の近くにある必要があるため、clientHeight = 上下のパディング + コンテンツの高さになります。特殊なケースがあることを忘れないでください。水平スクロール バーがある場合、水平スクロール バーが垂直スクロール バーのスペースの一部を占めることも考慮する必要があります。つまり、 clientHeight = 上下のパディング +コンテンツの高さ - 水平スクロールバーの高さ。卵を比喩として使用すると、clientHeight は殻をむいた卵になります。

2、クライアントトップ

読み取り専用プロパティ。上枠の幅を記述します。トップの境界線は髪の太さを思い出させやすいので、次回鏡の前で、「クライアントのトップは減りましたが、あなたも強くなりました」と自分に言い聞かせることができます。

3.オフセット高さ

読み取り専用プロパティ。offsetHeight は clientHeight に似ています。観察すると、clientHeight よりも追加の境界層があることがわかります: offsetHeight = clientHeight + 上下の境界線。ここで、卵の殻を剥く前の瞬間に戻り、卵を水から取り出し、洗浄し、オフセット高さを表示します。

4、オフセット上

読み取り専用プロパティ。offsetParent 要素の上部パディングを基準とした現在の要素の距離を返します。この距離には、それ自体と親要素の境界線の幅は含まれません。実際、 offsetTop = 自身の上マージン + 親要素の上パディングであり、これは卵と卵の箱の間のバッフルの厚さに相当します。

5、scrollHeight

読み取り専用プロパティ。オーバーフローによりビューに表示されないコンテンツを含む、要素のコンテンツの高さの尺度を記述します。これをハッキングして、親要素の非表示のコンテンツを表示できます。子要素は、垂直方向に 2 つの部分 (外側のマージンと内側の offsetHeight) に分割できます。

スクロール関係を持つ親子要素の場合、scrollHeight は異なる意味を持ちます。

(1) 子要素の場合、子要素自体にオーバーフロー部分が含まれていないため、scrollHeightとclientHeightは同じ値になります。

(2) 親要素の場合、親要素のコンテンツは実際には子要素によって「サポート」されているため、そのスクロールハイトはコンテンツ領域を「拡張」した後の実際の高さになります。 親要素のスクロールハイト = 子要素のオフセット高さ + 子要素 上下のマージン + 独自の上下のパディング

6、スクロールトップ

読み取り/書き込みプロパティ。要素のコンテンツが垂直方向にスクロールするピクセル数を取得または設定できます。これは、設定をサポートできるこれまでに見つかった最初のプロパティです。

(1) 初期状態ではコンテンツは縦スクロールされておらず、値は0です。

(2) コンテンツが垂直方向に下までスクロールする場合、実際のコンテンツ領域の高さはscrollHeightなので、表示可能な高さはclientHeightとなり、このときのscrollTopの値がscrollHeight-clientHeightとなります

したがって、次のように結論付けることができます: 0 <=scrollTop<=scrollHeight - clientHeight

七、実戦

上記の属性と情報を学習した後、 JD アプレットのスクロールビュー コンポーネント機能を模倣して、H5 バージョンのスライディング コンポーネントの共通機能であるリストのプルダウン更新とプルアップ ロードを実装します。この機能を実現するには、大きく次の 3 つの核心点に分けることができます。

(1) スクロール可能: 移動できないシェルとスライド可能なコンテンツ領域が必要です。

(2) ジェスチャ認識: モバイル端末のタッチ属性を通じて、タッチエンドとタッチスタートの指の位置を比較して、簡単にジェスチャ認識を実行できます。

(3) イベントトリガー: スライド位置の臨界条件に応じて、リフレッシュイベントとローディングイベントをトリガーするかどうかが判断されます。

実装コードの一部は次のとおりです。

// scroller.js

export default class Scroller {
	constructor(el, option) {
		this._el = el
		this._parent = el.parentNode
		this._option = option
		this._pos = 0
		this._handleScrollStart = this.handleScrollStart.bind(this)
		this._handleScroll = this.handleScroll.bind(this)
		this.init()
	}
	init() {
		this._el.addEventListener('touchstart', this._handleScrollStart)
		this._el.addEventListener('touchend', this._handleScroll)
		if(this._option.auto) {
			this.handleRefresh()
		}
	}
	destroy() {
		this._el.removeEventListener('touchstart', this._handleScrollStart)
		this._el.removeEventListener('touchend', this._handleScroll)
	}
	handleScrollStart(e) {
		const touch = e.targetTouches[0] || e.changedTouches[0]
		this._pos = touch.clientY
	}
	handleScroll(e) {
		const touch = e.targetTouches[0] || e.changedTouches[0]
		const delta = touch.clientY - this._pos
		if(delta >= this._option.threhold) {
			// 手势向下,且下行滚动距离超过判定阈值
			if(this._parent.scrollTop == 0) {
				this.handleRefresh(e)
			}
		} else if(this._parent.scrollHeight > this._parent.clientHeight && delta <= -this._option.threhold){
			// 内容可滚动,且上行滚动距离超过判定阈值
			if(this._parent.scrollTop > this._parent.scrollHeight - this._parent.clientHeight - this._option.threhold){
				this.handleLoad(e)
			}
		}
	}
	handleRefresh(...params) {
		this._option.onRefresh && this._option.onRefresh.apply(this, params)
	}
	handleLoad(...params) {
		this._option.onRefresh && this._option.onLoad.apply(this, params)
	}
}

デモを呼び出します。

var inner = document.getElementsById('inner')
var list = []
var pageIndex = 1
function getMockData() {
	const newData = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => i+(pageIndex-1)*10)
	setTimeout(() => {
	    if(pageIndex==1) { list = newData } else if(pageIndex < 5) { list.push(...newData) } else { alert('没有更多内容了') }
	    pageIndex++
	    inner.innerHTML = list.map(i => `<div class="inner-item">${i}</div>`).join('')
	}, 300)
}
function onRefresh() {
	pageIndex = 1
	getMockData()
}
new Scroller(inner, {
	threhold: 20,
	onRefresh: onRefresh,
	onLoad: getMockData,
	auto: true
})

自分で試してみても効果は悪くありません~~~ 議論や交換のためのメッセージエリアへようこそ。

著者: Jingdong Retail Chen Zhen

出典: JD Cloud 開発者コミュニティ

 

RustDesk 1.2: Flutterを使用してデスクトップバージョンを書き換え、Waylandの GPT-4モデルアーキテクチャリーク疑惑をサポート: 混合エキスパートモデルを使用して1.8兆個のパラメータが含まれている(MoE) マスク氏は、 V23の適合に成功したWSL CentOSプロジェクトの主張に基づい てxAI会社の設立を発表した すべてに公開」Rust 1.71.0 安定版リリースReact Angular.js の瞬間はありますか? Microsoft、CalibriMicrosoft に代わる新しいデフォルト フォント Aptos を発表: Windows 11 でRust IntelliJ IDEA 2023.1.4 リリースを使用する取り組みを強化
{{名前}}
{{名前}}

おすすめ

転載: my.oschina.net/u/4090830/blog/10089164