138 lines
3.5 KiB
Vue
138 lines
3.5 KiB
Vue
<template>
|
|
<div class="training-products-container">
|
|
<div class="products-section">
|
|
<div class="products-grid">
|
|
<div class="product-card" v-for="(item, index) in products" :key="index">
|
|
<div class="icon">
|
|
<img :src="item.icon" :alt="item.name" />
|
|
</div>
|
|
<div class="info flex">
|
|
<div class="label">{{ item.name }}</div>
|
|
<div class="value">{{ animatedValues.products[index] }}<span class="ge ml10">个</span> </div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from 'vue';
|
|
|
|
const products = ref([
|
|
{
|
|
name: '培训机构:',
|
|
count: 5,
|
|
icon: require('@/assets/images/2.png')
|
|
},
|
|
{
|
|
name: '企业:',
|
|
count: 23,
|
|
icon: require('@/assets/images/1.png')
|
|
},
|
|
{
|
|
name: '高校:',
|
|
count: 7,
|
|
icon: require('@/assets/images/3.png')
|
|
},
|
|
|
|
]);
|
|
|
|
const animatedValues = ref({
|
|
products: [0, 0, 0, 0]
|
|
});
|
|
|
|
const animateNumber = (startValue, endValue, duration, updateCallback) => {
|
|
const startTime = Date.now();
|
|
const endTime = startTime + duration;
|
|
|
|
const animate = () => {
|
|
const currentTime = Date.now();
|
|
const progress = Math.min((currentTime - startTime) / duration, 1);
|
|
const currentValue = Math.floor(startValue + (endValue - startValue) * progress);
|
|
|
|
updateCallback(currentValue);
|
|
|
|
if (progress < 1) {
|
|
requestAnimationFrame(animate);
|
|
}
|
|
};
|
|
|
|
requestAnimationFrame(animate);
|
|
};
|
|
|
|
onMounted(() => {
|
|
products.value.forEach((item, index) => {
|
|
animateNumber(0, item.count, 2000, (value) => animatedValues.value.products[index] = value);
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@mixin textColor($color1, $color2) {
|
|
background-image: linear-gradient(to top, $color1 0%, $color2 50%);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
}
|
|
.training-products-container {
|
|
padding: 0 24px;
|
|
min-height: 100vh;
|
|
color: #fff;
|
|
font-family: "YSBTH";
|
|
.products-section {
|
|
border-radius: 12px;
|
|
.products-grid {
|
|
.product-card {
|
|
border-radius: 8px;
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 10px;
|
|
margin-left: 30px;
|
|
height: 180px;
|
|
&:hover {
|
|
transform: translateY(-4px);
|
|
border-color: rgba(24, 144, 255, 0.6);
|
|
box-shadow: 0 0 30px rgba(24, 144, 255, 0.25);
|
|
}
|
|
|
|
.icon {
|
|
width: 188px;
|
|
margin-right: 16px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: contain;
|
|
}
|
|
}
|
|
|
|
.info {
|
|
display: flex;
|
|
align-items: center;
|
|
.label {
|
|
font-size: 3vw;
|
|
color: #8cc8ff;
|
|
letter-spacing: 4px;
|
|
@include textColor(#003D63,#ffffff);
|
|
margin-right: 30px;
|
|
margin-left: 30px;
|
|
}
|
|
|
|
.value {
|
|
letter-spacing: 4px;
|
|
font-size: 2.5vw;
|
|
font-weight: bold;
|
|
@include textColor(#8cc8ff,#ffffff);
|
|
}
|
|
.ge{
|
|
font-size: 2.2vw;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style> |