この記事では主に vue.js 2.x に基づく仮想スクロール バーのサンプル コードを紹介します。
前書き
以前、偶然オープンソースの cms プロジェクトを閲覧していて、プロジェクトの左側のメニューがウィンドウの幅を超えていることに気づきました。なぜスクロール バーがないのか疑問に思いました。そこでよく見てみると、その左側に小さな p があったので、それをドラッグしてみると、ネイティブのスクロール バーと同じであることがわかりました。ソースコードを見ると、このスクロールバーはslimScrollという名前であることがわかり、githubリポジトリにアクセスしてソースコードを調べたところ、同じスクロールバーを作成することができると感じました。 vueで実現!
デザイン
それでは、スクロール バーのデザイン手順を始めましょう:
スクロール バーの dom をデザインします
最初に考えるべきことは次のとおりです: コンテンツを作成したい場合は、スクロールする必要があります。スクロール、最初のことはそれです。親 dom は固定の長さと幅を持つ必要があります。つまり、余分な部分は非表示にする必要があります。つまり、スタイルが追加されます。したがって、コンテンツにラッパーを追加します。長さと幅が親 dom と等しくなるようにスクロールされると、スタイルが 1 つ呼び出されます: overflow: hidden、ラップされた要素はscrollPanel と呼ばれます
2 番目: と同じくらい強力である必要があることがわかっています。ネイティブスクロールバー!スクロール バーと垂直スクロール バーは、兄弟ノード間の関係に属しているため、スクロール バーの存在により元のスタイルのレイアウトが崩れることはなく、その位置を制御するために上と左をサポートするように設計する必要があります。なので、スクロール バーの位置は絶対値でなければなりません。水平スクロール バーを hBar、垂直スクロール バーを vBar と呼びましょう
最後に、scrollPanel、vBar、hBar を設計しました。これらをラップするには親 p が必要です。そして Style:position:relative を追加します
練習
デザインコンポーネント構造
まず第一に、私たちのプラグインには合計 4 つのコンポーネントがあり、そのうち 3 つは子コンポーネント、1 つは親コンポーネントです。すなわち: vueScroll (親コンポーネント)、scrollPanel (スクロールする必要があるコンテンツをラップするサブコンポーネント)、vBar (垂直スクロール バー)、hBar (水平スクロール バー)
次に、各コンポーネントが担当する関数を設計しましょう。ここでのコンポーネントは、コントロール層コンポーネントとディスプレイ コンポーネントに分かれています (反応に慣れている学生はこれを知っているはずです)。ディスプレイ層コンポーネントは、vBar、hBar、scrollPanel などの表示機能を完了するだけです。コントロール層コンポーネントは CPU に似ており、制御できます。幅、高さ、色、透明度、位置などのさまざまな状態のサブコンポーネント。コントロール層コンポーネントは vueScroll です。
具体的な実装
hBar/vBar
hBar/vBar これら 2 つはそれぞれ水平スクロール バーと垂直スクロール バーであり、実装する機能はほぼ同じであるため、ここでは vBar としてまとめて説明します。例。
props は、親コンポーネントによって渡されたプロパティ、具体的には、マウスの移動時に主にスクロール バーを表示する
{ height: vm.state.height + 'px', //滚动条的高度 width: vm.ops.width, // 滚动条的宽度 position: 'absolute', background: vm.ops.background, // 滚动条背景色 top: vm.state.top + 'px', // 滚动条的高度 transition: 'opacity .5s', // 消失/显示 所用的时间 cursor: 'pointer', // opacity: vm.state.opacity, // 透明度 userSelect: 'none' }
2 イベントを受け取ります。
... render(_c){ return _c( // ... { mouseenter: function(e) { vm.$emit('showVBar'); // 触发父组件事件,显示滚动条 } } // ... ) }
このうち、state は実行時に変更できる状態を表し、ops はユーザーによって渡される構成パラメーターです。
scrollPanel
は、スクロールコンテンツをラップするコンポーネントです: overflow: hidden に設定する必要があります。
1. スタイル
var style = vm.scrollContentStyle; style.overflow = 'hidden'; // ... { style: style } // ...
2. イベント
// ... render(_c) { // ... on: { mouseenter: function() { vm.$emit('showBar'); }, mouseleave: function() { vm.$emit('hideBar'); } } // ... } // ...
vescroll
コントロールコンポーネント。サブコンポーネントによって表示されるステータスを制御したり、さまざまなリスニング イベントを追加したりできます。
1. サブコンポーネントの dom 要素を取得して、dom のリアルタイム情報を取得します。
// ... initEl() { this.scrollPanel.el = this.$refs['vueScrollPanel'] && this.$refs['vueScrollPanel'].$el; this.vScrollBar.el = this.$refs['vScrollBar'] && this.$refs['vScrollBar'].$el; this.hScrollBar.el = this.$refs['hScrollBar'] && this.$refs['hScrollBar'].$el; } // ...
2. スクロール バーを表示します。水平スクロール バーの表示と垂直スクロール バーの表示を含みます。
// ... var temp; var deltaY = { deltaY: this.vScrollBar.ops.deltaY // 获取用户配置的deltaY }; if(!this.isMouseLeavePanel || this.vScrollBar.ops.keepShow){ if ((this.vScrollBar.state.height = temp = this.getVBarHeight(deltaY))) { // 判断条件 // 重新设置滚动条的状态 this.vScrollBar.state.top = this.resizeVBarTop(temp); this.vScrollBar.state.height = temp.height; this.vScrollBar.state.opacity = this.vScrollBar.ops.opacity; } } // ...
3.スクロールバー
dom要素のため高さは固定されていないため、リアルタイムでdomの実際の高さを取得する必要があります。 スクロールバーの高さの計算式は次のとおりです:
var height = Math.max( scrollPanelHeight / (scrollPanelScrollHeight / scrollPanelHeight), this.vScrollBar.minBarHeight );
つまり:スクロール バーの高さ: スクロール パネルの高さ == スクロール パネルの高さ: dom 要素の高さ
4. エラーを防ぐために、親からスクロール バーの高さを見つけることができるように、resizeVBarTop を実行します。要素。
resizeVBarTop({height, scrollPanelHeight, scrollPanelScrollHeight, deltaY}) { // cacl the last height first var lastHeight = scrollPanelScrollHeight - scrollPanelHeight - this.scrollPanel.el.scrollTop; if(lastHeight < this.accuracy) { lastHeight = 0; } var time = Math.abs(Math.ceil(lastHeight / deltaY)); var top = scrollPanelHeight - (height + (time * this.vScrollBar.innerDeltaY)); return top; }
5. ホイール スクロール イベントを監視します。
// ... on: { wheel: vm.wheel } // ... wheel(e) { var vm = this; vm.showVBar(); vm.scrollVBar(e.deltaY > 0 ? 1 : -1, 1); e.stopPropagation(); } // ...
6. スクロール バーのドラッグ イベントを監視します
listenVBarDrag: function() { var vm = this; var y; var _y; function move(e) { _y = e.pageY; var _delta = _y - y; vm.scrollVBar(_delta > 0 ? 1 : -1, Math.abs(_delta / vm.vScrollBar.innerDeltaY)); y = _y; } function t(e) { var deltaY = { deltaY: vm.vScrollBar.ops.deltaY }; if(!vm.getVBarHeight(deltaY)) { return; } vm.mousedown = true; y = e.pageY; // 记录初始的Y的位置 vm.showVBar(); document.addEventListener('mousemove', move); document.addEventListener('mouseup', function(e) { vm.mousedown = false; vm.hideVBar(); document.removeEventListener('mousemove', move); }); } this.listeners.push({ dom: vm.vScrollBar.el, event: t, type: "mousedown" }); vm.vScrollBar.el.addEventListener('mousedown', t); // 把事件放到数组里面,等销毁之前移除掉注册的时间。 }
7. モバイル端末に適応し、タッチ イベントをリッスンします。原理はドラッグ イベントの原理と似ていますが、現在の方向が x であるか y であるかを決定する追加の判断がある点が異なります。
listenPanelTouch: function() { var vm = this; var pannel = this.scrollPanel.el; var x, y; var _x, _y; function move(e) { if(e.touches.length) { var touch = e.touches[0]; _x = touch.pageX; _y = touch.pageY; var _delta = void 0; var _deltaX = _x - x; var _deltaY = _y - y; if(Math.abs(_deltaX) > Math.abs(_deltaY)) { _delta = _deltaX; vm.scrollHBar(_delta > 0 ? -1 : 1, Math.abs(_delta / vm.hScrollBar.innerDeltaX)); } else if(Math.abs(_deltaX) < Math.abs(_deltaY)){ _delta = _deltaY; vm.scrollVBar(_delta > 0 ? -1 : 1, Math.abs(_delta / vm.vScrollBar.innerDeltaY)); } x = _x; y = _y; } } function t(e) { var deltaY = { deltaY: vm.vScrollBar.ops.deltaY }; var deltaX = { deltaX: vm.hScrollBar.ops.deltaX }; if(!vm.getHBarWidth(deltaX) && !vm.getVBarHeight(deltaY)) { return; } if(e.touches.length) { e.stopPropagation(); var touch = e.touches[0]; vm.mousedown = true; x = touch.pageX; y = touch.pageY; vm.showBar(); pannel.addEventListener('touchmove', move); pannel.addEventListener('touchend', function(e) { vm.mousedown = false; vm.hideBar(); pannel.removeEventListener('touchmove', move); }); } } pannel.addEventListener('touchstart', t); this.listeners.push({ dom: pannel, event: t, type: "touchstart" }); }
8. コンテンツをスクロールする
コンテンツをスクロールする原理は、scrollPanel のscrollTop/scrollLeftを変更して、コンテンツが上下左右に移動するように制御することに他なりません。
scrollVBar: function(pos, time) { // >0 scroll to down <0 scroll to up var top = this.vScrollBar.state.top; var scrollPanelHeight = getComputed(this.scrollPanel.el, 'height').replace('px', ""); var scrollPanelScrollHeight = this.scrollPanel.el.scrollHeight; var scrollPanelScrollTop = this.scrollPanel.el.scrollTop; var height = this.vScrollBar.state.height; var innerdeltaY = this.vScrollBar.innerDeltaY; var deltaY = this.vScrollBar.ops.deltaY; if (!((pos < 0 && top <= 0) || (scrollPanelHeight <= top + height && pos > 0) || (Math.abs(scrollPanelScrollHeight - scrollPanelHeight) < this.accuracy))) { var Top = top + pos * innerdeltaY * time; var ScrollTop = scrollPanelScrollTop + pos * deltaY * time; if (pos < 0) { // scroll ip this.vScrollBar.state.top = Math.max(0, Top); this.scrollPanel.el.scrollTop = Math.max(0, ScrollTop); } else if (pos > 0) { // scroll down this.vScrollBar.state.top = Math.min(scrollPanelHeight - height, Top); this.scrollPanel.el.scrollTop = Math.min(scrollPanelScrollHeight - scrollPanelHeight, ScrollTop); } } // 这些是传递给父组件的监听滚动的函数的。 var content = {}; var bar = {}; var process = ""; content.residual = (scrollPanelScrollHeight - scrollPanelScrollTop - scrollPanelHeight); content.scrolled = scrollPanelScrollTop; bar.scrolled = this.vScrollBar.state.top; bar.residual = (scrollPanelHeight - this.vScrollBar.state.top - this.vScrollBar.state.height); bar.height = this.vScrollBar.state.height; process = bar.scrolled/(scrollPanelHeight - bar.height); bar.name = "vBar"; content.name = "content"; this.$emit('vscroll', bar, content, process); },
9. 登録されたイベントを破棄します。
今、登録されたイベントをリスナー配列に入れました。そして、それらを beforedestroy フックで破棄できます。
// remove the registryed event. this.listeners.forEach(function(item) { item.dom.removeEventListener(item.event, item.type); });
実行中のスクリーンショット
PC側の実行中のスクリーンショットは以下の通りです:
リスニングイベントの登録後は以下のようになります:
携帯電話での実行中のスクリーンショット:
パフォーマンス効果がネイティブ スクロール バーと一致していることがわかります。
結論と感想
上記は基本的に私がデザインしたスクロールバーのデザインです。 まず第一に、私にこのような共有プラットフォームを提供してくれたナゲッツに感謝し、それから作者に感謝したいと思います。このようなアイデアを与えてくれたslimScroll。このプラグインを完成させた後、DOM 要素のscrollWidth、scrollHeigh、scrollTop、scrollLeft について詳しく知ることができました。これが将来皆さんに役立つことを願っています。
関連記事:
vue ルートパラメータの受け渡しの 3 つの基本モード (詳細なチュートリアル) JavaScript の関連トリック webpack でファイルを動的にインポートする実装以上がvue.js の仮想スクロール バー約 2.xの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。