The actual results are as follows:

The specific implementation process is as follows: Because I am doing this for a book application, so many tag names are beginning with book, here to explain. Let’s look at this animation and break it down:

1. Eject the card

2. Card flip animation (difficult point)

3. Fireworks animation (difficult point)

4. Pop up recommended books

The first and fourth pop-up animations are made using the CSS3 keyFrames property.

The eject of an animation is a process of gradually getting bigger and faster to slower. We call it slow to the beginning and fast to the end of the animation.

His implementation uses the ease-In property in the animation and uses the BOTH property for the final state of the animation. The BOTH property is very interesting. It integrates forwards, which means targets being moved retain properties during execution, backwards targets apply their defined values immediately when executing. And retained during animation-delay.

The following CSS code mostly integrates some basic layout Settings and can focus on the &.animation{} section of the code

I animate the pop-up card application for 1 and 4:

.book-card{ position: relative; width: 65%; box-sizing: border-box; border-radius: px2rem(15); background: white; /* Animation-fill-mode: state at the end of the animation (reaching 100%). Values are: Backward (back to the initial state), forward (stop at the final state), None, both. Animations will follow the rules of forwards and backwards, extending animation properties in both directions. */ &.animation { animation: scale .3s ease-in both; @keyframes scale { 0% { transform: scale(0); opacity: 0; } 100% { transform: scale(1); opacity: 1; } } } .book-card-wrapper{ width: 100%; height: 100%; margin-bottom: px2rem(30); @include columnTop; .img-wrapper{ width: 100%; margin-top: px2rem(20); @include center; .img { width: px2rem(90); height: px2rem(130); } } .content-wrapper{ padding: 0 px2rem(20); margin-top: px2rem(20); .content-title{ color: #333; font-weight: bold; font-size: px2rem(18); line-heigth:px2rem(20); max-height: px2rem(40); text-align: center; @include ellipsis2(2) } .content-author{ margin-top: px2rem(10); text-align: center; } .content-category{ color: #999; font-size: px2rem(14); margin-top: px2rem(10); text-align: center; } } .read-btn{ position: absolute; bottom: 0; left: 0; z-index: 1100; width: 100%; border-radius: 0 0 px2rem(15) px2rem(15); padding: px2rem(15) 0; text-align: center; color: white; font-size: px2rem(14); background: $color-blue; }}}Copy the code

I gave 2 card flip animation pop-ups using the animation:

.flap-card-bg { position: relative; width: px2rem(64); height: px2rem(64); border-radius: px2rem(5); background: white; transform: scale(0); opacity: 0; &.animation{ animation: flap-card-move .3s ease-in both; } @keyframes flap-card-move { 0% { transform: scale(0); opacity: 0; } 50% {transform: scale(1.2); opacity: 1; } 75% { transform: scale(.9); opacity: 1; } 100% { transform: scale(1); opacity: 1; } } .flap-card{ width: px2rem(48); height: px2rem(48); @include absCenter; .flap-card-circle{ display: flex; width: 100%; height: 100%; .flap-card-semi-circle{ flex: 0 0 50%; width: 50%; height: 100%; background-repeat: no-repeat; backface-visibility: hidden; } .flap-card-semi-circle-left{ border-radius: px2rem(24) 0 0 px2rem(24); background-position: center right; transform-origin: right; } .flap-card-semi-circle-right{ border-radius: 0 px2rem(24) px2rem(24) 0; background-position: center left; transform-origin: left; } } } .point-wrapper{ z-index: 1500; @include absCenter; .point{ border-radius: 50%; @include absCenter; &.animation { @for $i from 1 to length($moves) { &:nth-child(#{$i}) { @include move($i); } } } } } }Copy the code

I gave 3 smoke bombs to use in the animation:

      .point-wrapper{
        z-index: 1500;
        @include absCenter;
        .point{
          border-radius: 50%;
          @include absCenter;
          &.animation {
            @for $i from 1 to length($moves) {
              &:nth-child(#{$i}) {
                @include move($i);
              }
            }
          }
        }
      }

Copy the code

You can see the flip of those little cards, and the implementation is very complicated, so I take five cards, and I flip them clockwise in a logical way, from light to dark, and then one after the other. So how do you actually do that? See below:

Here is the tag code, bound to pseudo-classes and refs, to manipulate the DOM using the $refs object

  <div class="flap-card" v-for="(item, index) in flapCardList" :key="index"
       :style="{zIndex: item.zIndex}">
         <div class="flap-card-circle">
           <div class="flap-card-semi-circle flap-card-semi-circle-left" :style="semiCircleStyle(item, 'left')" ref="left"></div>
           <div class="flap-card-semi-circle flap-card-semi-circle-right" :style="semiCircleStyle(item, 'right')" ref="right"></div>
         </div>
       </div>
Copy the code

You can see that I’m using a V-for loop to show the images in flapCardList:

export const flapCardList = [
  {
    r: 255,
    g: 102,
    _g: 102,
    b: 159,
    imgLeft: 'url(' + require('@/assets/images/gift-left.png') + ')',
    imgRight: 'url(' + require('@/assets/images/gift-right.png') + ')',
    backgroundSize: '50% 50%',
    zIndex: 100,
    rotateDegree: 0
  },
  {
    r: 74,
    g: 171,
    _g: 171,
    b: 255,
    imgLeft: 'url(' + require('@/assets/images/compass-left.png') + ')',
    imgRight: 'url(' + require('@/assets/images/compass-right.png') + ')',
    backgroundSize: '50% 50%',
    zIndex: 99,
    rotateDegree: 0
  },
  {
    r: 255,
    g: 198,
    _g: 198,
    b: 102,
    imgLeft: 'url(' + require('@/assets/images/star-left.png') + ')',
    imgRight: 'url(' + require('@/assets/images/star-right.png') + ')',
    backgroundSize: '50% 50%',
    zIndex: 98,
    rotateDegree: 0
  },
  {
    r: 255,
    g: 102,
    _g: 102,
    b: 159,
    imgLeft: 'url(' + require('@/assets/images/heart-left.png') + ')',
    imgRight: 'url(' + require('@/assets/images/heart-right.png') + ')',
    backgroundSize: '50% 50%',
    zIndex: 97,
    rotateDegree: 0
  },
  {
    r: 59,
    g: 201,
    _g: 201,
    b: 22,
    imgLeft: 'url(' + require('@/assets/images/crown-left.png') + ')',
    imgRight: 'url(' + require('@/assets/images/crown-right.png') + ')',
    backgroundSize: '50% 50%',
    zIndex: 96,
    rotateDegree: 0
  }
]

Copy the code

Each image is stitched together with left and right parts.

Note the use of pseudo classes to bind the image to its zIndex, appearance order

 :style="{zIndex: item.zIndex}"
Copy the code

The basic layout code is as follows:

.flap-card{ width: px2rem(48); height: px2rem(48); @include absCenter; .flap-card-circle{ display: flex; width: 100%; height: 100%; .flap-card-semi-circle{ flex: 0 0 50%; width: 50%; height: 100%; background-repeat: no-repeat; backface-visibility: hidden; } .flap-card-semi-circle-left{ border-radius: px2rem(24) 0 0 px2rem(24); background-position: center right; transform-origin: right; } .flap-card-semi-circle-right{ border-radius: 0 px2rem(24) px2rem(24) 0; background-position: center left; transform-origin: left; }}}Copy the code

Some methods operate on images:

// When the image is moving, Preparations at the back of the picture also need to do is move to also want to prepare () {const backFlapCard = this. FlapCardList. [this. Back] backFlapCard rotateDegree = 180 backFlapCard._g = backFlapCard - 5 * 9 this.rotate(this.back, 'back') },Copy the code

Color change

      semiCircleStyle(item, dir) {
        return {
          backgroundColor: `rgb(${item.r},${item.g},${item.b})`,
          backgroundSize: item.backgroundSize,
          backgroundImage: dir === 'left' ? item.imgLeft : item.imgRight
        }
      },

Copy the code

The Angle change

rotate(index, type) { const item = this.flapCardList[index] let dom if (type === 'front') { dom = this.$refs.right[index] } else { dom  = this.$refs.left[index] } dom.style.transform = `rotateY(${item.rotateDegree}deg)` dom.style.backgroundColor = `rgb(${item.r},${item._g},${item.b})` },Copy the code

Changes in the properties of the card as it rotates

flapCardRotate() { const frontFlapCard = this.flapCardList[this.front] const backFlapCard = this.flapCardList[this.back]  frontFlapCard.rotateDegree += 10 frontFlapCard._g -= 5 backFlapCard.rotateDegree -= 10 if (backFlapCard.rotateDegree < 90) { backFlapCard._g += 5 } if (frontFlapCard.rotateDegree === 90 && backFlapCard.rotateDegree === 90) { backFlapCard.zIndex += 2 } this.rotate(this.front, 'front') this.rotate(this.back, 'back') if (frontFlapCard.rotateDegree === 180 && backFlapCard.rotateDegree === 0) { this.next() } },Copy the code

The logic of how the card works

      startFlapCardAnimation() {
        this.prepare()
        this.task = setInterval(() => {
         this.flapCardRotate()
        }, this.intervalTime)
      },

Copy the code