vue3之css手写类柱状图

发布于:2025-02-21 ⋅ 阅读:(25) ⋅ 点赞:(0)
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>