Project background

Beijing 8pm is the content IP that the content platform focuses on. Through the high-quality content form, users can understand the product in a subtle way, and establish the mind and cognition of the content that JINGdong started to play. At present, this project has been iterated to the fifth phase. The background image on the front page changes as the timeline drags to different cards:

Starting from issue 5, we have adopted a new interactive way to give users better experience feedback, that is, the slide screen, you can take a look at the effect:

To achieve the slide screen effect

1. Slide left and right to switch the background image, and make corresponding processing in critical state (slide to the first picture can not slide left, the last picture can not slide right). 2

In order to implement the above requirements, three issues need to be considered:

  • What kind of layout should be used for groups of images?
  • How to achieve coverage dynamic effect when sliding switch?
  • How to deal with critical state and critical extension state?

What type of layout should be used for groups of images

Let’s assume we have a bunch of background images like this. The easiest way to achieve this cascading effect is to set z-Index. Swiping from right to left is like drawing the top card from the top of a deck, so we can use position: Absolute combine with z-index to get the basic layout:

<template> <div class="card-list"> <div class="card-wrapper" v-for="(item, index) in cardsList" :style="{zIndex: cardsList.length - index}" > <div class="card-img" :style="{backgroundImage: 'url('+ item +')'}" ></div> </div> </div> </template> <script> import bg1 from '.. /img/bg1.jpg' import bg2 from '.. /img/bg2.jpg' import bg3 from '.. /img/bg3.jpg' export default { data() { return { cardsList: [ bg1, bg2, bg3 ] } } } </script>Copy the code

How to achieve coverage dynamic effect when sliding switch

Control the displacement of the current card on the X-axis through translate3D (x, Y, Z). When the current card moves left from the screen when sliding from right to left, set a critical point for sliding, beyond which it will be determined to slide to the next picture. Otherwise return to the initial position:

<template> <div class="card-list"> <div class="card-wrapper" v-for="(item, index) in cardsList" :style="{zIndex: cardsList.length - index}" @touchstart="cardTouchStart" @touchmove="cardTouchMove" @touchend="cardTouchEnd" > // </div> </template> <script> import bg1 from '.. /img/bg1.jpg' import bg2 from '.. /img/bg2.jpg' import bg3 from '.. /img/bg3.jpg' export default {data() {return {cardsList: [bg1, bg2, bg3], // Use a touch object to store some attributes for subsequent operations: {} } }, methods: {cardTouchStart(e) {this.touch. Initiated = true // Start sliding the X-axis coordinates this.touch. StartX = e.touches[0].pagex This.touch. DeltaX = 0}, cardTouchMove(e) {if (! This.touch. Initiated) {return}, cardTouchEnd(e) {this.touch. Initiated = false}}} </script>Copy the code

The default initialization position is translate3D (0, 0, 0), and the maximum displacement of the current card out of the screen should be exactly the screen width. This allows the card to immediately return to the visible area of the screen when swiped backwards, so our code can handle this:

<script> import bg1 from '.. /img/bg1.jpg' import bg2 from '.. /img/bg2.jpg' import bg3 from '.. /img/bg3.jpg' const transform = 'transform' // screen width let clientWidth export default {data() {return {cardsList: [ bg1, bg2, bg3 ], touch: {}, nodeList: [], imgNodeList: [], selectIndex: 0}}, Created () {enclosing $nextTick (() = > {clientWidth = document. DocumentElement. ClientWidth / / array - like - the object is transformed into an array this.nodeList = Array.from(document.getElementsByClassName('card-wrapper')) this.imgNodeList = Array. The from (document. GetElementsByClassName (' card - img)) / / initializes the card position enclosing initCards ()})}, the methods: {cardTouchStart(e) {this.touch. Initiated = true // Start sliding the X-axis coordinates this.touch. StartX = e.touches[0].pagex This.initcards ()}, cardTouchMove(e) {if (! This.touch. Initiated) {return}, cardTouchEnd(e) {this.touch. Initiated = false}, initCards() { this.nodeList.forEach((item, index) => { if (index >= this.selectIndex) { item.style[transform] = `translate3d(0, 0, 0)` this.imgNodeList[index].style[transform] = `translate3d(0, 0, 0)` } else { item.style[transform] = `translate3d(-${clientWidth}px, 0, 0)` this.imgNodeList[index].style[transform] = `translate3d(${clientWidth}px, 0, 0)` } }) } } } </script>Copy the code

There are a few points to explain about the initCards method. There are several videos in each phase of the project at 8pm for the first time, and a video will be updated at 8pm every day, so a variable selectIndex is used to mark which video should be cut today. A card with an index less than selectIndex will be removed from the screen, while a card with an index greater than selectIndex will be invisible due to z-index. Second, to achieve the effect of the next card overwriting the current card when sliding, we have used a very clever way: Dislocation, that is, when the div wrapped in the card slides left out of the screen due to finger sliding, we control the card itself to produce a displacement of the same distance to the right, so as to give the illusion that the card is not moving. The same is true for moving right, as shown in the previous figure intuitively:

Render this effect with code:

CardTouchMove (e) {if (! This.touch. DeltaX = e.touches[0].pagex - this.touch. StartX if (this.touch. DeltaX >= 0) {// swipe right} else {// swipe left}}, // omit unwanted codeCopy the code

Take right slide as an example to give the general processing process:

cardTouchMove(e) { if (! This.touch. DeltaX = e.touches[0].pagex - this.touch. StartX This.touch. Offset = -clientWidth + this.touch. DeltaX if (this.touch. DeltaX >= 0) {if (this.selectIndex === 0) { } else {// slide right this.nodelist [this.selectIndex]. Style [transform] = 'translate3d(0, 0, 0)` this.imgNodeList[this.selectIndex].style[transform] = `translate3d(0, 0, This. NodeList [this.selectIndex - 1]. Style [transform] = 'translate3d(${this.touch. Offset}px, 0, 0)` this.imgNodeList[this.selectIndex - 1].style[transform] = `translate3d(${-this.touch.offset}px, 0, 0) '}} else {// swipe left}}, If (this.touch. DeltaX >= 0) {if (this.selectIndex === 0) {// the first one, } else {this.nodelist [this.selectIndex-1]. Style [transform] = 'translate3d(0, 0, 0)` this.imgNodeList[this.selectIndex - 1].style[transform] = `translate3d(0, 0, 0)` } } }Copy the code

Look at the initial results

How to deal with critical state and critical extension state

Actually processed above a critical state, that is, when sliding to the first or the last one don’t do processing, there is also a critical state, assuming that this time our fingers sliding from left to right, and we as the width of the slip has gone beyond the width of the screen, this time for the user experience of the best treatment is to continue to pull out the card, Also swipe to the right to give an example of processing:

// Swipe right this.nodelist [this.selectIndex]. Style [transform] = 'translate3d(0, 0, 0)` this.imgNodeList[this.selectIndex].style[transform] = `translate3d(0, 0, 0) 'if (this.touch. Offset < 0) {this.nodelist [this.selectIndex-1]. Style [transform] = `translate3d(${this.touch.offset}px, 0, 0)` this.imgNodeList[this.selectIndex - 1].style[transform] = `translate3d(${-this.touch.offset}px, 0, } else {this.nodelist [this.selectIndex-1]. Style [transform] = 'translate3d(0, 0, 0)` this.imgNodeList[this.selectIndex - 1].style[transform] = `translate3d(0, 0, 0)` this.selectIndex -= 1 this.touch.startX = e.touches[0].pageX }Copy the code

Here, I set 20% of the screen width. If the user does not swipe past this threshold, the card will return to its original position. If the user does not swipe past this threshold, the card will slide to the next one:

If (this.touch. DeltaX < this.touch. Threshold) {// return to the origin} else {// perform sliding logic}Copy the code

Optimal point

So far the code example above just gives the effect of the most basic, card is also used in the actual project to smooth transition, here recommend using tween. Js ES6 implementation version ES6 – tween, in addition, the actual project we deal with touch events when it is very important is dealing with multiple sliding, here to provide a comparison of ideas: Use the changedTouches object provided in the official TouchEvent API to find the touches that have changed, and compare the identifier property of those touches to find the one that touched the screen in the first place. Ignore the events of other touches.