ES6に全画面スクロールプラグインを実装する手順を詳しく解説
今回はES6で全画面スクロールプラグインを実装する手順について詳しく解説します ES6プラグインを使用して全画面スクロールを実装する際の注意点についてご紹介します。ケース、見てみましょう。
1) 前に述べたように
有名な fullPage など、すでに多くの全画面スクロール プラグインが存在するのに、なぜ独自のホイールを発明する必要があるのでしょうか?
既存のホイールには次の問題があります:
まず第一に、最大の問題は、最も人気のあるプラグインのいくつかが jQuery に依存していることです。つまり、React または Vue を使用するプロジェクトでこれらのプラグインを使用するのは非常に面倒です。 : 全画面スクロール機能だけが必要ですが、肉屋のナイフで鶏を殺すような感じの jQuery も導入する必要があります
次に、既存の全画面スクロール プラグインの多くは非常に複雑です。ここ数年はそうではなかった機能の豊富さは利点ですが、現在(2018-5)では欠点として捉えられています。フロントエンド開発は大きな変化を遂げており、重要な変化の 1 つです。 ES6 はモジュール型開発をネイティブにサポートしているということです。モジュール型開発の最大の特徴は、1 つのモジュールが最適であることです。この観点から見ると、大規模で包括的なプラグインは、要件に違反します。モジュール開発の原則。
対照的に、ネイティブ言語を使用してホイールを作成すると、次の利点があります:
ネイティブ言語で書かれたプラグインは、依存するプラグイン (現在は依存するプラグイン) の使用シナリオの影響を受けません。 jQuery は単一ページのアプリケーションの開発にはあまり適していないため、より柔軟に使用できます
モジュール開発では、ネイティブ言語を使用して開発されたプラグインは 1 つの機能のみに集中できるため、コードの量が少なくなります。
最後に、JS/CSS/HTML の開発とブラウザの継続的な更新により、ネイティブ言語でプラグインを作成する開発コストはますます低くなっています。ない?
2) 実装原理とコード構造
2.1 実装原理
実装原理については、以下の図を参照してください: コンテナーとコンテナー内のページは、現在の視覚領域の高さを取得し、コンテナの親要素の overflow 属性値を hidden に設定し、コンテナの上部の値を変更することで全画面スクロール効果を実現します。
2.2 コードアーキテクチャ
コードを書くという考え方は、クラスを通じて全画面スクロールクラスを定義し、 new PureFullPage().init()
を通してそれを使用することです。
/** * 全屏滚动类 */ class PureFullPage { // 构造函数 constructor() {} // 原型方法 methods() {} // 初始化函数 init() {} }
3) HTML 構造
上記の実装原則を考慮すると、HTML の構造には次のような特定の要件があります: ページ コンテナーは #pureFullPageContainer で、すべてのページはその直接の子要素です。便宜上、ここでは本文を直接の親要素として取り上げます。
<body> <p id="pureFullPageContainer"> <p class="page"></p> <p class="page"></p> <p class="page"></p> </p> </body>
4) CSS 設定
まず、コンテナーとコンテナー内のページは、各スイッチの完全なページを表示する準備をするために、現在の視覚領域の高さを取得します。コンテナー要素 (ここではボディ) のオーバーフロー属性値が hidden に設定されており、これにより、一度に 1 ページのみが表示され、他のページは非表示になります。
上記の設定後、コンテナの上部の値に対して、ビジュアル領域の高さの距離を変更するたびに、ページ間の切り替えを実現するコードの一部は次のとおりです:
body { /* body 为容器直接的父元素 */ overflow: hidden; } #pureFullPage { /* 只有当 position 的值不是 static 时,top 值才有效 */ position: relative; /* 设置初始值 */ top: 0; } .page { /* 此处不能为 100vh,后面详述 */ /* 其父元素,也就是 #pureFullPage 的高度,通过 js 动态设置*/ height: 100%; }
注意:
top は位置属性値が静的でない場合にのみ有効であるため、コンテナの位置属性値は相対値に設定する必要があります。
ページの高さは現在のビジュアル領域の高さに設定する必要がありますが、設定することはできません。 Safari モバイル ブラウザはアドレス バーを 100vh の計算にカウントしますが、アドレス バーの下の領域は「可視領域」としてカウントされるべきではありません。結局のところ、それは実際には「不可視」領域です。これにより、100vh に対応するピクセル値が document.documentElement.clientHeight で取得されるピクセル値よりも大きくなります。このように、上部の値を切り替える場合、全画面切り替えではありません。実際には、この場合のスイッチの高さはページの高さよりも小さくなります。
解决 safari 手机浏览器可视区高度问题:既然通过 js 获取的 document.documentElement.clientHeight 值是符合预期的可视区高度(不包括顶部地址栏和底部工具栏),那就 将该值通过 js 设置为容器的高度,同时,容器内的页面高度设置为 100% ,这样就可以保证容器及页面的高度和切换 top 值相同了,也就保证了全屏切换。
// 伪代码 '#pureFullPage'.style.height = document.documentElement.clientHeight + 'px';
5)监控滚动/滑动事件
这里的滚动/滑动事件包括鼠标滚动、触摸板滑动以及手机屏幕上下滑动。
5.1 PC 端
PC 端主要解决的问题是获取鼠标滚动或触摸板滑动方向,触摸板上下滑动和鼠标滚动绑定的是同一个事件:
firefox 是 DOMMouseScroll 事件,对应的滚轮信息(向前滚还是向后滚)存储在 detail 属性中,向前滚,这个属性值是 3 的倍数,反之,是 -3 的倍数;
firefox 之外的其他浏览器是 mousewheel 事件,对应的滚轮信息存储在 wheelDelta 属性中,向前滚,这个属性值是 -120 的倍数,反之, 120 的倍数。
macOS 如此,windows 相反?
所以,可以通过 detail 或 wheelDelta 的值判断鼠标的滚动方向,进而控制页面是向上还是向下滚动。在这里我们只关心正负,不关心具体值的大小,为了便于使用,下面基于这两个事件封装了一个函数:如果鼠标往前滚动,返回负数,反之,返回正数,代码如下:
// 鼠标滚轮事件 getWheelDelta(event) { if (event.wheelDelta) { return event.wheelDelta; } else { // 兼容火狐 return -event.detail; } },
有了滚动事件,就可以据此编写页面向上或者向下滚动的回调函数了,如下:
// 鼠标滚动逻辑(全屏滚动关键逻辑) scrollMouse(event) { let delta = utils.getWheelDelta(event); // delta < 0,鼠标往前滚动,页面向下滚动 if (delta < 0) { this.goDown(); } else { this.goUp(); } }
goDown 、 goUp 是页面滚动的逻辑代码,需要特别说明的是必须 判断滚动边界,保证容器中显示的始终是页面内容 :
上边界容易确定,为 1 个页面(也即可视区)的高度,即如果容器当前的上外边框距离整个页面顶部的距离(这里此值正是容器的 offsetTop 值的绝对值,因为它父元素的 offsetTop 值都是 0 )大于等于当前可视区高度时,才允许向上滚动,不然,就证明上面已经没有页面了,不允许继续向上滚动;
下边界为 n - 2 (n 表示全屏滚动的页面数) 个可视区的高度,当容器的 offsetTop 值的绝对值小于等于 n - 2 个可视区的高度时,表示还可以向下滚动一个页面。
具体代码如下:
goUp() { // 只有页面顶部还有页面时页面向上滚动 if (-this.container.offsetTop >= this.viewHeight) { // 重新指定当前页面距视图顶部的距离 currentPosition,实现全屏滚动, // currentPosition 为负值,越大表示超出顶部部分越少 this.currentPosition = this.currentPosition + this.viewHeight; this.turnPage(this.currentPosition); } } goDown() { // 只有页面底部还有页面时页面向下滚动 if (-this.container.offsetTop <= this.viewHeight * (this.pagesNum - 2)) { // 重新指定当前页面距视图顶部的距离 currentPosition,实现全屏滚动, // currentPosition 为负值,越小表示超出顶部部分越多 this.currentPosition = this.currentPosition - this.viewHeight; this.turnPage(this.currentPosition); } }
最后添加滚动事件:
// 鼠标滚轮监听,火狐鼠标滚动事件不同其他 if (navigator.userAgent.toLowerCase().indexOf('firefox') === -1) { document.addEventListener('mousewheel', scrollMouse); } else { document.addEventListener('DOMMouseScroll', scrollMouse); }
5.2 移动端
移动端需要判断是向上还是向下滑动,可以结合 touchstart (手指开始接触屏幕时触发) 和 touchend (手指离开屏幕时触发) 两个事件实现判断:分别获取两个事件开始触发时的 pageY 值,如果触摸结束时的 pageY 大于触摸开始时的 pageY ,表示手指向下滑动,对应页面向上滚动,反之亦然。
此处我们需要触摸事件跟踪触摸的属性:
touches :当前跟踪的触摸操作的 Touch 对象的数组,用于获取触摸开始时的 pageY 值;
changeTouches :自上次触摸以来发生了改变的 Touch 对象的数组,用于获取触摸触摸结束时的 pageY 值。
相关代码如下:
// 手指接触屏幕 document.addEventListener('touchstart', event => { this.startY = event.touches[0].pageY; }); //手指离开屏幕 document.addEventListener('touchend', event => { let endY = event.changedTouches[0].pageY; if (endY - this.startY < 0) { // 手指向上滑动,对应页面向下滚动 this.goDown(); } else { // 手指向下滑动,对应页面向上滚动 this.goUp(); } });
为了避免下拉刷新,可以阻止 touchmove 事件的默认行为:
// 阻止 touchmove 下拉刷新 document.addEventListener('touchmove', event => { event.preventDefault(); });
6)PC 端滚动事件性能优化
6.1 防抖函数和截流函数介绍
优化主要从两方便入手:
更改页面大小时,通过防抖动(debounce)函数限制 resize 事件触发频率;
滚动/滑动事件触发时,通过截流(throttle)函数限制滚动/滑动事件触发频率。
既然都是限制触发频率(都通过定时器实现),那这两者有什么区别?
首先,防抖动函数工作时,如果在指定的延迟时间内,某个事件连续触发,那么绑定在这个事件上的回调函数永远不会触发,只有在延迟时间内,这个事件没再触发,对应的回调函数才会执行。防抖动函数非常适合改变窗口大小这一事件,这也符合 拖动到位以后再触发事件,如果一直拖个不停,始终不触发事件 这一直觉。
而截流函数是在延迟时间内,绑定到事件上的回调函数能且只能触发一次,这和截流函数不同,即便是在延迟时间内连续触发事件,也不会阻止在延迟时间内有一个回调函数执行。并且截流函数允许我们指定回调函数是在延迟时间开始时还是结束时执行。
鉴于截流函数的上述两个特性,尤其适合优化滚动/滑动事件:
可以限制频率;
不会因为滚动/滑动事件太灵敏(在延迟时间内不断触发)导致注册在事件上的回调函数无法执行;
可以设置在延迟时间开始时触发回调函数,从而避免用户感到操作之后的短暂延时。
这里不介绍防抖动函数和截流函数的实现原理,感兴趣的可以看 Throttling and Debouncing in JavaScript ,下面是实现的代码:
// 防抖动函数,method 回调函数,context 上下文,event 传入的时间,delay 延迟函数 debounce(method, context, event, delay) { clearTimeout(method.tId); method.tId = setTimeout(() => { method.call(context, event); }, delay); }, // 截流函数,method 回调函数,context 上下文,delay 延迟函数, // immediate 传入 true 表示在 delay 开始时执行回调函数 throttle(method, context, delay, immediate) { return function() { const args = arguments; const later = () => { method.tID = null; if (!immediate) { method.apply(context, args); } }; const callNow = immediate && !method.tID; clearTimeout(method.tID); method.tID = setTimeout(later, delay); if (callNow) { method.apply(context, args); } }; },
《JavaScript 高级程序设计 - 第三版》 22.33.3 节中介绍的 throttle 函数和此处定义的不同,高程中定义的 throttle 函数对应此处的 debounce 函数,但网上大多数文章都和高程中的不同,比如 lodash 中定义的 debounce 。
6.2 改造 PC 端滚动事件
通过上述说明,我们已经知道截流函数可以通过限定滚动事件触发频率提升性能,同时,设置在 延迟时间开始阶段立即调用滚动事件的回调函数 并不会牺牲用户体验。
截流函数上文已经定义好,使用起来就很简单了:
// 设置截流函数 let handleMouseWheel = utils.throttle(this.scrollMouse, this, this.DELAY, true); // 鼠标滚轮监听,火狐鼠标滚动事件不同其他 if (navigator.userAgent.toLowerCase().indexOf('firefox') === -1) { document.addEventListener('mousewheel', handleMouseWheel); } else { document.addEventListener('DOMMouseScroll', handleMouseWheel); }
上面这部分代码是写在 class 的 init 方法中,所以截流函数的上下文(context)传入的是 this ,表示当前 class 实例。
7)其他
7.1 导航按钮
为了简化 html 结构,导航按钮通过 js 创建。这里的难点在于 如何实现点击不同按钮实现对应页面的跳转并更新对应按钮的样式 。
解决的思路是:
页面跳转:页面个数和导航按钮的个数一致,所以点击第 i 个按钮也就是跳转到第 i 个页面,而第 i 个页面对应的容器 top 值恰好是 -(i * this.viewHeight)
更改样式:更改样式即先删除所有按钮的选中样式,然后给当前点击的按钮添加选中样式。
// 创建右侧点式导航 createNav() { const nav = document.createElement('p'); nav.className = 'nav'; this.container.appendChild(nav); // 有几页,显示几个点 for (let i = 0; i < this.pagesNum; i++) { nav.innerHTML += '<p class="nav-dot"><span></span></p>'; } const navDots = document.querySelectorAll('.nav-dot'); this.navDots = Array.prototype.slice.call(navDots); // 添加初始样式 this.navDots[0].classList.add('active'); // 添加点式导航点击事件 this.navDots.forEach((el, i) => { el.addEventListener('click', event => { // 页面跳转 this.currentPosition = -(i * this.viewHeight); this.turnPage(this.currentPosition); // 更改样式 this.navDots.forEach(el => { utils.deleteClassName(el, 'active'); }); event.target.classList.add('active'); }); }); }
7.2 自定义参数
得当的自定义参数可以增加插件的灵活性。
参数通过构造函数传入,并通过 Object.assign() 进行参数合并:
constructor(options) { // 默认配置 const defaultOptions = { isShowNav: true, delay: 150, definePages: () => {}, }; // 合并自定义配置 this.options = Object.assign(defaultOptions, options); }
7.3 窗口尺寸改变时更新数据
浏览器窗口尺寸改变的时候,需要重新获取可视区、页面元素高度,并重新确定容器当前的 top 值。
同时,为了避免不必要的性能开支,这里使用了防抖动函数。
// window resize 时重新获取位置 getNewPosition() { this.viewHeight = document.documentElement.clientHeight; this.container.style.height = this.viewHeight + 'px'; let activeNavIndex; this.navDots.forEach((e, i) => { if (e.classList.contains('active')) { activeNavIndex = i; } }); this.currentPosition = -(activeNavIndex * this.viewHeight); this.turnPage(this.currentPosition); } handleWindowResize(event) { // 设置防抖动函数 utils.debounce(this.getNewPosition, this, event, this.DELAY); } // 窗口尺寸变化时重置位置 window.addEventListener('resize', this.handleWindowResize.bind(this));
7.4 兼容性
这里的兼容性主要指两个方面:一是不同浏览器对同一行为定义了不同 API,比如上文提到的获取鼠标滚动信息的 API Firefox 和其他浏览器不一样;第二点就是 ES6 新语法、新 API 的兼容处理。
对于 class、箭头函数这类新语法的转换,通过 babel 就可完成,鉴于本插件代码量很小,都处于可控的状态,并没有引入 babel 提供的 polyfill 方案,因为新 API 只有 Object.assign() 需要做兼容处理,单独写个 polyfill 就好,如下:
// polyfill Object.assign polyfill() { if (typeof Object.assign != 'function') { Object.defineProperty(Object, 'assign', { value: function assign(target, varArgs) { if (target == null) { throw new TypeError('Cannot convert undefined or null to object'); } let to = Object(target); for (let index = 1; index < arguments.length; index++) { let nextSource = arguments[index]; if (nextSource != null) { for (let nextKey in nextSource) { if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; } } } } return to; }, writable: true, configurable: true, }); } },
引用自: MDN-Object.assign()
因为本插件只兼容到 IE10,所以不打算对事件做兼容处理,毕竟IE9 都支持 addEventListener 了。
7.5 通过惰性载入进一步优化性能
在 5.1 中写的 getWheelDelta 函数每次执行都需要检测是否支持 event.wheelDelta ,实际上,浏览器只需在第一次加载时检测,如果支持,接下来都会支持,再做检测是没必要的。
并且这个检测在页面的生命周期中会执行很多次,这种情况下可以通过 惰性载入 技巧进行优化,如下:
getWheelDelta(event) { if (event.wheelDelta) { // 第一次调用之后惰性载入,无需再做检测 this.getWheelDelta = event => event.wheelDelta; // 第一次调用使用 return event.wheelDelta; } else { // 兼容火狐 this.getWheelDelta = event => -event.detail; return -event.detail; } },
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
以上がES6に全画面スクロールプラグインを実装する手順を詳しく解説の詳細内容です。詳細については、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)

ホットトピック











iPhone のデフォルトの地図は、Apple 独自の地理位置情報プロバイダーである Maps です。マップは改善されていますが、米国外ではうまく機能しません。 Googleマップと比べて何も提供するものはありません。この記事では、Google マップを iPhone のデフォルトの地図として使用するための実行可能な手順について説明します。 Google マップを iPhone のデフォルトの地図にする方法 Google マップを携帯電話のデフォルトの地図アプリとして設定するのは、思っているよりも簡単です。以下の手順に従ってください – 前提条件 – 携帯電話に Gmail がインストールされている必要があります。ステップ 1 – AppStore を開きます。ステップ 2 – 「Gmail」を検索します。ステップ 3 – Gmail アプリの横にある をクリックします

AppleIDを使用してiTunesStoreにログインすると、「このAppleIDはiTunesStoreで使用されていません」というエラーが画面に表示される場合があります。心配するようなエラー メッセージはありません。これらのソリューション セットに従って問題を修正できます。解決策 1 – 配送先住所を変更する iTunes Store にこのプロンプトが表示される主な理由は、AppleID プロファイルに正しい住所がないことです。ステップ 1 – まず、iPhone で iPhone 設定を開きます。ステップ 2 – AppleID は他のすべての設定の最上位にある必要があります。それで、開けてください。ステップ 3 – そこに到達したら、「支払いと配送」オプションを開きます。ステップ 4 – Face ID を使用してアクセスを確認します。ステップ

WeChat は、より良いユーザー エクスペリエンスを提供するために新しいバージョンを継続的にリリースしている中国のソーシャル メディア プラットフォームの 1 つです。 WeChat を最新バージョンにアップグレードすることは、家族や同僚と連絡を取り合ったり、友人と連絡を取り合ったり、最新の動向を把握したりするために非常に重要です。 1. 最新バージョンの機能と改善点を理解する WeChat をアップグレードする前に、最新バージョンの機能と改善点を理解することが非常に重要です。パフォーマンスの向上やバグ修正については、WeChat 公式 Web サイトまたはアプリ ストアのアップデート ノートを確認することで、新しいバージョンによってもたらされるさまざまな新機能について知ることができます。 2. 現在の WeChat バージョンを確認する WeChat をアップグレードする前に、携帯電話に現在インストールされている WeChat バージョンを確認する必要があります。クリックして WeChat アプリケーション「Me」を開き、メニュー「About」を選択すると、現在の WeChat バージョン番号が表示されます。 3. アプリを開きます

PyCharm は、開発者がより効率的にコードを記述できるようにする豊富な機能とツールを提供する、強力で人気のある Python 統合開発環境 (IDE) です。 PyCharm のプラグイン機構は、機能を拡張するための強力なツールであり、さまざまなプラグインをインストールすることで、PyCharm にさまざまな機能やカスタマイズ機能を追加できます。したがって、PyCharm の初心者にとって、プラグインのインストールを理解し、習熟することが重要です。この記事では、PyCharm プラグインの完全なインストールについて詳しく説明します。

Safari でズーム レベルを制御できない場合、作業が困難になることがあります。したがって、Safari がズームアウトしているように見える場合は、それが問題である可能性があります。 Safari でのこの小さなズームの問題を解決する方法をいくつか紹介します。 1. カーソル拡大率:Safari メニューバーの「表示」>「カーソル拡大率」を選択します。これにより、カーソルが画面上でより見やすくなり、制御が容易になります。 2. マウスを移動します。これは簡単に聞こえるかもしれませんが、マウスを画面上の別の場所に移動するだけで、マウスが自動的に通常のサイズに戻ることがあります。 3. キーボード ショートカットを使用する 解決策 1 – ズーム レベルをリセットする Safari ブラウザから直接ズーム レベルを制御できます。ステップ 1 – Safari を使用している場合

Windows 11は、マイクロソフトが発売した最新のオペレーティングシステムとして、ユーザーに深く愛されています。 Windows 11 を使用する過程で、権限が必要な操作を実行するためにシステム管理者権限を取得する必要がある場合があります。次に、Windows 11でシステム管理者権限を取得する手順を詳しく紹介します。まずは「スタートメニュー」をクリックすると、左下隅にWindowsのアイコンが表示されますので、このアイコンをクリックして「スタートメニュー」を開きます。 2 番目のステップでは、「」を見つけてクリックします。

iPhone の Shazam アプリに問題がありますか? Shazam は、曲を聞いて曲を見つけるのに役立ちます。ただし、Shazam が正常に動作しない場合、または曲が認識されない場合は、手動でトラブルシューティングを行う必要があります。 Shazam アプリの修復にはそれほど時間はかかりません。したがって、これ以上時間を無駄にすることなく、以下の手順に従って Shazam アプリの問題を解決してください。解決策 1 – 太字テキスト機能を無効にする iPhone の太字テキストが、Shazam が正しく動作しない原因である可能性があります。ステップ 1 – これは iPhone の設定からのみ実行できます。それで、開けてください。ステップ 2 – 次に、そこにある「ディスプレイと明るさ」設定を開きます。ステップ 3 – 「太字テキスト」が有効になっている場合

iPhone ではスクリーンショット機能が動作しませんか?スクリーンショットの撮影は非常に簡単で、音量を上げるボタンと電源ボタンを同時に押して携帯電話の画面を取得するだけです。ただし、デバイスでフレームをキャプチャする方法は他にもあります。解決策 1 – Assistive Touch の使用 Assistive Touch 機能を使用してスクリーンショットを撮ります。ステップ 1 – 電話の設定に移動します。ステップ 2 – 次に、タップしてアクセシビリティ設定を開きます。ステップ 3 – タッチ設定を開きます。ステップ 4 – 次に、Assistive Touch 設定を開きます。ステップ 5 – 携帯電話の Assistive Touch をオンにします。ステップ 6 – 「トップメニューのカスタマイズ」を開いてアクセスします。ステップ 7 – ここで必要なのは、これらの機能のいずれかを画面キャプチャにリンクすることだけです。それで最初をクリックしてください
