Virtual scroll bars about 2.x in vue.js
This article mainly introduces the sample code of the virtual scroll bar based on vue.js 2.x. Now I share it with you and give it as a reference.
Preface
I remember that I once browsed an open source cms project by chance, and found that the menu on the left side of the project exceeded the width of windows. I was curious why. There is no scroll bar? Then I took a closer look and found a small p on the left side of it. Then I tried dragging it and found that it was the same as the native scroll bar! By looking at its source code, I discovered that this scroll bar is called slimScroll. Then I went to its github repository and looked at it. After studying the source code, I felt that I could also make the same scroll bar! Achieved through vue!
Design
Okay, now we start the steps of designing the scroll bar:
Design scroll bar dom
The first thing to think about is: If you want to scroll the content you need to scroll, the first thing is that its parent dom must have a fixed length and width, that is, the excess part must be hidden, that is, add a style: overflow: hidden, Therefore, we add a wrapper to the content to be scrolled so that its length and width are equal to the parent dom, and then there is a style called: overflow: hidden, and the wrapped element is called scrollPanel
Secondly: we know, We want to make it as powerful as the native scroll bar! It is necessary to design horizontal scroll bars and vertical scroll bars. The scroll bar and scrollPanel belong to the relationship between sibling nodes. Because the existence of the scroll bar cannot make the original style layout error, and supports top and left to control its position, so the scroll bar The position must be absolute. Okay, let's call the horizontal scroll bar: hBar, and the vertical scroll bar: vBar
Finally: we designed scrollPanel, vBar, hBar, and we need a parent p to wrap them , and then add a style: position: relative
Practice
Design component structure
First of all, our plug-ins are 4 components, 3 of which are child components and 1 is a parent component, namely: vueScroll (parent component), scrollPanel (subcomponent that wraps content that needs to be scrolled), vBar (vertical scroll bar), hBar (horizontal scroll bar)
Secondly, let us design the functions that each component is responsible for. The components here are divided into control layer components and display components (students familiar with react should know this). The display layer components only complete the display function: vBar, hBar, scrollPanel. The control layer components are somewhat similar to CPU and can control sub-components. Various states, such as width, height, color, transparency, position, etc. The control layer component is: vueScroll.
Specific implementation
##hBar/vBar
hBar/vBar These two are horizontal scroll bars and vertical scroll bars respectively. , the functions implemented are roughly the same, so the old ones are mentioned together. Here we take vBar as an example. props receives the properties passed by the parent component, specifically:{ 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' }
... render(_c){ return _c( // ... { mouseenter: function(e) { vm.$emit('showVBar'); // 触发父组件事件,显示滚动条 } } // ... ) }
scrollPanel
The component that wraps the scrolling content, the style needs to be set to: overflow: hidden. 1. Stylevar style = vm.scrollContentStyle; style.overflow = 'hidden'; // ... { style: style } // ...
// ... render(_c) { // ... on: { mouseenter: function() { vm.$emit('showBar'); }, mouseleave: function() { vm.$emit('hideBar'); } } // ... } // ...
vuescroll
Control component. Control the status displayed by sub-components, add various listening events, etc. 1. Obtain the dom element of the sub-component to obtain real-time information of the 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; } // ...
// ... 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; } } // ...
var height = Math.max( scrollPanelHeight / (scrollPanelScrollHeight / scrollPanelHeight), this.vScrollBar.minBarHeight );
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; }
// ... on: { wheel: vm.wheel } // ... wheel(e) { var vm = this; vm.showVBar(); vm.scrollVBar(e.deltaY > 0 ? 1 : -1, 1); e.stopPropagation(); } // ...
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); // 把事件放到数组里面,等销毁之前移除掉注册的时间。 }
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" }); }
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); },
// remove the registryed event. this.listeners.forEach(function(item) { item.dom.removeEventListener(item.event, item.type); });
It can be seen that the performance effect is consistent with the native scroll bar.
Conclusion & Reflection
The above is basically the design of the scroll bar I designed. First of all, I am very grateful to the Nuggets for giving me such a sharing platform, and then I would like to thank slimScroll. The author gave me such an idea. After finishing this plug-in, I know more about scrollWidth, scrollHeigh, scrollTop, and scrollLeft of DOM elements.
The above is what I compiled for everyone. I hope it will be helpful to everyone in the future.
Related articles:
3 basic modes of vue routing parameters (detailed tutorial)
Implement dynamic introduction of files in webpack
The above is the detailed content of Virtual scroll bars about 2.x in vue.js. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

Unlike Windows 10, Windows 11 features new modern “fluid scrollbars” that change shape when users interact with them. Fluent scrollbars are dynamic in nature, they automatically scale in different form factors or when you change the window size, and it is currently used in apps like Settings, Media Players, and more. Google Chrome may soon have smooth scrollbar functionality, according to a new proposal from Microsoft. Microsoft says in a proposal that they want to modernize old scroll bars in Chrome

When using the Vue framework to develop front-end projects, we will deploy multiple environments when deploying. Often the interface domain names called by development, testing and online environments are different. How can we make the distinction? That is using environment variables and patterns.

Ace is an embeddable code editor written in JavaScript. It matches the functionality and performance of native editors like Sublime, Vim, and TextMate. It can be easily embedded into any web page and JavaScript application. Ace is maintained as the main editor for the Cloud9 IDE and is the successor to the Mozilla Skywriter (Bespin) project.

Vue.js has become a very popular framework in front-end development today. As Vue.js continues to evolve, unit testing is becoming more and more important. Today we’ll explore how to write unit tests in Vue.js 3 and provide some best practices and common problems and solutions.

Foreword: In the development of vue3, reactive provides a method to implement responsive data. This is a frequently used API in daily development. In this article, the author will explore its internal operating mechanism.

In Vue.js, developers can use two different syntaxes to create user interfaces: JSX syntax and template syntax. Both syntaxes have their own advantages and disadvantages. Let’s discuss their differences, advantages and disadvantages.

In the actual development project process, sometimes it is necessary to upload relatively large files, and then the upload will be relatively slow, so the background may require the front-end to upload file slices. It is very simple. For example, 1 A gigabyte file stream is cut into several small file streams, and then the interface is requested to deliver the small file streams respectively.

Since the release of Vue3, the word composition API has entered the field of vision of students who write Vue. I believe everyone has always heard how much better the composition API is than the previous options API. Now, due to the release of the @vue/composition-api plug-in, Vue2 Students can also get on the bus. Next, we will mainly use responsive ref and reactive to conduct an in-depth analysis of how this plug-in achieves this.
