useAnimationList.ts
import { ref } from 'vue'
export interface Timedtask {
time: number
method: (...arg: any[]) => void
}
const binaryInsert = (arr: { key: number; time: number }[], value: number) => {
let left = 0
let right = arr.length - 1
let item: { key: number; time: number } = { key: 0, time: 0 }
while (left <= right) {
const mid = Math.floor((left + right) / 2)
if (arr[mid].time === value) {
item = { key: mid, time: value }
arr.splice(mid, 0, item)
return item
} else if (arr[mid].time < value) {
left = mid + 1
} else {
right = mid - 1
}
}
item = { key: left, time: value }
arr.splice(left, 0, item)
return item
}
export const animationList = () => {
const tiskNumArray: { key: number; time: number }[] = []
const tiskArray = new Map<any, Timedtask>()
let intervalIndex: number | null = null
let timeNum = 0
const implementMethod = (item: (typeof tiskNumArray)[0]) => {
const method = tiskArray.get(item)?.method
console.log('执行', item.time, tiskNumArray)
method && method()
tiskArray.delete(item)
tiskNumArray.shift()
if (tiskNumArray.length === 0) clearIntervalM()
}
const clearIntervalM = () => {
timeNum = 0
intervalIndex && clearInterval(intervalIndex)
intervalIndex = null
}
const diffTimeValue = (timeNum: number) => {
if (timeNum >= tiskNumArray[0].time) {
implementMethod(tiskNumArray[0])
tiskNumArray.length > 0 && diffTimeValue(timeNum)
}
}
const initInterval = () => {
if (intervalIndex === null) {
diffTimeValue(timeNum)
intervalIndex = setInterval(() => {
timeNum += 100
diffTimeValue(timeNum)
}, 100)
}
}
const setTimedtask = (time: number, method: (...arg: any[]) => void) => {
let index = 0
let value = { key: index, time }
if (tiskNumArray.length === 0) tiskNumArray.push(value)
else {
value = binaryInsert(tiskNumArray, time)
}
tiskArray.set(value, { time, method })
if (tiskNumArray.length !== 0) initInterval()
}
return {
setTimedtask
}
}
directives.ts
import { animationList } from './useAnimationList'
const { setTimedtask } = animationList()
function callback(entries: any) {
for (let i of entries) {
if (i.isIntersecting) {
let img = i.target
let trueSrc = img.getAttribute('data-src')
img.setAttribute('src', trueSrc)
observer?.unobserve(img)
}
}
}
let observer: IntersectionObserver | null = null
try {
observer = new IntersectionObserver(callback)
} catch (error) {
observer = null
}
export const LazyImg = {
beforeMount(el: Element, binding: { value: boolean }) {
if (!observer || binding.value === false) return
let trueSrc = el.getAttribute('src')
el.setAttribute('src', null as unknown as string)
el.setAttribute('data-src', trueSrc as string)
observer.observe(el)
},
beforeUnmount(el: Element) {
observer?.unobserve(el)
}
}
const antmBindingObjs: { [key: number]: antmType } = {}
function antmCallback(entries: any) {
for (let i of entries) {
if (i.isIntersecting) {
antmObserver?.unobserve(i.target)
const uuid: number = Number(i.target.getAttribute('antm-id'))
if (antmBindingObjs[uuid].child_node === true) {
const flag = typeof antmBindingObjs[uuid].time === 'number'
i.target.childNodes.forEach((childNode: any, index: number) => {
setTimedtask(
flag
? (antmBindingObjs[uuid].time as number) * ++index
: (antmBindingObjs[uuid].time as number[])[index],
() => {
childNode.classList.add(antmBindingObjs[uuid].after)
if (antmBindingObjs[uuid].opacity) childNode.style.opacity = 1
}
)
})
} else
setTimedtask((antmBindingObjs[uuid].time as number[])[0], () => {
i.target.classList.add(antmBindingObjs[uuid].after)
if (antmBindingObjs[uuid].opacity) i.target.style.opacity = 1
})
}
}
}
let antmObserver: IntersectionObserver | null = null
try {
antmObserver = new IntersectionObserver(antmCallback)
} catch (error) {
antmObserver = null
}
const initBinding = (value: number | antmType) => {
const initObj = {
time: [0],
child_node: false,
before: '',
after: 'animation_start',
opacity: true
}
if (typeof value === 'number') {
initObj.time = [value]
return initObj
} else
return {
...initObj,
...value,
time: typeof value.time === 'number' && !value.child_node ? [value.time] : value.time
}
}
export interface antmType {
time: number | number[]
child_node?: boolean
before?: string | string[]
after?: string | string[]
opacity?: boolean
}
let uuid: number = 0
export const antm = {
beforeMount(el: Element, binding: { value: number | antmType }) {
const bindingM = initBinding(binding.value)
antmBindingObjs[uuid] = bindingM
if (bindingM.child_node)
bindingM.opacity &&
el.childNodes.forEach((childNode: any) => {
childNode.style.opacity = 0
})
else bindingM.opacity && (el.style.opacity = 0)
el.setAttribute('antm-id', uuid as unknown as string)
++uuid
antmObserver?.observe(el)
},
beforeUnmount(el: Element) {
antmObserver?.unobserve(el)
}
}
transition.scss
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.28s;
}
.fade-enter,
.fade-leave-active {
opacity: 0;
}
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all 0.3s;
}
.fade-transform-enter-from {
opacity: 0;
transform: translateX(-30px);
}
.fade-transform-leave-to {
opacity: 0;
transform: translateX(30px);
}
.breadcrumb-enter-active {
transition: all 0.4s;
}
.breadcrumb-leave-active {
position: absolute;
transition: all 0.3s;
}
.breadcrumb-enter-from,
.breadcrumb-leave-active {
opacity: 0;
transform: translateX(20px);
}
.outer-most .el-collapse-transition-leave-active,
.outer-most .el-collapse-transition-enter-active {
transition: 0.2s all ease-in-out !important;
}
.horizontal-collapse-transition {
transition: var(--pure-transition-duration) all !important;
}
@keyframes downUp {
0% {
opacity: 0;
transform: translateY(calc(100% + 400px));
}
30% {
transform: translateY(-30px);
}
60% {
transform: translateY(10px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.animation_start {
animation: downUp 2s;
}
@keyframes animation_left {
0% {
opacity: 0;
transform: translateX(calc(-100% - 400px));
}
100% {
opacity: 1;
transform: translateX(0);
}
}
@keyframes animation_right {
0% {
opacity: 0;
transform: translateX(calc(100% + 400px));
}
100% {
opacity: 1;
transform: translateX(0);
}
}
.animation_left {
animation: animation_left 1s;
}
.animation_right {
animation: animation_right 1s;
}
main
app.directive('lazy', LazyImg)
app.directive('antm', antm)
App.vue
<div v-antm="{time: 100,child_node: true}"></div>