157 lines
2.8 KiB
Vue
157 lines
2.8 KiB
Vue
<template>
|
|
<div class="chat-loading">
|
|
<div class="loading-container flex flex-column flex-align-center">
|
|
<img src="@/assets/images/chat_loading.png" alt="Loading" class="loading-image" />
|
|
<!-- 阴影效果 -->
|
|
<div class="loading-shadow"></div>
|
|
<div class="loading-text" ref="textBox">
|
|
<span>{{ text }}</span>
|
|
<div class="loading-dot" ref="dotBox"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onMounted, watch, reactive, toRefs, nextTick, ref } from "vue";
|
|
import { gsap, TweenMax } from "gsap";
|
|
|
|
|
|
// 这个组件只负责显示loading动画
|
|
|
|
// 定义组件props类型
|
|
const props = defineProps({
|
|
title: {
|
|
type: String,
|
|
default: 'Analyzing the Outfit...'
|
|
}
|
|
})
|
|
|
|
const dotBox = ref(null)
|
|
const textBox = ref(null)
|
|
let tl1 = null;
|
|
const text = ref('')
|
|
|
|
watch(() => props.title, (newVal, oldVal) => {
|
|
if (newVal !== oldVal) {
|
|
destroyAnimation()
|
|
fadeOut(newVal)
|
|
}
|
|
})
|
|
|
|
function fadeOut(newVal) {
|
|
gsap.to(textBox.value,.5, {
|
|
opacity: 0,
|
|
duration: 1,
|
|
ease: "power2.in",
|
|
onComplete: () => {
|
|
// 消失后更换文字
|
|
text.value = newVal
|
|
// 然后出现
|
|
fadeIn();
|
|
}
|
|
});
|
|
}
|
|
|
|
// 出现动画
|
|
function fadeIn() {
|
|
gsap.to(textBox.value,.5, {
|
|
opacity: 1,
|
|
duration: 1,
|
|
onComplete:()=>{
|
|
setTl1();
|
|
}
|
|
});
|
|
}
|
|
|
|
const setTl1 = ()=>{
|
|
nextTick(()=>{
|
|
let el = dotBox.value
|
|
let width = el.offsetWidth + el.parentElement.offsetWidth
|
|
let time = el.parentElement.offsetWidth / el.offsetWidth / 2
|
|
console.log(time,width)
|
|
tl1 = gsap.timeline();
|
|
tl1.to(el,time,
|
|
{
|
|
ease: "power1.in",
|
|
left:width,
|
|
onComplete:()=>{
|
|
setTimeout(() => {
|
|
tl1.restart()
|
|
}, 1000)
|
|
}
|
|
},
|
|
)
|
|
tl1.progress(0);
|
|
})
|
|
}
|
|
|
|
function destroyAnimation() {
|
|
if (tl1) {
|
|
tl1.progress(0);
|
|
tl1.kill();
|
|
tl1 = null;
|
|
}
|
|
}
|
|
onMounted(() => {
|
|
text.value = props.title
|
|
setTl1()
|
|
})
|
|
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
.chat-loading {
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
.loading-container {
|
|
position: relative;
|
|
}
|
|
|
|
.loading-image {
|
|
width: 36.4rem;
|
|
height: 36.4rem;
|
|
animation: rotate 1s linear infinite;
|
|
}
|
|
|
|
.loading-shadow {
|
|
width: 19.6rem;
|
|
height: 5.2rem;
|
|
background-color: #d9d9d9;
|
|
border-radius: 50%;
|
|
filter: blur(35.5px);
|
|
margin-top: 5.1rem;
|
|
margin-bottom: 6.4rem;
|
|
}
|
|
|
|
.loading-text{
|
|
font-family: 'satoshiRegular';
|
|
font-size: 4.8rem;
|
|
letter-spacing: 0.02em;
|
|
line-height: 124%;
|
|
position: relative;
|
|
}
|
|
|
|
.loading-dot{
|
|
height: 100%;
|
|
aspect-ratio: 1.2/1;
|
|
background: radial-gradient(ellipse 150% 150% at center, #ffffff,rgba(255,255,255,.4), transparent);
|
|
border-radius: 50%;
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 0;
|
|
transform: translate(-100%, -50%);
|
|
}
|
|
|
|
@keyframes rotate {
|
|
from {
|
|
transform: rotate(0deg);
|
|
}
|
|
to {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
</style>
|