<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鳥教程(runoob.com)</title>
<!-- <script src="https://unpkg.com/axios/dist/axios.min.js"></script> -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<!-- <link rel="stylesheet" href="https://daneden.github.io/animate.css/animate.min.css"> -->
</head>
<body>
<div class="demo">
<div class="addBtn" @click="addGoods"></div>
<div class="cart">
這是購物車
<div class="ball-wrapper" v-for="(ball, index) of balls" :key="index">
<transition name="drop" @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter">
<div class="ball" v-show="ball.show">
<!--這裏爲了做兩個維度的動畫,因此需要多包一層,外層做Y軸,內層做X軸動畫-->
<div class="inner inner-hook"></div>
</div>
</transition>
</div>
</div>
</div>
</body>
<script>
new Vue ({
el: ".demo",
data() {
return {
balls: [
// 這裏定義了多個ball,是因爲可能同時有多個小球在動畫中(快速點擊多次或者多個商品)
{
show: false
}
],
dropBalls: [] // 在動畫中的小球集合
};
},
methods: {
addGoods(e) {
let el = e.target;
this.balls.forEach(v => {
if (!v.show) {
v.show = true; // 當切換元素的display:block/none時,會觸發vue的動畫
v.el = el; // 將觸發點擊事件的“+”號保定道小球對象上,方便獲取動畫初始時的位置
this.dropBalls.push(v); // 取一個小球加入動畫隊列
return;
}
});
},
beforeEnter(el) {
let count = this.balls.length;
while (count--) {
// 將動畫隊列中的小球,依次處理
let ball = this.balls[count];
if (ball.show) {
let rect = ball.el.getBoundingClientRect(); //拿到點擊的“+”號的位置,這裏不直接取值(我是用的絕對定位,當然可以直接取值)的原因是,商品列表中每個加號的位置是不固定的,如果上下滑動了,這個位置就不確定
let x = rect.left - 70; // 需要偏移的x向距離
let y = -(window.innerHeight - rect.top - 65); // 需要偏移的y向距離
el.style.display = ""; // 當前狀態下,display值爲none,將其置空。
// 這裏需要注意了,小球飛入的動畫分兩個維度,X軸和Y軸,因此
el.style.webkitTransform = `translate3d(0px, ${y}px, 0px)`; // 首先將“ball”Y向移動至“+”好位置
el.style.transform = `translate3d(0px, ${y}px, 0px)`;
// 接着將“inner-hook”X向移動至“+”號處,其實此時外層“ball”的X位置沒有動,但因爲背景色等等樣式只應用於“inner-hook”上,因此,視覺效果上,這個小球是移動到了“+”號的位置
let inner = el.getElementsByClassName("inner-hook")[0];
inner.style.webkitTransform = `translate3d(${x}px, 0, 0)`;
inner.style.transform = `translate3d(${x}px, 0, 0)`;
}
}
},
enter(el) {
/* eslint-disable no-unused-vars */
let rf = el.offsetHeight; // 主動觸發瀏覽器重繪
this.$nextTick(() => {
el.style.webkitTransform = "translate3d(0, 0, 0)"; //接着將小球位置置爲初始值,但css中設置了transition .8s,因此,動畫效果就出來了
el.style.transform = "translate3d(0, 0, 0)";
let inner = el.getElementsByClassName("inner-hook")[0];
inner.style.webkitTransform = "translate3d(0, 0, 0)";
el.style.transform = "translate3d(0, 0, 0)";
});
},
afterEnter(el) {
let ball = this.dropBalls.shift(); //結束後,將這個活動中的小球刪除
if (ball) {
ball.show = false;
el.style.display = "none"; // 並且將其設爲不可見
}
}
}
});
</script>
<style lang="scss" scoped>
.addBtn {
position: absolute;
top: 50px;
right: 50px;
width: 20px;
height: 20px;
background: blue;
border-radius: 50%;
}
.cart {
position: absolute;
left: 30px;
bottom: 30px;
width: 100px;
text-align: center;
height: 50px;
line-height: 50px;
border: 1px solid #ccc;
}
.cart .ball {
position: fixed;
z-index: 100;
left: 70px;
bottom: 45px;
}
.ball.drop-enter-active {
transition: all 0.8s cubic-bezier(0.49, -0.29, 0.75, 0.41);
}
.ball .inner {
width: 20px;
height: 20px;
border-radius: 50%;
background: red;
transition: all 0.8s;
}
</style>
</html>
效果: