• css实现一个温度计图表


    背景

    👏最近在工作中遇到一个温度计图表,用css写起来超简单~
    🥇文末分享源代码。记得点赞+关注+收藏!

    1.实现效果

    在这里插入图片描述

    2.实现步骤

    2.1 温度计参数

    const obj={
     max: 60,//刻度最大值
     min: 0,//刻度最小值 (默认为0,不为0的情况暂未考虑,可根据文中思路自行修改)
     value: 0,//当前值(<=刻度最大值)
     warn: 35,//预警值(当前值>预警值时,显示图表红色区域)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.2 刻度

    2.2.1 刻度列表

    这里将刻度从0开始分为4等分(其他等分情况暂未考虑,可根据文中思路自行修改),设置的刻度最大值为4的倍数,如60,100等。

    • 计算展示的刻度列表,定义空数组stepList[]
    • 假设刻度最大值为60,4等分之后,没一段的长度为15,即为stepList=[0,15,30,45,60]
    let step = parseInt(刻度最大值) / 4,stepList = [];
    for (let i = 0; i < 5; i++) {
      stepList.unshift(step * i);
    }
    
    • 1
    • 2
    • 3
    • 4
    2.2.2 样式布局

    在这里插入图片描述

    <div class="box-lines">
     <div
        v-for="(item, index) in stepList"
        :key="index"
        class="line-item flex-row j_b"
      >
        <div class="left">{{ item }}</div>
        <div class="right">{{ item }}</div>
      </div>
    </div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.3 温度计表盘

    2.3.1 温度计底盘

    本来是准备自己写表盘背景的,奈何才疏学浅,结果不尽人意,最后让聪明可爱的ui小伙伴切了张背景图和金豆形状的阴影,咱们浅浅看下对比吧~

    👏切图真香啊~👏

    在这里插入图片描述

    2.3.2 温度计线条
    • 写两个伪元素定位在合适的位置

    在这里插入图片描述

    2.4 温度计内容

    2.4.1 温度计圆底高亮(当温度<=0°C)
    • 先画一个圆,背景渐变+box-shadow,设置过渡2.5s,延迟0.8s

    在这里插入图片描述

    width: 60px;
    height: 60px;
    background: linear-gradient(90deg, #fde44d 0%, #e8a901 100%);
    box-shadow: 0px 1px 5px 3px #f4ca2b;
    border-radius: 50%;
    transition: all 2.5s;
    transition-delay: 0.8s;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 画一个宽高稍微大于圆的盒子,设置overflow为hidden,添加伪元素为圆,基于盒子底部bottom为0,水平居中,通过溢出hidden,显示满圆以下状态

    在这里插入图片描述

    • 当温度低于0°C以下,温度计圆底固定为高亮一半(也可以按照度数进行展示,可自行修改)

    在这里插入图片描述

    • 当温度等于0°C,温度计圆底显示满圆

    在这里插入图片描述

    2.4.2 温度计中间高亮(当温度>0°C)
    • 画出中间矩形,背景渐变+box-shadow,设置过渡2.5s,延迟2.2s(等待圆底过渡执行完毕)

    在这里插入图片描述

     width: 100%;
     height: 50px;
     background: linear-gradient(90deg, #fde44d 0%, #e8a901 100%);
     box-shadow: 0px 1px 5px 3px #f4ca2b;
     transition: all 2.5s;
     transition-delay: 2.2s;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 计算当前温度占据刻度最大值的百分比 !
    • 如:当前值为30,最大值为60(最小值固定为0),可得到百分比为30/60 *100%=50%,即中间的高亮区域的高度为总高度的50%
    let currPer = parseInt(当前值) / parseInt(刻度最大值)
    
    • 1
    • 画一个盒子,高度与温度计长条区域的高度一致,将计算出高度的矩形作为子元素。style设置var变量,参数为占据的百分比
    :style="{ '--per': currPer < 1 ? currPer : 1 }"
    
    • 1
    height: calc(var(--per) * 100%);
    
    • 1
    • 当百分比>=94%,设置矩形上半部分为圆角

    在这里插入图片描述

    .br {
      border-radius: 16px 16px 0 0;
     }
    
    • 1
    • 2
    • 3
    2.4.3 温度计预警高亮(当温度>预警值)
    • 画出预警矩形,背景渐变+box-shadow,设置过渡2.5s,延迟2.2s(等待圆底过渡执行完毕)

    在这里插入图片描述

    box-shadow: 0px -5px 5px 0px rgb(231 0 0 / 74%);
    width: 42px;
    height: 60px;
    background: linear-gradient(180deg, #e80000 0%, rgba(254, 100, 100, 0) 100%);
    filter: blur(1px);
    transition: all 2.5s;
    transition-delay: 2.2s;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 计算预警高度百分比
    • 如:当前值45,预警值15,最大值为60(最小值固定为0),超出高度百分比为(45-15)/6*100%=50%
    let warnPer =(parseInt(当前值) - parseInt(预警值)) /parseInt(最大值);
    
    • 1
    • 预警矩形基于中间高亮矩形顶部top为0,水平居中,层级盖在中间矩形之上
    :style="{ '--per': warnPer < 1 ? warnPer : 1 }
    
    • 1
    height: calc(温度计矩形区域总高度 * var(--per));
    
    • 1

    在这里插入图片描述

    • 当中间矩形的百分比>=94%,并且当前值超出预警值,设置预警矩形上半部分为圆角

    在这里插入图片描述

    3.实现代码

    3.1 定义一个温度计组件

    <template>
      <section class="container flex-row j_c">
        <div class="container-box">
          <div class="box-lines">
            <div
              v-for="(item, index) in stepList"
              :key="index"
              class="line-item flex-row j_b"
            >
              <div class="left">{{ item }}</div>
              <div class="right">{{ item }}</div>
            </div>
          </div>
          <div class="box-pan">
            <div class="box-shadow"></div>
            <div
              :class="[
                'bottom-circle',
                show && (data.value >= 0 ? 'active' : 'trans'),
              ]"
            ></div>
            <div class="bottom-center" v-show="data.value >= 0">
              <div
                :class="['active', currPer >= 0.94 && 'br', show && 'trans']"
                :style="{ '--per': currPer < 1 ? currPer : 1 }"
              >
                <div
                  :class="['bottom-warn', currPer >= 0.94 && 'br']"
                  v-show="data.value > data.warn"
                  :style="{ '--per': warnPer < 1 ? warnPer : 1 }"
                ></div>
              </div>
            </div>
          </div>
        </div>
        <div class="container-title">{{ data.value }}°<text>C</text></div>
      </section>
    </template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    <script setup>
    import { onMounted, ref } from "vue";
    const props = defineProps({
      show: {
        type: Boolean,
        default: false, //展示过渡效果
      },
      data: {
        type: Object,
        default: () => {},
        required: true,
      },
    });
    let step = parseInt(props.data.max) / 4,
      stepList = [],
      currPer = parseInt(props.data.value) / parseInt(props.data.max),
      warnPer = 0;
    if (parseInt(props.data.value) > parseInt(props.data.warn)) {
      warnPer =
        (parseInt(props.data.value) - parseInt(props.data.warn)) /
        parseInt(props.data.max);
    }
    for (let i = 0; i < 5; i++) {
      stepList.unshift(step * i);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    <style scoped lang="less">
    .flex-row {
        display       : flex;
        flex-direction: row;
        align-items   : center;
    }
    .j_c {
        justify-content: center;
    }
    .j_b {
        justify-content: space-between;
    }
    .container {
      font-size: 16px;
      color: #ffffff;
      height: 280px;
      width: 300px;
      &-box {
        position: relative;
        .box-pan {
          background: url("xx/xx温度计底盘背景") no-repeat;
          background-size: 100% 100%;
          width: 83px;
          height: 203px;
          position: absolute;
          left: calc(50% - 42px);
          top: -2px;
          z-index: 1;
          &::before {
            content: "";
            width: 4px;
            height: 105px;
            background: linear-gradient(
              90deg,
              rgba(255, 255, 255, 0.34) 0%,
              rgba(255, 255, 255, 0.12) 100%
            );
            border-radius: 2px;
            opacity: 0.31;
            position: absolute;
            right: 35px;
            top: 21px;
            z-index: 13;
          }
          &::after {
            content: "";
            position: absolute;
            left: 30px;
            top: 15px;
            width: 6px;
            height: 119px;
            border-radius: 20px 0 20px 10px;
            background: linear-gradient(
              90deg,
              rgba(255, 255, 255, 0.82) 0%,
              rgba(255, 255, 255, 0) 100%
            );
            z-index: 13;
          }
          .box-shadow {
            position: absolute;
            width: 23px;
            height: 29px;
            background: url("xx/xx温度计底盘金豆阴影") no-repeat;
            background-size: 100% 100%;
            bottom: 32px;
            left: 20px;
            z-index: 13;
          }
          .bottom-circle {
            position: absolute;
            width: 70px;
            height: 0px;
            overflow: hidden;
            bottom: 2px;
            left: calc(50% - 35px);
            filter: blur(1px);
            z-index: 11;
            transition: all 2.5s;
            transition-delay: 0.8s;
            &.trans {
              height: 45px;
            }
            &.active {
              height: 75px;
            }
            &::after {
              content: "";
              width: 60px;
              height: 60px;
              background: linear-gradient(90deg, #fde44d 0%, #e8a901 100%);
              box-shadow: 0px 1px 5px 3px #f4ca2b;
              position: absolute;
              left: calc(50% - 30px);
              bottom: 10px;
              border-radius: 50%;
            }
          }
          .bottom-center {
            width: 36px;
            height: 135px; //满高
            position: absolute;
            bottom: 60px;
            left: calc(50% - 18px);
            filter: blur(1px);
            z-index: 10;
            .active {
              width: 100%;
              height: 0;
              position: absolute;
              left: 0;
              bottom: 0;
              background: linear-gradient(90deg, #fde44d 0%, #e8a901 100%);
              transition: all 2.5s;
              transition-delay: 2.2s;
              .bottom-warn {
                width: 42px;
                height: 0;
                background: linear-gradient(
                  180deg,
                  #e80000 0%,
                  rgba(254, 100, 100, 0) 100%
                );
                filter: blur(1px);
                position: absolute;
                top: 0;
                left: calc(50% - 21px);
                z-index: 11;
                transition: all 2.5s;
                transition-delay: 2.2s;
              }
            }
            .trans {
              height: calc(var(--per) * 100%);
              box-shadow: 0px 1px 5px 3px #f4ca2b;
              .bottom-warn {
                height: calc(135px * var(--per));
                box-shadow: 0px -5px 5px 0px rgba(231, 0, 0, 0.74);
              }
            }
            .br {
              border-radius: 16px 16px 0 0;
            }
          }
        }
        .box-lines {
          .line-item {
            font-size: 12px;
            line-height: 17px;
            margin-bottom: 13px;
            &:last-child {
              margin-bottom: 0;
            }
            .left {
              margin-right: 118px;
              min-width: 25px;
              text-align: right;
              position: relative;
              &::after {
                content: "";
                width: 19px;
                opacity: 0.9;
                height: 1px;
                border-bottom: 1px dashed rgba(255, 255, 255, 0.39);
                position: absolute;
                top: calc(50% - 1px);
                right: -25px;
              }
            }
            .right {
              min-width: 25px;
              text-align: left;
              position: relative;
              &::after {
                content: "";
                width: 19px;
                opacity: 0.9;
                height: 1px;
                border-bottom: 1px dashed rgba(255, 255, 255, 0.39);
                position: absolute;
                top: calc(50% - 1px);
                left: -25px;
              }
            }
          }
        }
      }
      &-title {
        margin-left: 15px;
        font-size: 24px;
        font-family: HuXiaoBo;
        color: #f2af33;
        line-height: 31px;
        position: relative;
        text {
          font-size: 12px;
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199

    3.2 组件的使用

    <template>
    	<Thermometer :show="showAni" :data="thermObjLow" />
    </template>
    
    
    • 1
    • 2
    • 3
    • 4
    import Thermometer from "xx/xx/thermometer.vue";
    const showAni = ref(false),
      thermObjHigh = ref({
        max: 60,
        min: 0,
        value: 60,
        warn: 60,
      });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4.写在最后🍒

    看完本文如果觉得对你有一丢丢帮助,记得点赞+关注+收藏鸭 🍕
    更多相关内容,关注🍥苏苏的bug,🍡苏苏的github,🍪苏苏的码云~
  • 相关阅读:
    Zookeeper特性与节点数据类型详解
    李峋同款爱心代码!跳动的心,给你爱的人一个惊喜!
    MySql主从同步介绍
    FISCO BCOS(二十五)———多机部署
    IEnumerable与IQueryable延迟加载
    【国庆活动】掌握这些SQL核心理念,一起轻松玩转国庆假期
    【数据结构】复习汇总I
    [网络安全提高篇] 一二六.恶意软件分析之IDA Python基础用法及CFG控制流图提取详解[上]
    php生成个性二维码
    微信小程序数据绑定和条件渲染
  • 原文地址:https://blog.csdn.net/qq_48085286/article/details/127418338