vue 数据可视化大屏项目开发中,有一个类似echarts柱状图的效果实例,不过和柱状图有一些区别,所以使用的html+css+js的方式来写的。
效果要求
柱状图组件按照百分比占比来表示长度,为了避免数字占比小而无法装下数字,设置了一个最小宽度。
左下角有说明标签,标签点击后可以置灰以及点亮并且上面的柱状图对应的部分隐藏或显示。
另外柱状图可以设置高亮闪烁效果
效果演示
更多进度条效果实例
使用实例
<itemc2 @getPop="popList" @getData="getData($event,list3)" class="wow fadeInDown" data-wow-delay="0.9s"
:list="newList3"></itemc2>
data() {
return {
list: [{
title: '固定器具',
num: 300000,
color: 'rgba(49, 197, 222, 0.7)',
color2: 'rgba(75, 122, 250, 0.7)',
progress: 0,
isSelected: true,
flicker: false
}, {
title: '非固定器具',
num: 600000,
color: 'rgba(11, 70, 206, 0.7)',
color2: 'rgba(143, 102, 248, 0.7)',
progress: 0,
isSelected: true,
flicker: false
}],
list2: [{
title: '固定器具',
num: 300000,
color: 'rgba(49, 197, 222, 0.7)',
color2: 'rgba(75, 122, 250, 0.7)',
progress: 0,
isSelected: true,
flicker: false
}, {
title: '固定器具借出',
num: 600000,
color: 'rgba(11, 70, 206, 0.7)',
color2: 'rgba(143, 102, 248, 0.7)',
progress: 0,
isSelected: true,
flicker: false
}, {
title: '非固定器具',
num: 600000,
color: 'rgba(255, 149, 149, 0.7)',
color2: 'rgba(244, 33, 33, 0.7)',
progress: 0,
isSelected: true,
flicker: true
}],
list3: [{
title: '非固定器具库存',
num: 300000,
color: 'rgba(49, 197, 222, 0.7)',
color2: 'rgba(75, 122, 250, 0.7)',
progress: 0,
isSelected: true,
flicker: false
}, {
title: '非固定器具借出',
num: 300000,
color: 'rgba(11, 70, 206, 0.7)',
color2: 'rgba(143, 102, 248, 0.7)',
progress: 0,
isSelected: true,
flicker: false
}, {
title: '异常数',
num: 300000,
color: 'rgba(255, 149, 149, 0.7)',
color2: 'rgba(244, 33, 33, 0.7)',
progress: 0,
isSelected: true,
flicker: true
}],
}
},
computed: {
newList1: function () {
var list = this.list
var total = 0
list.forEach((type) => {
if (type.isSelected) {
total += type.num
}
});
list.forEach((type) => {
if (type.isSelected) {
type.progress = (type.num / total) * 100
} else {
type.progress = 0
}
});
return list
},
newList2: function () {
var list = this.list2
var total = 0
list.forEach((type) => {
if (type.isSelected) {
total += type.num
}
});
list.forEach((type) => {
if (type.isSelected) {
type.progress = (type.num / total) * 100
} else {
type.progress = 0
}
});
return list
},
newList3: function () {
var list = this.list3
var total = 0
list.forEach((type) => {
if (type.isSelected) {
total += type.num
}
});
list.forEach((type) => {
if (type.isSelected) {
type.progress = (type.num / total) * 100
} else {
type.progress = 0
}
});
return list
},
},
methods: {
getData(e, list) {
console.log(e)
console.log(list)
list[e].isSelected = !list[e].isSelected
},
},
组件代码
<template>
<div class="centerItem">
<div class="centerItem0">
<div @click="getPop(index)" :class="{flicker:item.flicker,flicker2:item.flicker&&isFlicker}" class="centerItem1"
v-if="item.isSelected"
:style="'background: linear-gradient(0deg,'+item.color+' 0%, '+item.color2+' 100%);'+'width:'+item.progress+'%'"
v-for="(item,index) in list" :key="index">
<numcard :num="item.num|getArea"></numcard>
</div>
</div>
<div class="centerItem2">
<div class="centerItem2s" @click="getIndex(index)" v-for="(item,index) in list" :key="index">
<div class="centerItem2s1" v-if="item.isSelected">
<span :style="'background: linear-gradient(0deg,'+item.color+' 0%, '+item.color2+' 100%);'"></span>
<p>{{ item.title }} {{ item.num|getArea }}</p>
</div>
<div class="centerItem2s1" v-else>
<span style="background: #999"></span>
<p style="color:#999">{{ item.title }} {{ item.num|getArea }}</p>
</div>
</div>
</div>
</div>
</template>
<script>
import numcard from "../../../../../components/numcard.vue";
export default {
data() {
return {}
},
props: {
list: [],
isFlicker: true
},
components: {
numcard
},
computed: {},
mounted() {
new this.$wow.WOW().init()
var that = this;
that.time = window.setInterval(() => {
setTimeout(() => {
that.isFlicker = !that.isFlicker
}, 0)
}, 1500)
},
methods: {
getIndex(index) {
this.$emit('getData', index)
},
getPop(index) {
this.$emit('getPop', index)
},
},
filters: {
getArea: function (area) {
return String(area).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
},
watch: {}
}
</script>
<style lang="scss" scoped>
.flicker {
box-shadow: 0px 0px 10px 1px red;
}
.flicker2 {
box-shadow: 0px 0px 10px 5px red;
}
.centerItem {
width: 100%;
position: relative;
height: 160px;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
flex-direction: column;
align-content: flex-start;
.centerItem0 {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
align-content: flex-start;
position: relative;
width: 100%;
height: calc(100% - 50px);
.centerItem1 {
width: 100%;
height: 100%;
position: relative;
display: flex;
min-width: 10%;
transition: all 1s;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
align-content: flex-start;
font-size: 42px;
font-family: PangMenZhengDao;
font-weight: 400;
color: #FFFFFF;
p {
font-size: 42px;
font-family: PangMenZhengDao;
font-weight: 400;
color: #FFFFFF;
::v-deep(.numCard) {
font-size: 42px;
font-family: PangMenZhengDao;
font-weight: 400;
color: #FFFFFF;
}
}
}
}
.centerItem2 {
display: flex;
justify-content: flex-start;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
align-content: flex-start;
width: 100%;
height: 50px;
.centerItem2s {
display: flex;
justify-content: flex-start;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
align-content: flex-start;
cursor: pointer;
span {
width: 10px;
height: 10px;
background: linear-gradient(0deg, #31C5DE 0%, #4B7AFA 100%);
margin-right: 7px;
}
p {
font-size: 14px;
font-family: PingFang;
font-weight: 500;
color: #B2CBDA;
margin-right: 40px;
}
}
.centerItem2s1 {
display: flex;
justify-content: flex-start;
align-items: center;
flex-wrap: nowrap;
flex-direction: row;
align-content: flex-start;
}
}
}
</style>