数据可视化大屏项目开发中,经常会需要需要展示进度条排行榜的效果。今天我们来整理一下其实现的思路。
获取占比
一般后端返回数据后,前端需要根据数值自行计算占比,占比方式有两种,一种是所有数据累加占比,一种是最大值占比。这就需要我们获取数组中的最大值,以及获取数据中的总数,然后我们根据需要来判断使用哪种方式。
获取数组中的最大值
getMaxNumByKey(list, key) {
var maxNum = list[0][key];
list.forEach((type) => {
if (type[key] > maxNum) {
maxNum = type[key]
}
});
return maxNum
},
获取数组总和
getTotalByKey(list, key) {
var total = 0;
list.forEach((type) => {
total = total + type[key]
});
return total
},
排序
后端返回的数据可能并不是按照我们需要的排序返回的,所以前端需要对数据进行计算,按照需求,让其按照从小到大或者从大到小的方式进行排序.
从小到大排序
sortKey(array, key) {
var list = array.sort(function (a, b) {
var x = a[key];
var y = b[key];
return ((x < y) ? -1 : (x > y) ? 1 : 0)
})
return list
},
从大到小排序
sortKey(array, key) {
var list = array.sort(function (a, b) {
var x = a[key];
var y = b[key];
return ((x < y) ? -1 : (x > y) ? 1 : 0)
})
return list.reverse()
},
进度条加载动画效果
为了让进度条更加生动,我们需要给一个进度条加载动画效果。这里我们可以通过gsap补间动画来实现这种动画。我们需要安装gsap 然后引入使用gsap来达到我们想要的效果。
pnpm i gsap
import gsap from 'gsap'
setTimeout(() => {
gsap.to(this.$refs.progressBarInner, {
duration: 1.5, width: this.width + '%', onComplete: () => {
console.log('动画完成')
}
})
}, 1000)
上下滚动效果
数据大屏中一般展示区域是有限的,所以我们可能需要滚动展示,vue3项目中我们可以使用vue3-seamless-scroll
来实现我们想要的效果。
安装vue3-seamless-scroll
pnpm i vue3-seamless-scroll
使用
import {Vue3SeamlessScroll} from "vue3-seamless-scroll";
components: {Vue3SeamlessScroll},
<Vue3SeamlessScroll :step="0.5" :wheel="true" :hover="true" :list="sortList" class="tableBody">
<div class="list">
<div class="listItem" v-for="(item,index) in sortList" :key="index">
<div class="listItemTop">
<div class="listItemTopl">
<span>TOP {{ index + 1 }}</span><span>{{ item.name }}</span>
</div>
<div class="listItemTopr"><span>{{ item.num }}</span></div>
</div>
<div class="listItemBottom">
<progressBar :index="index" :total="total" :item="item"></progressBar>
</div>
</div>
</div>
</Vue3SeamlessScroll>
完整实例代码
父组件
<template>
<div class="itemBodys hideScrollBar">
<Vue3SeamlessScroll :step="0.5" :wheel="true" :hover="true" :list="sortList" class="tableBody">
<div class="list">
<div class="listItem" v-for="(item,index) in sortList" :key="index">
<div class="listItemTop">
<div class="listItemTopl">
<span>TOP {{ index + 1 }}</span><span>{{ item.name }}</span>
</div>
<div class="listItemTopr"><span>{{ item.num }}</span></div>
</div>
<div class="listItemBottom">
<progressBar :index="index" :total="total" :item="item"></progressBar>
</div>
</div>
</div>
</Vue3SeamlessScroll>
</div>
</template>
<script>
import progressBar from './components/progressBar.vue'
import {Vue3SeamlessScroll} from "vue3-seamless-scroll";
export default {
name: "title",
data() {
return {}
},
components: {progressBar, Vue3SeamlessScroll},
props: {
// 类型 0所有数据累加作为基数进行占比计算 1取数据中最大的数值作为基数进行计算
type: {
type: Number,
default() {
return 0;
}
},
list: {
type: Array,
default() {
return [{
name: '测试1',
num: 200,
}, {
name: '测试2',
num: 10,
}, {
name: '测试3',
num: 40,
}, {
name: '测试4',
num: 50,
}, {
name: '测试5',
num: 100,
}, {
name: '测试6',
num: 10,
}, {
name: '测试7',
num: 80,
}, {
name: '测试8',
num: 90,
}];
}
},
},
computed: {
sortList: function () {
return this.sortKey(this.list, 'num')
},
total: function () {
var total = 0
if (this.type == 0) {
total = this.getTotalByKey(this.list, 'num')
} else {
total = this.getMaxNumByKey(this.list, 'num')
}
return total
},
},
watch: {},
mounted() {
},
methods: {
sortKey(array, key) {
var list = array.sort(function (a, b) {
var x = a[key];
var y = b[key];
return ((x < y) ? -1 : (x > y) ? 1 : 0)
})
return list.reverse()
},
getMaxNumByKey(list, key) {
var maxNum = list[0][key];
list.forEach((type) => {
if (type[key] > maxNum) {
maxNum = type[key]
}
});
return maxNum
},
getTotalByKey(list, key) {
var total = 0;
list.forEach((type) => {
total = total + type[key]
});
return total
},
},
}
</script>
<style lang="scss" scoped>
.itemBodys {
position: relative;
width: calc(100% - 35px - 20px);
margin: 0 auto;
height: calc(100% - 20px);
overflow: hidden;
.list {
width: calc(100% - 0px);
display: flex;
justify-content: flex-start;
align-items: center;
flex-wrap: nowrap;
flex-direction: column;
align-content: flex-start;
height: 100%;
.listItem {
font-size: 14px;
display: flex;
justify-content: flex-start;
align-items: flex-start;
flex-wrap: nowrap;
flex-direction: column;
align-content: flex-start;
width: 100%;
.listItemTop {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: nowrap;
width: 100%;
flex-direction: row;
height: 50px;
align-content: flex-start;
.listItemTopl {
display: flex;
justify-content: flex-start;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
align-content: flex-start;
span {
font-size: 16px;
font-family: DIN-Bold;
font-weight: 500;
color: #FFFFFF;
text-shadow: 0 0 10px #3873ad;
margin-right: 20px;
}
}
.listItemTopr {
display: flex;
justify-content: flex-end;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
align-content: flex-start;
font-size: 16px;
font-family: DIN-Bold;
font-weight: 500;
color: #FFFFFF;
text-shadow: 0 0 10px #3873ad;
}
}
.listItemBottom {
width: 100%;
}
}
}
}
</style>
进度条组件
/**
* @Author: 858834013@qq.com
* @Name: progressBar
* @Date: 2023年06月11日10:10:10
* @Desc: 进度条效果
*/
<template>
<div class="progressBar">
<div class="progressBarInner"
ref="progressBarInner"></div>
<div class="line"></div>
</div>
</template>
<script>
import gsap from "gsap";
export default {
name: "progressBarBody",
props: {
item: {
type: Object,
default() {
return {}
}
},
index: {
type: Number,
default() {
return 0
}
},
total: {
type: Number,
default() {
return 0
}
},
},
data() {
return {}
},
computed: {
index2: function () {
return Number(this.index) + 1
},
width: function () {
return (this.item.num / this.total).toFixed(2) * 100
},
},
mounted() {
setTimeout(() => {
gsap.to(this.$refs.progressBarInner, {
duration: 1.5, width: this.width + '%', onComplete: () => {
console.log('动画完成')
}
})
}, 1000)
},
methods: {}
}
</script>
<style lang="scss" scoped>
.progressBar {
width: calc(100% - 0px);
height: 10px;
background: #20385c;
border-radius: 3px;
display: flex;
justify-content: flex-start;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
align-content: flex-start;
position: relative;
.progressBarInner {
height: 10px;
background: linear-gradient(180deg, #04CEF7 0%, #1263FF 100%);
opacity: 1;
width: 0%;
border-radius: 3px;
max-width: calc(100% - 0px);
}
.line {
background: url("./assets/dot.png");
width: 26px;
height: 26px;
opacity: 1;
margin-left: -13px;
}
}
</style>