This time I will bring you the implementation of sliding stacking components in vue. What are the precautions for implementing sliding stacking components in vue. The following is a practical case, let's take a look.
Preface
Hi, talking about Tantan, I think all of you are familiar with the program (after all, there are many girls). Tantan’s stacked sliding components play a key role in being able to flip brands on it smoothly. Let’s take a look at how to write it with vue. A Tantan stacking component
1. Functional analysis
A brief summary of the basic functional points included:
Stacking of pictures
-
Sliding of the first picture
-
Sliding out after the condition is successful, condition Rebound after failure
-
The next picture after sliding out is stacked to the top
-
Experience optimization
-
According to different touch points, the first image will be offset at different angles when sliding
-
The offset area determines whether the slide-out is successful
2. Specific implementation
With the summarized functional points, our ideas for implementing components will be clearer
1. Stacking effect
There are a large number of examples of stacked picture effects on the Internet. The implementation methods are similar. The perspective of the sub-layer is mainly achieved by setting perspective and perspective-origin on the parent layer. The stacking effect can be simulated by setting the translate3d Z-axis value on the sub-layer. , the specific code is as follows
// 图片堆叠dom <!--opacity: 0 隐藏我们不想看到的stack-item层级--> <!--z-index: -1 调整stack-item层级"-->
The above is just a set of static code. What we hope to get is a vue component, so we need to create a component template stack.vue first. In the template, we can use v-for to traverse the stack nodes and use :style to modify the style of each item. , the code is as follows
<template> <ul> <li> <img src="/static/imghwm/default1.png" data-src="item.src" class="lazy" alt="Vue implements sliding stacking components" > </li> </ul> </template> <script> export default { props: { // pages数据包含基础的图片数据 pages: { type: Array, default: [] } }, data () { return { // basicdata数据包含组件基本数据 basicdata: { currentPage: 0 // 默认首图的序列 }, // temporaryData数据包含组件临时数据 temporaryData: { opacity: 1, // 记录opacity zIndex: 10, // 记录zIndex visible: 3 // 记录默认显示堆叠数visible } } }, methods: { // 遍历样式 transform (index) { if (index >= this.basicdata.currentPage) { let style = {} let visible = this.temporaryData.visible let perIndex = index - this.basicdata.currentPage // visible可见数量前滑块的样式 if (index <= this.basicdata.currentPage + visible - 1) { style['opacity'] = '1' style['transform'] = 'translate3D(0,0,' + -1 * perIndex * 60 + 'px' + ')' style['zIndex'] = visible - index + this.basicdata.currentPage style['transitionTimingFunction'] = 'ease' style['transitionDuration'] = 300 + 'ms' } else { style['zIndex'] = '-1' style['transform'] = 'translate3D(0,0,' + -1 * visible * 60 + 'px' + ')' } return style } } } } </script>
key point
:style can bind objects as well as arrays and functions, which is very useful when traversing.
The most basic dom structure has been constructed, and the next step is to make the first picture "move"
2. Picture sliding
The picture sliding effect appears in many scenes. Its principle is nothing more than listening to the touches event, getting the displacement, and then changing the target displacement through translate3D. Therefore, the steps we want to implement are as follows
Binding the touches event to the stack
-
Monitor and store the value of the gesture position change
-
Change The x and y values of translate3D in the css attribute of the first image
# Implementation In the vue framework, it is not recommended to directly operate nodes, but to bind elements through the instruction v-on. Therefore, we write the bindings in v-for traversal, and use index to determine whether it is the first image, and then use :style modifies the style of the homepage. The specific code is as follows:
<template> <ul> <li> <img src="/static/imghwm/default1.png" data-src="item.src" class="lazy" alt="Vue implements sliding stacking components" > </li> </ul> </template> <script> export default { props: { // pages数据包含基础的图片数据 pages: { type: Array, default: [] } }, data () { return { // basicdata数据包含组件基本数据 basicdata: { start: {}, // 记录起始位置 end: {}, // 记录终点位置 currentPage: 0 // 默认首图的序列 }, // temporaryData数据包含组件临时数据 temporaryData: { poswidth: '', // 记录位移 posheight: '', // 记录位移 tracking: false // 是否在滑动,防止多次操作,影响体验 } } }, methods: { touchstart (e) { if (this.temporaryData.tracking) { return } // 是否为touch if (e.type === 'touchstart') { if (e.touches.length > 1) { this.temporaryData.tracking = false return } else { // 记录起始位置 this.basicdata.start.t = new Date().getTime() this.basicdata.start.x = e.targetTouches[0].clientX this.basicdata.start.y = e.targetTouches[0].clientY this.basicdata.end.x = e.targetTouches[0].clientX this.basicdata.end.y = e.targetTouches[0].clientY } // pc操作 } else { this.basicdata.start.t = new Date().getTime() this.basicdata.start.x = e.clientX this.basicdata.start.y = e.clientY this.basicdata.end.x = e.clientX this.basicdata.end.y = e.clientY } this.temporaryData.tracking = true }, touchmove (e) { // 记录滑动位置 if (this.temporaryData.tracking && !this.temporaryData.animation) { if (e.type === 'touchmove') { this.basicdata.end.x = e.targetTouches[0].clientX this.basicdata.end.y = e.targetTouches[0].clientY } else { this.basicdata.end.x = e.clientX this.basicdata.end.y = e.clientY } // 计算滑动值 this.temporaryData.poswidth = this.basicdata.end.x - this.basicdata.start.x this.temporaryData.posheight = this.basicdata.end.y - this.basicdata.start.y } }, touchend (e) { this.temporaryData.tracking = false // 滑动结束,触发判断 }, // 非首页样式切换 transform (index) { if (index > this.basicdata.currentPage) { let style = {} let visible = 3 let perIndex = index - this.basicdata.currentPage // visible可见数量前滑块的样式 if (index <= this.basicdata.currentPage + visible - 1) { style['opacity'] = '1' style['transform'] = 'translate3D(0,0,' + -1 * perIndex * 60 + 'px' + ')' style['zIndex'] = visible - index + this.basicdata.currentPage style['transitionTimingFunction'] = 'ease' style['transitionDuration'] = 300 + 'ms' } else { style['zIndex'] = '-1' style['transform'] = 'translate3D(0,0,' + -1 * visible * 60 + 'px' + ')' } return style } }, // 首页样式切换 transformIndex (index) { // 处理3D效果 if (index === this.basicdata.currentPage) { let style = {} style['transform'] = 'translate3D(' + this.temporaryData.poswidth + 'px' + ',' + this.temporaryData.posheight + 'px' + ',0px)' style['opacity'] = 1 style['zIndex'] = 10 return style } } } } </script>3. Slide out after the condition is successful, rebound after the condition fails The triggering judgment of the condition is performed after touchend/mouseup. Here we first use simple conditions to judge, and at the same time give the first image pop-up and rebound effect. The code is as follows
<template> <ul> <li> <img src="/static/imghwm/default1.png" data-src="item.src" class="lazy" alt="Vue implements sliding stacking components" > </li> </ul> </template> <script> export default { props: { // pages数据包含基础的图片数据 pages: { type: Array, default: [] } }, data () { return { // basicdata数据包含组件基本数据 basicdata: { start: {}, // 记录起始位置 end: {}, // 记录终点位置 currentPage: 0 // 默认首图的序列 }, // temporaryData数据包含组件临时数据 temporaryData: { poswidth: '', // 记录位移 posheight: '', // 记录位移 tracking: false, // 是否在滑动,防止多次操作,影响体验 animation: false, // 首图是否启用动画效果,默认为否 opacity: 1 // 记录首图透明度 } } }, methods: { touchstart (e) { if (this.temporaryData.tracking) { return } // 是否为touch if (e.type === 'touchstart') { if (e.touches.length > 1) { this.temporaryData.tracking = false return } else { // 记录起始位置 this.basicdata.start.t = new Date().getTime() this.basicdata.start.x = e.targetTouches[0].clientX this.basicdata.start.y = e.targetTouches[0].clientY this.basicdata.end.x = e.targetTouches[0].clientX this.basicdata.end.y = e.targetTouches[0].clientY } // pc操作 } else { this.basicdata.start.t = new Date().getTime() this.basicdata.start.x = e.clientX this.basicdata.start.y = e.clientY this.basicdata.end.x = e.clientX this.basicdata.end.y = e.clientY } this.temporaryData.tracking = true this.temporaryData.animation = false }, touchmove (e) { // 记录滑动位置 if (this.temporaryData.tracking && !this.temporaryData.animation) { if (e.type === 'touchmove') { this.basicdata.end.x = e.targetTouches[0].clientX this.basicdata.end.y = e.targetTouches[0].clientY } else { this.basicdata.end.x = e.clientX this.basicdata.end.y = e.clientY } // 计算滑动值 this.temporaryData.poswidth = this.basicdata.end.x - this.basicdata.start.x this.temporaryData.posheight = this.basicdata.end.y - this.basicdata.start.y } }, touchend (e) { this.temporaryData.tracking = false this.temporaryData.animation = true // 滑动结束,触发判断 // 简单判断滑动宽度超出100像素时触发滑出 if (Math.abs(this.temporaryData.poswidth) >= 100) { // 最终位移简单设定为x轴200像素的偏移 let ratio = Math.abs(this.temporaryData.posheight / this.temporaryData.poswidth) this.temporaryData.poswidth = this.temporaryData.poswidth >= 0 ? this.temporaryData.poswidth + 200 : this.temporaryData.poswidth - 200 this.temporaryData.posheight = this.temporaryData.posheight >= 0 ? Math.abs(this.temporaryData.poswidth * ratio) : -Math.abs(this.temporaryData.poswidth * ratio) this.temporaryData.opacity = 0 // 不满足条件则滑入 } else { this.temporaryData.poswidth = 0 this.temporaryData.posheight = 0 } }, // 非首页样式切换 transform (index) { if (index > this.basicdata.currentPage) { let style = {} let visible = 3 let perIndex = index - this.basicdata.currentPage // visible可见数量前滑块的样式 if (index <= this.basicdata.currentPage + visible - 1) { style['opacity'] = '1' style['transform'] = 'translate3D(0,0,' + -1 * perIndex * 60 + 'px' + ')' style['zIndex'] = visible - index + this.basicdata.currentPage style['transitionTimingFunction'] = 'ease' style['transitionDuration'] = 300 + 'ms' } else { style['zIndex'] = '-1' style['transform'] = 'translate3D(0,0,' + -1 * visible * 60 + 'px' + ')' } return style } }, // 首页样式切换 transformIndex (index) { // 处理3D效果 if (index === this.basicdata.currentPage) { let style = {} style['transform'] = 'translate3D(' + this.temporaryData.poswidth + 'px' + ',' + this.temporaryData.posheight + 'px' + ',0px)' style['opacity'] = this.temporaryData.opacity style['zIndex'] = 10 if (this.temporaryData.animation) { style['transitionTimingFunction'] = 'ease' style['transitionDuration'] = 300 + 'ms' } return style } } } } </script>4. After sliding out, the next picture is stacked on top Restacking is the last function of the component, and it is also the most important and complex function. In our code, the sorting of stack-item depends on the transformIndex and transform function of the binding: style. The condition determined in the function is currentPage. Is it necessary to change the currentPage to 1 to complete the re-stacking? The answer is not that simple, because our slide out is an animation effect, which will last 300ms, and the rearrangement caused by the change of currentPage will change immediately, interrupting the progress of the animation. Therefore, we need to modify the sorting conditions of the transform function first, and then change the currentPage.
- # Implementation
-
Modify the sorting condition of transform function
Let currentPage - TransitionEnd event , and after the slide-out ends, relocate it to the stack list
The code is as follows:
<template> <ul> <li> <img src="/static/imghwm/default1.png" data-src="item.src" class="lazy" alt="Vue implements sliding stacking components" > </li> </ul> </template> <script> export default { props: { // pages数据包含基础的图片数据 pages: { type: Array, default: [] } }, data () { return { // basicdata数据包含组件基本数据 basicdata: { start: {}, // 记录起始位置 end: {}, // 记录终点位置 currentPage: 0 // 默认首图的序列 }, // temporaryData数据包含组件临时数据 temporaryData: { poswidth: '', // 记录位移 posheight: '', // 记录位移 lastPosWidth: '', // 记录上次最终位移 lastPosHeight: '', // 记录上次最终位移 tracking: false, // 是否在滑动,防止多次操作,影响体验 animation: false, // 首图是否启用动画效果,默认为否 opacity: 1, // 记录首图透明度 swipe: false // onTransition判定条件 } } }, methods: { touchstart (e) { if (this.temporaryData.tracking) { return } // 是否为touch if (e.type === 'touchstart') { if (e.touches.length > 1) { this.temporaryData.tracking = false return } else { // 记录起始位置 this.basicdata.start.t = new Date().getTime() this.basicdata.start.x = e.targetTouches[0].clientX this.basicdata.start.y = e.targetTouches[0].clientY this.basicdata.end.x = e.targetTouches[0].clientX this.basicdata.end.y = e.targetTouches[0].clientY } // pc操作 } else { this.basicdata.start.t = new Date().getTime() this.basicdata.start.x = e.clientX this.basicdata.start.y = e.clientY this.basicdata.end.x = e.clientX this.basicdata.end.y = e.clientY } this.temporaryData.tracking = true this.temporaryData.animation = false }, touchmove (e) { // 记录滑动位置 if (this.temporaryData.tracking && !this.temporaryData.animation) { if (e.type === 'touchmove') { this.basicdata.end.x = e.targetTouches[0].clientX this.basicdata.end.y = e.targetTouches[0].clientY } else { this.basicdata.end.x = e.clientX this.basicdata.end.y = e.clientY } // 计算滑动值 this.temporaryData.poswidth = this.basicdata.end.x - this.basicdata.start.x this.temporaryData.posheight = this.basicdata.end.y - this.basicdata.start.y } }, touchend (e) { this.temporaryData.tracking = false this.temporaryData.animation = true // 滑动结束,触发判断 // 简单判断滑动宽度超出100像素时触发滑出 if (Math.abs(this.temporaryData.poswidth) >= 100) { // 最终位移简单设定为x轴200像素的偏移 let ratio = Math.abs(this.temporaryData.posheight / this.temporaryData.poswidth) this.temporaryData.poswidth = this.temporaryData.poswidth >= 0 ? this.temporaryData.poswidth + 200 : this.temporaryData.poswidth - 200 this.temporaryData.posheight = this.temporaryData.posheight >= 0 ? Math.abs(this.temporaryData.poswidth * ratio) : -Math.abs(this.temporaryData.poswidth * ratio) this.temporaryData.opacity = 0 this.temporaryData.swipe = true // 记录最终滑动距离 this.temporaryData.lastPosWidth = this.temporaryData.poswidth this.temporaryData.lastPosHeight = this.temporaryData.posheight // currentPage+1 引发排序变化 this.basicdata.currentPage += 1 // currentPage切换,整体dom进行变化,把第一层滑动置零 this.$nextTick(() => { this.temporaryData.poswidth = 0 this.temporaryData.posheight = 0 this.temporaryData.opacity = 1 }) // 不满足条件则滑入 } else { this.temporaryData.poswidth = 0 this.temporaryData.posheight = 0 this.temporaryData.swipe = false } }, onTransitionEnd (index) { // dom发生变化后,正在执行的动画滑动序列已经变为上一层 if (this.temporaryData.swipe && index === this.basicdata.currentPage - 1) { this.temporaryData.animation = true this.temporaryData.lastPosWidth = 0 this.temporaryData.lastPosHeight = 0 this.temporaryData.swipe = false } }, // 非首页样式切换 transform (index) { if (index > this.basicdata.currentPage) { let style = {} let visible = 3 let perIndex = index - this.basicdata.currentPage // visible可见数量前滑块的样式 if (index <= this.basicdata.currentPage + visible - 1) { style['opacity'] = '1' style['transform'] = 'translate3D(0,0,' + -1 * perIndex * 60 + 'px' + ')' style['zIndex'] = visible - index + this.basicdata.currentPage style['transitionTimingFunction'] = 'ease' style['transitionDuration'] = 300 + 'ms' } else { style['zIndex'] = '-1' style['transform'] = 'translate3D(0,0,' + -1 * visible * 60 + 'px' + ')' } return style // 已滑动模块释放后 } else if (index === this.basicdata.currentPage - 1) { let style = {} // 继续执行动画 style['transform'] = 'translate3D(' + this.temporaryData.lastPosWidth + 'px' + ',' + this.temporaryData.lastPosHeight + 'px' + ',0px)' style['opacity'] = '0' style['zIndex'] = '-1' style['transitionTimingFunction'] = 'ease' style['transitionDuration'] = 300 + 'ms' return style } }, // 首页样式切换 transformIndex (index) { // 处理3D效果 if (index === this.basicdata.currentPage) { let style = {} style['transform'] = 'translate3D(' + this.temporaryData.poswidth + 'px' + ',' + this.temporaryData.posheight + 'px' + ',0px)' style['opacity'] = this.temporaryData.opacity style['zIndex'] = 10 if (this.temporaryData.animation) { style['transitionTimingFunction'] = 'ease' style['transitionDuration'] = 300 + 'ms' } return style } } } } </script>### ok~ After completing the above four steps, the basic functions of stacking components have been implemented. Come and see the effect###
I believe you have mastered the method after reading the case in this article. For more exciting information, please pay attention to other related articles on the php Chinese website!
Recommended reading:
How Native uses fetch to implement the image upload function
vue.js moves the array position and updates it in real time view
The above is the detailed content of Vue implements sliding stacking components. For more information, please follow other related articles on the PHP Chinese website!

Vue是目前最流行的前端框架之一,而VUE3则是Vue框架的最新版本,相较于VUE2,VUE3具备了更高的性能和更出色的开发体验,成为了众多开发者的首选。在VUE3中,使用extends继承组件是一个非常实用的开发方式,本文将为大家介绍如何使用extends继承组件。extends是什么?在Vue中,extends是一个非常实用的属性,它可以用于子组件继承父

Vue是一款非常流行的前端框架,它提供了很多工具和功能,如组件化、数据绑定、事件处理等,能够帮助开发者构建出高效、灵活和易维护的Web应用程序。在这篇文章中,我来介绍如何使用Vue实现一个日历组件。1、需求分析首先,我们需要分析一下这个日历组件的需求。一个基本的日历应该具备以下功能:展示当前月份的日历页面;支持切换到前一月或下一月;支持点击某一天,

Vue怎么通过JSX动态渲染组件?下面本篇文章给大家介绍一下Vue高效通过JSX动态渲染组件的方法,希望对大家有所帮助!

在VSCode中开发Vue/React组件时,怎么实时预览组件?本篇文章就给大家分享一个VSCode 中实时预览Vue/React组件的插件,希望对大家有所帮助!

在现代Web开发中,组件化是一个极受欢迎的开发模式。而Vue.js则是一个非常适合组件化的前端框架。在这篇文章中,我们将介绍如何使用Vue.js创建一个仿照片墙组件。在开始之前,我们需要明确一些准备工作。首先,我们需要安装Vue.js。安装的方法非常简单,只需在终端中输入以下命令:npminstallvue安装完成后,我们可以创建一个名为

VUE3开发入门教程:使用组件实现分页分页是一个常见的需求,因为在实际开发中,我们往往需要将大量的数据分成若干页以展示给用户。在VUE3开发中,可以通过使用组件实现分页功能,本文将介绍如何使用组件实现简单的分页功能。1.创建组件首先,我们需要创建一个分页组件,使用“vuecreate”命令创建VUE项目,并在src/components目录下创建Pagin

Vue是一款优秀的前端框架,在处理大量数据时,分页组件是必不可少的。分页组件可以使页面更加整洁,同时也可以提高用户体验。在Vue中,实现一个分页组件并不复杂,本文将介绍Vue如何实现分页组件。一、需求分析在实现分页组件前,我们需要对需求进行分析。一个基本的分页组件需要具有以下功能:展示当前页数、总页数以及每页展示条数点击分页按钮可以切换至不同页数展示当前页

Vue.js是一款流行的前端JavaScript框架,它提供了一种简单易用的方式来构建动态网页应用程序。Vue.js的主要特点是其模块化的设计和可插拔的组件系统。这使得开发者可以轻松地创建可复用的组件,从而提高了应用程序的重用性和可维护性。在本文中,我们将重点介绍VUE3初学者如何使用Vue.js组件组合实现可复用组合。Vue.js组件是一个完整的封装元素,


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

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

MinGW - Minimalist GNU for Windows
This project is in the process of being migrated to osdn.net/projects/mingw, you can continue to follow us there. MinGW: A native Windows port of the GNU Compiler Collection (GCC), freely distributable import libraries and header files for building native Windows applications; includes extensions to the MSVC runtime to support C99 functionality. All MinGW software can run on 64-bit Windows platforms.

mPDF
mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

WebStorm Mac version
Useful JavaScript development tools

Atom editor mac version download
The most popular open source editor

ZendStudio 13.5.1 Mac
Powerful PHP integrated development environment
