Example code of virtual scroll bar based on vue. js 2.x, vue. js2.x
Preface
I remember that I had browsed an open-source cms project and found that the menu on the left of the Project had exceeded the width of windows. Why didn't I get a scroll bar? Then I took a closer look and found a small div on the left of it. Then I tried to drag it and found that it could be the same as the native scroll bar! You can view its source code and find that this scroll bar is called slimScroll. Then I went to its github repository and looked at the source code, I feel like I can make the same scroll bar! Implemented through vue!
Design
Now let's start to design the scroll bar:
Design the scroll bar dom
The first thing to think about is: If you want to scroll the content you want to scroll, the first point is that its parent dom must be fixed length and width, that is, the excess part must be hidden, that is, a style is added: overflow: hidden. Therefore, we add a wrap for the content to be rolled so that its length and width are equal to that of the parent dom. Then there is a style called overflow: hidden, the encapsulated element is called scrollPanel.
Second, we know that we need to be as powerful as the native scroll bar! The horizontal and vertical scroll bars must be designed. The relationship between the scroll bars and scrollPanel belongs to the sibling node, because the existence of the scroll bars cannot make the original style typographical error, it also supports top and left to control its position, so the position of the scroll bar must be absolute. Well, we call the horizontal scroll bar: hBar and the vertical scroll bar: vBar.
Finally, we have designed scrollPanel, vBar, and hBar. We need a parent div to wrap them up and add a style: position: relative.
Practice
Design Component Structure
First, our plug-in consists of four components, three of which are child components and one is parent component, namely vueScroll (parent component) scrollPanel (child components that require scrolling content), vBar (vertical scroll bar), and hBar (horizontal scroll bar)
Next, let's design the functions in charge of each component. The components here are divided into control layer components and display components (those familiar with react should have some knowledge). The display layer components only display functions: vBar, hBar, scrollPanel, the control layer component is similar to the cpu and can control the status of sub-components, such as width, height, color, transparency, and position. The control layer component is vueScroll.
Implementation
HBar/vBar
The two hBar and vBar are horizontal and vertical scroll bars respectively. The functions are basically the same, so we put them together. Here we take vBar as an example.
The properties passed by the props receiving parent component are as follows:
{Height: vm. state. height + 'px ', // The height width of the scroll bar: vm. ops. width, // The position of the scroll bar width: 'absolute ', background: vm. ops. background, // top: vm. state. top + 'px ', // The height transition of the scroll bar: 'opacity. 5s ', // disappear/the time used for displaying cursor: 'pointer', // opacity: vm. state. opacity, // transparency userSelect: 'none '}
2. the scroll bar is displayed when the mouse moves.
... Render (_ c) {return _ c (//... {mouseenter: function (e) {vm. $ emit ('showvbar'); // trigger the parent component event and display the scroll bar }}//...)}
The state indicates the state, which can be changed during running, while the ops parameter is configured and transmitted by the user.
ScrollPanel
The widget that contains the scrolling content. The style must be set to overflow: hidden.
1. Style
var style = vm.scrollContentStyle; style.overflow = 'hidden'; // ... { style: style } // ...
2. Events
// ... render(_c) { // ... on: { mouseenter: function() { vm.$emit('showBar'); }, mouseleave: function() { vm.$emit('hideBar'); } } // ... } // ...
Vuescroll
Control components. Controls the status of sub-components and adds various listener events.
1. Obtain the dom element of the Child component to obtain the Real-Time dom information.
// ... 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. display the scroll bar
Display a scroll bar, including a horizontal scroll bar and a vertical scroll bar. The following uses a vertical scroll bar as an example:
//... Var temp; var deltaY = {deltaY: this. vScrollBar. ops. deltaY // obtain the user-configured deltaY}; if (! This. isMouseLeavePanel | this. vScrollBar. ops. keepShow) {if (this. vScrollBar. state. height = temp = this. getVBarHeight (deltaY) {// judgment condition // reset the status of the scroll bar this. vScrollBar. state. top = this. resizeVBarTop (temp); this. vScrollBar. state. height = temp. height; this. vScrollBar. state. opacity = this. vScrollBar. ops. opacity ;}}//...
3. Obtain the height of the scroll bar.
Because the height of the dom element is not fixed, you need to obtain the real height of the dom in real time. The formula for calculating the height of the scroll bar is as follows:
var height = Math.max( scrollPanelHeight / (scrollPanelScrollHeight / scrollPanelHeight), this.vScrollBar.minBarHeight );
That is, the height of the scroll bar: scrollPanel Height = scrollPanel Height: dom element height
4. resizeVBarTop: to prevent errors, you can determine the height of the scroll bar from the parent element.
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. Listen to scroll events.
// ... on: { wheel: vm.wheel } // ... wheel(e) { var vm = this; vm.showVBar(); vm.scrollVBar(e.deltaY > 0 ? 1 : -1, 1); e.stopPropagation(); } // ...
6. Listen to the scroll bar drag event
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; // record the initial Y location 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); // put the event into the array and remove the registration time before destruction. }
7. Adapt to the mobile terminal and listen for touch events. The principle is similar to the drag event. It is nothing more than an extra judgment to determine whether the current direction is x or 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. Scroll content
The principle of scrolling content is nothing more than changing the scrollTop/scrollLeft of the scrollPanel to control the movement of content from top to bottom to left.
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. innerDel TaY; 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) ;}/// these are the listening and rolling functions passed to the parent component. 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. Destroy registration events.
We have put the registration event in the listeners array just now. We can destroy it in the beforedestroy hook.
// remove the registryed event. this.listeners.forEach(function(item) { item.dom.removeEventListener(item.event, item.type); });
Run
Shows how the PC runs:
As shown in the following figure after the listener event is registered:
Run:
It can be seen that the performance is consistent with that of the native scroll bar.
Conclusion & sentiment
The above basically finished the design of the scroll bar. First of all, I am very grateful to the nuggets for giving me such a sharing platform, and then I am grateful to the slimScroll author for giving me such an idea. After completing this plug-in, I have learned more about dom element scrollWidth, scrollHeigh, scrollTop, and scrollLeft. Finally, I have attached the github Project address.
The above is the core source code of this component. I hope it will be helpful for everyone's learning, and I hope you can support the house of helping customers more.