先上效果图
使用方法
<addShop ref="addShop" />
import addShop from '@/views/components/addShop'
this.$refs.addschool.init(dom, state)
addShop组件
<template>
<div class="add-school-box">
<transition @before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter">
<div id="cloneBox"
v-show="isShow"></div>
</transition>
<div class="add-school-shop"
:style="{left: isCollapse ? '250px' : '100px'}">
<div class="add-school-shop-num">
{{count.none_publish_count > 999 ? '999+' : count.none_publish_count }}
</div>
<svg-icon icon-class="lanzi"
style="font-size:60px;" />
</div>
</div>
</template>
<script>
import addShop from '@/utils/addShop'
import { mapGetters } from 'vuex'
export default {
data () {
return {
addShop: '',
isShow: false
}
},
computed: {
...mapGetters([
'count'
]),
isCollapse () {
return this.$store.state.app.sidebar.opened
}
},
methods: {
init (dom, state) {
this.addCard = new addShop({
dom,
state,
cloneBoxId: 'cloneBox',
shopClass: 'add-school-shop',
shopAnmationClass: 'dom-all-in'
})
this.addCard.init()
this.isShow = true
},
beforeEnter (el) {
this.addCard.beforeEnter(el)
},
enter (el, done) {
this.addCard.enter(el, done)
},
afterEnter (el) {
this.addCard.afterEnter(el).then(() => {
this.isShow = false
})
}
},
}
</script>
<style lang="scss">
.add-school-shop {
position: fixed;
bottom: 100px;
left: 200px;
width: 100px;
height: 100px;
transition: all 0.4s;
i {
font-size: 100px;
}
}
.add-school-shop-num {
width: 45px;
height: 20px;
font-size: 14px;
color: red;
background: white;
border-radius: 10px;
text-align: center;
line-height: 18px;
border: 1px solid red;
position: absolute;
right: 25px;
top: 6px;
}
.dom-all-in {
animation: mymove 1s infinite;
animation-iteration-count: 1;
}
@keyframes mymove {
0% {
transform: scale(1);
}
50% {
transform: scale(1.2);
}
100% {
transform: scale(1);
}
}
</style>
使用到的addShop方法
export default class addShop {
constructor({ dom, shopClass, state, cloneBoxId, shopAnmationClass }) {
// 点击的dom
this.dom = dom
// 购物车dom
this.shopDom = ""
this.shopClass = shopClass
this.shopAnmationClass = shopAnmationClass
// 判断是添加还是删除
this.state = state
// 复制的dom
this.cloneDiv = ""
// 装复制dom的容器
this.cloneDomBox = ""
this.cloneBoxId = cloneBoxId
this.x = 0
this.y = 0
// 点击dom的信息
this.width = 0
this.height = 0
// 购物车的位置信息
this.shopX = 0
this.shopY = 0
}
init() {
this.shopDom = document.getElementsByClassName(this.shopClass)[0]
this.cloneDomBox = document.getElementById(this.cloneBoxId)
// 初始化被点击dom的信息
const oRectDom = this.dom.getBoundingClientRect()
const { width, height, x, y } = oRectDom
this.width = width
this.height = height
this.x = x
this.y = y
// 克隆点击的dom
this.cloneDiv = this.dom.cloneNode(true)
// 将克隆的元素添加到盒子里
this.cloneDomBox.appendChild(this.cloneDiv)
// 初始化购物车的位置
const oRectShop = this.shopDom.getBoundingClientRect()
this.shopX = oRectShop.x
this.shopY = oRectShop.y
}
beforeEnter(el) {
let top = this.state ? this.y : this.shopY
let left = this.state ? this.x : this.shopX
// 动画入场前,设置元素开始动画的起始位置
el.style.width = this.width + "px"
el.style.height = this.height + "px"
el.style.position = "fixed"
el.style.left = left + "px"
el.style.top = top + "px"
// 动画过程禁止滚动
document.getElementsByTagName("html")[0].style.overflowY = "hidden"
if (!this.state) {
el.style.transform = `scale(0.1)`
el.style.transformOrigin = "0 0"
this.shopDom.classList.add(this.shopAnmationClass)
}
}
// 设置完成动画之后的结束状态
enter(el, done) {
el.offsetWidth
// 结束位置
let enterX = this.x - this.shopX
let enterY = this.shopY - this.y
// 这里要考虑enterX,enterY为负数的情况
let x = this.state ? (enterX > 0 ? '-' + enterX : Math.abs(enterX)) : enterX
let y = this.state ? enterY : (enterY > 0 ? '-' + enterY : Math.abs(enterX))
let scaleNum = this.state ? "0.1" : "1"
el.style.transform = `translate(${x}px,${y}px) scale(${scaleNum})`
el.style.transformOrigin = "0 0"
el.style.transition = "all 1s ease"
// 执行done函数,完成下面钩子函数
setTimeout(() => {
done()
}, 800)
}
// 动画完成之后调用afterEnter函数
afterEnter(el) {
this.shopDom.classList[this.state ? "add" : "remove"](
this.shopAnmationClass
)
return new Promise(resolve => {
setTimeout(() => {
this.cloneDomBox.style = {
position: "fixed",
"z-index": 1000,
background: "white"
}
this.cloneDiv && this.cloneDiv.remove()
this.shopDom.classList.remove(this.shopAnmationClass)
// 动画结束允许滚动
document.getElementsByTagName("html")[0].style.overflowY = "auto"
resolve()
}, 500)
})
}
}