需求分析:进入产品列表,点击缩略图旁+号,有个该商品被动态添加到购物车的动画。让我们实现这个动画吧。

如下图:

在这里插入图片描述

通过动图可以发现移动的曲线是蜿蜒曲折的,这就用到了贝塞尔曲线。

他的弧度是这样的。
在这里插入图片描述

贝塞尔曲线链接

实现原理:先创建一个隐藏的圆点在购物车icon那里。当点击+号时,让圆点先移动到当前点击+的位置并让其显示。然后开始动画,让圆点移动回到原来的购物车icon位置并让其隐藏。

使用vue提供的vue动画和贝塞尔曲线来实现移动动画

vue动画详情链接

  1. HTML
<template>
   <div>
       <van-nav-bar
           title="图书战术"
           left-text="返回"
           left-arrow
           @click-left="onClickLeft"
       >
           <template #right>
               <van-icon name="cart-o" size="18" />
               <div class="shop_car_icon">{{ shopCarNum }}</div>
           </template>
       </van-nav-bar>
       <div class="bookmall__list">
           <ul>
               <li class="bookmall__item" v-for="(item, index) in testList" :key="index" @click="goBookUrl(item)">
                   <div class="bookmall__item-left">
                       {{ item }}
                   </div>
                   <div class="bookmall__item-right">
                       <h5>{{ item }} <span>{{ item }}</span></h5>
                       <p><span>分类:</span>{{ item }}<span style="margin-left: 10px;padding: 10px;" class="shopCarAdd" @click.stop="addToShopCars"><van-icon name="add" color="rgb(238, 57, 57"/></span></p>
                       <p class="bookmall__item-right-tag"><span>标签:</span>{{ item }}</p>
                       <p class="bookmall__item-right-sub2">
                           {{ item }}
                       </p>
                   </div>
               </li>
           </ul>
       </div>
       <transition
       @before-enter="beforeEnter"
       @enter="enter"
       @after-enter="afterEnter"
       >
           
           <div class="shop_car_ball" v-show="shopCarBall">
               <div class="linner_ball">
                   <van-icon name="add" color="rgb(238, 57, 57"/>
               </div>
           </div>
           
       </transition>
   </div>
</template>
  1. CSS
.van-nav-bar {
   position: fixed;
   width: 100%;
}
.shop_car_icon {
   position: absolute;
   top: 10px;
   right: -8px;
   font-size: 10px;
   background-color: rgb(238, 57, 57);
   border-radius: 50%;
   text-align: center;
   color: rgb(82, 80, 80);
   line-height: 0;
   display: table-cell;
   vertical-align: middle;
   padding: 8px 2px;
}
.bookmall__list {
   padding-top: 40px;
   .bookmall__item {
       display: flex;
       padding: 10px;
       border-bottom: 1px a #000;
       .bookmall__item-left {
           flex: 25%;
           margin-right: 5px;
           img {
               width: 90px;
           }
       }
       .bookmall__item-right {
           flex: 75%;
           height: 130px;
           overflow: hidden;
           h5 {
               font-size: 16px;
               span {
                   margin-left: 65px;
                   font-weight: 400;
                   font-size: 12px;
                   color: rgb(107, 106, 106);
               }
           }
           p {
               font-size: 13px;
           }
           .bookmall__item-right-tag {
               color: rgb(119, 118, 118);
           }
           
       }
   }
}
.bookmall__item .bookmall__item-right-sub2 ::after {
   content: "...";
   position: absolute;
   right: 0;
   bottom: 0;
}

.shop_car_ball {
   position: fixed;
   top: 10px;
   right: 12px;
   z-index: 9;
   font-size: 16px;
   // transition: all .25s cubic-bezier(0.49, -0.29, 0.75, 90.41);
   /* 贝塞尔曲线 */
   transition: all .25s cubic-bezier(.17, .86, .73, .14);
   .linner_ball {
       transition: all .25s linear;
   }
}

  1. JS
export default {
   data () {
       return {
           shopCarNum: window.localStorage.getItem('shopcarnum') || 0,
           shopCarBall: false,
           shopCarBallEl: null,
           ss: null,
           // demo测试数据
           testList: new Array(1,2,3,4,5)
       }
   },
   methods: {
       addToShopCars (e) {
           this.shopCarBallEl = e.target
           this.shopCarBall = true
       },
       // 动画开始
       beforeEnter (el) {
       	   // 获取元素的大小及其相对于视口的位置
           const dom = this.shopCarBallEl.getBoundingClientRect()
           const offsetX = window.innerWidth - dom.left - 16
           const offsetY = dom.top - 22
   
           el.style.display = ''
           // y轴是曲直向上的,x轴是蜿蜒的向右的
           el.style.transform = `translate3d(0, ${offsetY}px, 0)`
           
           
           const linnerBall = el.querySelector('.linner_ball')
           linnerBall.style.transform = `translate3d(-${offsetX}px, 0, 0)`
       },
       enter (el, done) {
           // 触发重绘,来实现动画的移动过程
           this.ss = document.body.offsetHeight
           el.style.transform = `translate3d(0, 0, 0)`

           const linnerBall = el.querySelector('.linner_ball')
           linnerBall.style.transform = `translate3d(0, 0, 0)`
           el.addEventListener('transitionend', done)
       },
       afterEnter (el) {
           this.shopCarBall = false
           el.style.display = 'none'
           this.shopCarNum++
       }
   },
   watch: {
   	// 监听shopCarNum属性,只考虑新增。新增时储存本地做永久化处理
       shopCarNum (newValue) {
           window.localStorage.setItem('shopcarnum', newValue)
       }
   }
}

这样我们就实现购物车的动态添加demo。

Logo

前往低代码交流专区

更多推荐