モバイルでのスワイパーの使用方法
最近、モバイル開発でEle.meのvueフロントエンドコンポーネントライブラリを使用しています。単純にコンポーネントで使用したくないので、実装原理をより深く理解したいと考えています。この記事では主にモバイルエフェクトに関するSwiperの関連情報を詳しく紹介しますので、興味のある方は参考にしていただければ幸いです。
コードはこちらです: poke me
1. 説明
親コンテナoverflow:hidden;,子ページtransform:translateX(-100%);width:100%;
2. コア分析
2.1 ページの初期化
すべてのページがモバイル画面の 1 画面幅の左側に配置されているため、初期状態ではページ内にサブページが表示されません。最初のステップは、表示されるサブページを設定する必要があります。デフォルトでは、defaultIndex: 0
function reInitPages() { // 得出页面是否能够被滑动 // 1. 子页面只有一个 // 2. 用户手动设置不能滑动 noDragWhenSingle = true noDrag = children.length === 1 && noDragWhenSingle; var aPages = []; var intDefaultIndex = Math.floor(defaultIndex); var defaultIndex = (intDefaultIndex >= 0 && intDefaultIndex < children.length) ? intDefaultIndex : 0; // 得到当前被激活的子页面索引 index = defaultIndex; children.forEach(function(child, index) { aPages.push(child); // 所有页面移除激活class child.classList.remove('is-active'); if (index === defaultIndex) { // 给激活的子页面加上激活class child.classList.add('is-active'); } }); pages = aPages; }
2.2 コンテナのスライディング スタート (onTouchStart)
android 携帯電話の以前のバージョンでは、event.preventDefault( )が始まります 一定のパフォーマンス向上効果があり、スライドの引っかかりが少なくなります。
事前作業:
ユーザーがPrevent:trueを設定した場合、スライド時のデフォルトの動作を防止します
ユーザーがstopPropagation:trueを設定した場合、スライド時にイベントが上方向に伝播するのを防止します
アニメーションはまだ終了していません。スライドを防止します
ドラッグを設定します: true、スライド開始します
ユーザーのスクロールを false に設定します
ドラッグ開始:
グローバル オブジェクトを使用して、次の情報を記録します。
dragState = { startTime // 开始时间 startLeft // 开始的X坐标 startTop // 开始的Y坐标(相对于整个页面viewport pageY) startTopAbsolute // 绝对Y坐标(相对于文档顶部 clientY) pageWidth // 一个页面宽度 pageHeight // 一个页面的高度 prevPage // 上一个页面 dragPage // 当前页面 nextPage // 下一个页面 };
2.3 コンテナスライド(onTouchMove)
グローバルdragStateを適用し、新しい情報を記録します
dragState = { currentLeft // 开始的X坐标 currentTop // 开始的Y坐标(相对于整个页面viewport pageY) currentTopAbsolute // 绝对Y坐标(相对于文档顶部 clientY) };
次に、スタートとスライドの情報を通じて何かを計算できます:
の水平変位スライド (offsetLeft = currentLeft - startLeft)
スライドの垂直変位 (offsetTop = currentTopAbsolute - startTopAbsolute)
これはユーザーの自然なスクロールですか? ここでの自然なスクロールとは、ユーザーがスワイパーをスライドさせたくないことを意味しますが、ページをスライドさせたいです
// 条件 // distanceX = Math.abs(offsetLeft); // distanceY = Math.abs(offsetTop); distanceX < 5 || ( distanceY >= 5 && distanceY >= 1.73 * distanceX )
左に移動するか右に移動するかを決定します(offsetLeft < 0の場合は左に移動し、それ以外の場合は右に移動します)
変位をリセットします
// 如果存在上一个页面并且是左移 if (dragState.prevPage && towards === 'prev') { // 重置上一个页面的水平位移为 offsetLeft - dragState.pageWidth // 由于 offsetLeft 一直在变化,并且 >0 // 那么也就是说 offsetLeft - dragState.pageWidth 的值一直在变大,但是仍未负数 // 这就是为什么当连续属性存在的时候左滑会看到上一个页面会跟着滑动的原因 // 这里的 translate 方法其实很简单,在滑动的时候去除了动画效果`transition`,单纯改变位移 // 而在滑动结束的时候,加上`transition`,使得滑动到最后释放的过渡更加自然 translate(dragState.prevPage, offsetLeft - dragState.pageWidth); } // 当前页面跟着滑动 translate(dragState.dragPage, offsetLeft); // 后一个页面同理 if (dragState.nextPage && towards === 'next') { translate(dragState.nextPage, offsetLeft + dragState.pageWidth); }
2.4 スライド終了(onTouchEnd)
Pre -work:
スライド中に、ユーザーの自然なスクロールであるかどうかをリアルタイムで判断できます。ユーザーの自然なスクロールである場合、swiperのスライド情報はカウントされないため、いくつかのクリア操作が必要です。もちろん、 userScrolling: false の場合は、サブページをスライドして doOnTouchEnd メソッド
dragging = false;
dragState = {};
方向を決定する
// 时间小于300ms,click事件延迟300ms触发
// 水平位移和垂直位移栋小于5像素
if (dragDuration < 300) {
var fireTap = Math.abs(offsetLeft) < 5 && Math.abs(offsetTop < 5);
if (isNaN(offsetLeft) || isNaN(offsetTop)) {
fireTap = true;
}
if (fireTap) {
console.log('tap');
}
}
アニメーションを実行する
// 如果事件间隔小于300ms但是滑出屏幕,直接返回
if (dragDuration < 300 && dragState.currentLeft === undefined) return;
// 如果事件间隔小于300ms 或者 滑动位移超过屏幕宽度 1/2, 根据位移判断方向
if (dragDuration < 300 || Math.abs(offsetLeft) > pageWidth / 2) {
towards = offsetLeft < 0 ? 'next' : 'prev';
}
// 如果非连续,当处于第一页,不会出现上一页,当处于最后一页,不会出现下一页
if (!continuous) {
if ((index === 0 && towards === 'prev')
|| (index === pageCount - 1 && towards === 'next')) {
towards = null;
}
}
// 子页面数量小于2时,不执行滑动动画
if (children.length < 2) {
towards = null;
}
// 当没有options的时候,为自然滑动,也就是定时器滑动 function doAnimate(towards, options) { if (children.length === 0) return; if (!options && children.length < 2) return; var prevPage, nextPage, currentPage, pageWidth, offsetLeft; var pageCount = pages.length; // 定时器滑动 if (!options) { pageWidth = element.clientWidth; currentPage = pages[index]; prevPage = pages[index - 1]; nextPage = pages[index + 1]; if (continuous && pages.length > 1) { if (!prevPage) { prevPage = pages[pages.length - 1]; } if (!nextPage) { nextPage = pages[0]; } } // 计算上一页与下一页之后 // 重置位移 // 参看doOnTouchMove // 其实这里的options 传与不传也就是获取上一页信息与下一页信息 if (prevPage) { prevPage.style.display = 'block'; translate(prevPage, -pageWidth); } if (nextPage) { nextPage.style.display = 'block'; translate(nextPage, pageWidth); } } else { prevPage = options.prevPage; currentPage = options.currentPage; nextPage = options.nextPage; pageWidth = options.pageWidth; offsetLeft = options.offsetLeft; } var newIndex; var oldPage = children[index]; // 得到滑动之后的新的索引 if (towards === 'prev') { if (index > 0) { newIndex = index - 1; } if (continuous && index === 0) { newIndex = pageCount - 1; } } else if (towards === 'next') { if (index < pageCount - 1) { newIndex = index + 1; } if (continuous && index === pageCount - 1) { newIndex = 0; } } // 动画完成之后的回调 var callback = function() { // 得到滑动之后的激活页面,添加激活class // 重新赋值索引 if (newIndex !== undefined) { var newPage = children[newIndex]; oldPage.classList.remove('is-active'); newPage.classList.add('is-active'); index = newIndex } if (isDone) { end(); } if (prevPage) { prevPage.style.display = ''; } if (nextPage) { nextPage.style.display = ''; } } setTimeout(function() { // 向后滑动 if (towards === 'next') { isDone = true; before(currentPage); // 当前页执行动画,完成后执行callback translate(currentPage, -pageWidth, speed, callback); if (nextPage) { // 下一面移动视野中 translate(nextPage, 0, speed) } } else if (towards === 'prev') { isDone = true; before(currentPage); translate(currentPage, pageWidth, speed, callback); if (prevPage) { translate(prevPage, 0, speed); } } else { // 如果既不是左滑也不是右滑 isDone = true; // 当前页面依旧处于视野中 // 上一页和下一页滑出 translate(currentPage, 0, speed, callback); if (typeof offsetLeft !== 'undefined') { if (prevPage && offsetLeft > 0) { translate(prevPage, pageWidth * -1, speed); } if (nextPage && offsetLeft < 0) { translate(nextPage, pageWidth, speed); } } else { if (prevPage) { translate(prevPage, pageWidth * -1, speed); } if (nextPage) { translate(nextPage, pageWidth, speed); } } } }, 10); }
全体的に、実装原理は比較的単純です。スライドは初期位置を記録し、前のページと次のページに表示されるページを計算します。前のページと次のページの移動。スライドが終了すると、移動結果に応じて対応するアニメーションが実行されます。
1つの詳細は、スライド中のトランジションによって前のページと次のページが不自然に移動するのを防ぐために、トランジション
WeChat アプレットタブとスワイパーの組み合わせ効果の実現
以上がモバイルでのスワイパーの使用方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









appdata フォルダーを D ドライブに移動できますか? コンピューターの使用が普及するにつれて、ますます多くのユーザーの個人データやアプリケーションがコンピューターに保存されています。 Windows オペレーティング システムには、ユーザーのアプリケーション データを保存するために使用される appdata フォルダーと呼ばれる特定のフォルダーがあります。多くのユーザーは、データ管理とセキュリティを考慮して、このフォルダーを D ドライブまたは他のディスクに移動できるかどうか疑問に思っています。この記事では、この問題について説明し、いくつかの解決策を提供します。まず、させてください

7月23日のニュースによると、ブロガーのDigital Chat Stationは、Xiaomi 15 Proのバッテリー容量が6000mAhに増加し、90Wの有線フラッシュ充電をサポートしているというニュースを伝えました。これは、Xiaomiのデジタルシリーズで最大のバッテリーを搭載したProモデルになります。 Digital Chat Stationは以前、Xiaomi 15Proのバッテリーは超高エネルギー密度を持ち、シリコン含有量が競合製品よりもはるかに高いことを明らかにしました。 2023 年にシリコンベースの電池が大規模にテストされた後、第 2 世代のシリコン負極電池が業界の将来の発展方向として特定されており、今年は直接的な競争のピークを迎えます。 1. シリコンの理論グラム容量は 4200mAh/g に達することがあり、これはグラファイトのグラム容量の 10 倍以上です (グラファイトの理論グラム容量は 372mAh/g)。負極の場合、リチウムイオン挿入量が最大に達したときの容量が理論上のグラム容量であり、同じ重量下での容量を意味します。

JSP コメントの分類と使用状況の分析 JSP コメントは 2 つのタイプに分類されます。 単一行コメント: で終わる、単一行のコードのみコメントできます。複数行のコメント: /* で始まり */ で終わると、複数行のコードにコメントを付けることができます。単一行のコメントの例 複数行のコメントの例/**これは複数行のコメントです*コードの複数行にコメントできます*/JSP コメントの使用法 JSP コードを読みやすくするために JSP コメントを使用できます。

Microsoft は、最新の Windows 11 バージョンで PhoneLink の名前を MobileDevice に変更しました。この変更により、ユーザーはプロンプトを通じてモバイル デバイスへのコンピュータ アクセスを制御できるようになります。この記事では、モバイル デバイスからのアクセスを許可または拒否するコンピューターの設定を管理する方法について説明します。この機能を使用すると、モバイル デバイスを設定してコンピュータに接続し、テキスト メッセージの送受信、モバイル アプリケーションの制御、連絡先の表示、電話の発信、ギャラリーの表示などを行うことができます。携帯電話を PC に接続することは良い考えですか?携帯電話を Windows PC に接続すると、機能やメディアを簡単に転送できる便利なオプションです。これは、モバイルデバイスが利用できないときにコンピュータを使用する必要がある人にとって便利です

WPS は一般的に使用されるオフィス ソフトウェア スイートであり、WPS テーブル関数はデータ処理と計算に広く使用されています。 WPS テーブルには、2 つの日付間の時差を計算するために使用される、DATEDIF 関数という非常に便利な関数があります。 DATEDIF 関数は英語の DateDifference の略語で、構文は次のとおりです: DATEDIF(start_date,end_date,unit) ここで、start_date は開始日を表します。

C 言語で exit 関数を使用する方法には、具体的なコード例が必要です。C 言語では、プログラムの実行をプログラムの初期段階で終了したり、特定の条件下でプログラムを終了したりする必要があることがよくあります。 C 言語には、この関数を実装するための exit() 関数が用意されています。この記事では、exit() 関数の使用法を紹介し、対応するコード例を示します。 exit() 関数は C 言語の標準ライブラリ関数であり、ヘッダー ファイルに含まれています。その機能はプログラムの実行を終了することであり、整数を取ることができます。

Python 関数入門: abs 関数の使い方と例 1. abs 関数の使い方の概要 Python では、abs 関数は、指定された値の絶対値を計算するために使用される組み込み関数です。数値引数を受け入れ、その数値の絶対値を返すことができます。 abs 関数の基本構文は次のとおりです。 abs(x) ここで、x は絶対値を計算する数値パラメータであり、整数または浮動小数点数を指定できます。 2. abs 関数の例 以下に、いくつかの具体的な例を通して abs 関数の使用法を示します。 例 1: 計算

Python 関数の紹介: isinstance 関数の使用法と例 Python は、プログラミングをより便利かつ効率的にするための多くの組み込み関数を提供する強力なプログラミング言語です。非常に便利な組み込み関数の 1 つは isinstance() 関数です。この記事では、isinstance関数の使い方と例を紹介し、具体的なコード例を紹介します。 isinstance() 関数は、オブジェクトが指定されたクラスまたは型のインスタンスであるかどうかを判断するために使用されます。この関数の構文は次のとおりです
