vue3之css手写类柱状图
效果:
核心代码:
<template>
<div class="wrap">
<div class="meals">
<div class="meals_title">
<div class="meals_title_uinit">单位:千元</div>
<div class="meals_title_lengend">
<span>
<span class="icon color1"></span>
<span class="text">新校区食堂</span>
</span>
<span>
<span class="icon color2"></span>
<span class="text">西苑餐厅</span>
</span>
<span>
<span class="icon color3"></span>
<span class="text">东苑餐厅</span>
</span>
</div>
</div>
<div class="meals_chart">
<div class="foodChart">
<div class="foodChart_xaxis">
<p v-for="item in list.splitArray" :key="item">
<span class="scale">{{ item }}{{ list.isPercent ? '%' : '' }}</span>
<span class="line"></span>
</p>
</div>
<div class="foodChart_container">
<div class="foodChart_container_item" v-for="(item, index) in list.dataList" :key="index">
<div class="content">
<div class="content_last">
<span></span>
<div class="content_last_border content_border">
<div class="chart" :style="{
height: (list.domHeight * item.newCampusValue) / list.splitArray[0] + 'px',
}">
<span class="circle"></span>
</div>
</div>
<span></span>
</div>
<div class="content_current">
<span></span>
<div class="content_current_border content_border">
<div class="chart" :style="{
height: (list.domHeight * item.xiYuanValue) / list.splitArray[0] + 'px',
}">
<span class="circle"></span>
</div>
</div>
<span></span>
</div>
<div class="content_next">
<span></span>
<div class="content_next_border content_border">
<div class="chart" :style="{
height: (list.domHeight * item.dongYuanValue) / list.splitArray[0] + 'px',
}">
<span class="circle"></span>
</div>
</div>
<span></span>
</div>
</div>
<div class="yaxis">{{ item.title }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive, onMounted } from 'vue';
const list = reactive({
dataList: [
{
title: '早餐',
newCampusValue: 5,
xiYuanValue: 11,
dongYuanValue: 12.5,
},
{
title: '中餐',
newCampusValue: 4,
xiYuanValue: 11,
dongYuanValue: 6,
},
{
title: '晚餐',
newCampusValue: 10,
xiYuanValue: 7,
dongYuanValue: 3,
},
],
splitArray: [15, 12, 9, 6, 3, 0],
domHeight: 173,
// 是否显示%
isPercent: false,
});
const getIntervalNumber = () => {
// 返回数据值的数组
const numberList: any = [];
list.dataList.forEach((item: any) => {
numberList.push(item.newCampusValue);
numberList.push(item.xiYuanValue);
numberList.push(item.dongYuanValue);
});
// 求出数组中的最大值
let maxNumber = Math.max(...numberList);
if (maxNumber % 5) maxNumber = 5 - (maxNumber % 5) + maxNumber;
// 刻度间距
const intervalNumber = maxNumber / 5;
// 得到最后的刻度数组
list.splitArray = list.splitArray.map((item: any, index: number) => maxNumber - intervalNumber * index);
};
onMounted(() => {
getIntervalNumber();
});
</script>
<style lang="scss" scoped>
p {
margin: 0;
}
.wrap {
box-sizing: border-box;
width: 440px;
height: 300px;
padding: 10px;
background-color: rgb(1 22 38 / 100%);
.meals {
height: 244px;
margin: 18px 0 0 12px;
font-size: 14px;
color: rgb(236 236 237 / 100%);
&_title {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding-right: 16px;
margin-bottom: 20px;
&_uinit {
font-size: 14px;
}
&_lengend {
display: flex;
justify-content: space-between;
height: 13px;
> span {
margin-right: 8px;
&:nth-last-of-type(1) {
margin-right: 0;
}
}
.icon {
display: inline-block;
width: 8px;
height: 8px;
margin-right: 4px;
border-radius: 2px;
&.color1 {
background-color: rgb(144 224 112 / 100%);
}
&.color2 {
background-color: rgb(224 191 112 / 100%);
}
&.color3 {
background-color: rgb(112 215 224 / 100%);
}
}
}
}
&_chart {
width: 100%;
height: 224px;
.foodChart {
position: relative;
width: 100%;
height: 100%;
&_xaxis {
position: relative;
top: -2px;
display: flex;
flex-direction: column;
justify-content: space-between;
height: 88%;
margin-right: 8px;
font-family: DIN;
font-size: 14px;
color: #868c97;
> p {
display: flex;
align-items: center;
justify-content: space-between;
margin: 0;
line-height: 14px;
.scale {
width: 20px;
margin-right: 10px;
text-align: right;
}
.line {
width: 390px;
height: 1px;
background-color: rgb(230 238 255 / 14%);
}
&:nth-last-of-type(1) {
.line {
background-color: rgb(230 238 255 / 54%);
}
}
}
}
&_container {
position: absolute;
top: 2%;
left: 6%;
display: flex;
justify-content: space-around;
width: 94%;
height: 100%;
&_item {
width: 58px;
height: 100%;
text-align: center;
.content {
box-sizing: border-box;
display: flex;
align-items: flex-end;
justify-content: center;
width: 58px;
height: 88%;
padding-bottom: 24%;
.content_border {
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: flex-end;
width: 100%;
height: 95%;
overflow: hidden;
}
&_last,
&_current,
&_next {
display: flex;
flex-direction: column;
justify-content: space-between;
width: 12px;
height: 100%;
margin-right: 9px;
> span {
width: 12px;
height: 3px;
}
}
&_last {
> span {
background-color: #58e168;
}
&_border {
border: 1px solid rgb(144 224 112 / 34%);
}
.chart {
position: relative;
left: 2px;
width: 6px;
height: 50px;
background: linear-gradient(rgb(144 224 112 / 0%), rgb(144 224 112 / 72%));
> .circle {
background-color: #58e168;
}
}
}
&_next {
> span {
background-color: #00d8e1;
}
&_border {
border: 1px solid rgb(112 215 224 / 34%);
}
.chart {
position: relative;
left: 2px;
width: 6px;
height: 50px;
background: linear-gradient(rgb(112 215 224 / 0%), rgb(112 215 224 / 72%));
> .circle {
background-color: #00d8e1;
}
}
}
&_current {
> span {
background-color: #ecc06b;
}
&_border {
border: 1px solid rgb(224 191 112 / 34%);
}
.chart {
position: relative;
left: 2px;
width: 6px;
height: 50px;
background: linear-gradient(rgb(224 191 112 / 0%), rgb(224 191 112 / 72%));
> .circle {
background-color: #ecc06b;
}
}
}
.circle {
position: absolute;
top: 0;
left: -1px;
width: 8px;
height: 12px;
}
}
.yaxis {
position: relative;
top: -8px;
left: -5px;
font-size: 14px;
color: #868c97;
}
}
}
}
}
}
}
</style>