vue 梯形百分比占比图

最近碰到一个需求梯形占比,一开始两个数据写死的,后面变成三个数据需要动态计算高度
在这里插入图片描述
在这里插入图片描述

  本人的理解:
   1、求它的方向 (需求点:第一个值和最后一个值比较大小)
   			 true:从左至右    false:从右至左
   2、求它的高度:(最大值和最小值是确定5016),
   			2-1.求它的偏差值:50-162-2.求它上一个梯形的高度:第一个梯形的值至有两种情况要么最高50要么最低16
   			2-3.求当前梯形的高从左至右(true, 上一个梯形高度 - 偏差值 * (当前高度 / 100 )   
   				从右至左(false, 上一个梯形高度 + 偏差值 * (当前高度 / 1003、图形用css裁剪出来   clip-path: polygon(上左坐标点(x,y),上右,下右, 下左);
   					     clip-path: polygon(0% 0%,100% 60%,100% 100%, 0% 100%);
		
核心代码:
 computed: {
    itemData () {
      return this.compData
    },
    itemValues () {
      // return
      return this.itemData.data
    },
    direction () {
      // true: left to right、 false: right to left
      return this.itemValues[0].value > this.itemValues[this.itemData.data.length - 1].value
    }
  },

  methods: {
    curBoxH (i) {
      // 如果当前i小于0,并且方向是从左往右,高是50,如果是从右向左,高是16
      if (i < 0) return this.direction ? 50 : 16
      // 如果当前i大于等于当前数据的length,并且方向是从左往右,高是16,如果是从右往左的话,高是50
      if (i >= this.itemValues.length) return this.direction ? 16 : 50
      // 当前box的所占百分比
      const curVal = this.itemValues[0].value === 0 && !this.itemValues.some(item => item.value > 0) ? parseInt(100 / this.itemValues.length) : this.itemValues[i].value
      // 偏差值
      const offsetVal = 50 - 16
      // 计算上一个盒子的高度
      const preH = this.curBoxH(i - 1)
      // 计算当前盒子的高度,如果是从左往右, 上一个盒子的高度 - 偏差值 * ( 当前所占百分比 / 100) ,如果是从右到左, 上一个盒子的高度 + 偏差值 * (当前所占百分比 / 100 )
      return this.direction ? preH - offsetVal * (curVal / 100) : preH + offsetVal * (curVal / 100)
    },
    curBoxW (i) {
  	  // 当前盒子所占百分比
      const curVal = this.itemValues[0].value === 0 && !this.itemValues.some(item => item.value > 0) ? parseInt(100 / this.itemValues.length) : this.itemValues[i].value
      // 当前盒子的宽度
      return (curVal / 100) * this.totalClientWidth
    }
  }
完整代码
<template>
  <div>
    <div class="div-content">
      <span>{{itemData.title}}</span>
      <div style="display: flex;align-items: flex-end;">
        <div
          v-for="(item, index) in itemValues"
          :key="index"
          class="front-trapezoid"
          @click="openPieModel(item.title)"
          :style="{
            width: curBoxW(index) + 'px',
            height: direction  ?  curBoxH(index - 1) + 'px': curBoxH(index) + 'px',
            clipPath: direction ? `polygon(0% 0%,100% ${curBoxH(index - 1) - curBoxH(index)}px,100% 100%, 0% 100%)`:`polygon(0% ${curBoxH(index) - curBoxH(index - 1)}px,100% 0%,100% 100%, 0% 100%)`,
            background: `linear-gradient(to right, ${itemData.color[index][direction ? 0 : 1]}, ${itemData.color[index][direction ? 1 : 0]})`,
            marginLeft: index === 1 || index === 2 ? '1px':'0'
          }">
        </div>
      </div>
      <div class="txt-wrap">
        <div class="left-text">
          <span class="series-value">{{leftItemValue.value.toFixed(0)}}%</span>
          <span class="series-name">{{leftItemValue.name}}</span>
        </div>
        <div class="middle-text" v-if="itemValues.length >= 3" :style="{left:direction ? (curBoxW(0) + 2) + 'px' : ''}">
          <span class="series-value">{{itemValues[1].value.toFixed(0)}}%</span>
          <span class="series-name">{{itemValues[1].name}}</span>
        </div>
        <div class="triangle"></div>
        <div class="right-text">
          <span class="series-value">{{rightItemValue.value.toFixed(0)}}%</span>
          <span class="series-name">{{rightItemValue.name}}</span>
        </div>

      </div>
    </div>

  </div>
</template>

<script>
export default {
  name: 'trapezoidComp',
  props: {
    compData: {
      type: Object,
      default: () => ({})
    }
  },
  data () {
    return {
      totalClientWidth: 0
    }
  },
  mounted () {
    this.$nextTick(() => {
      if (this.$el.querySelector('.div-content')) {
        const offsetVal = this.itemValues.length > 3 ? 2 : 1
        this.totalClientWidth = this.$el.querySelector('.div-content').clientWidth - offsetVal
      }
    })
  },
  computed: {
    itemData () {
      return this.compData
    },
    leftItemValue () {
      return this.itemData.data[0]
    },
    rightItemValue () {
      return this.itemData.data[this.itemData.data.length - 1]
    },
    itemValues () {
      // return
      return this.itemData.data
    },
    direction () {
      // true: left to right、 false: right to left
      return this.itemValues[0].value > this.itemValues[this.itemData.data.length - 1].value
    }
  },
  methods: {
    openPieModel (data) {
      console.log('data=', data)
      const nameArr = {
        年龄结构: 'bznl',
        学历结构: 'bzxl',
        专业结构: 'bzzy',
        党派结构: 'bzdp',
        性别结构: 'bzxb'
      }
      this.$emit('changePieModelVisable', nameArr[data])
    },
    curBoxH (i) {
      // 如果当前i小于0,并且方向是从左往右,高是50,如果是从右向左,高是16
      if (i < 0) return this.direction ? 50 : 16
      // 如果当前i大于等于当前数据的length,并且方向是从左往右,高是16,如果是从右往左的话,高是50
      if (i >= this.itemValues.length) return this.direction ? 16 : 50
      // 当前box的所占百分比
      const curVal = this.itemValues[0].value === 0 && !this.itemValues.some(item => item.value > 0) ? parseInt(100 / this.itemValues.length) : this.itemValues[i].value
      // 偏差值
      const offsetVal = 50 - 16
      // 计算上一个盒子的高度
      const preH = this.curBoxH(i - 1)
      // 计算当前盒子的高度,如果是从左往右, 上一个盒子的高度 - 偏差值 * ( 当前所占百分比 / 100) ,如果是从右到左, 上一个盒子的高度 + 偏差值 * (当前所占百分比 / 100 )
      return this.direction ? preH - offsetVal * (curVal / 100) : preH + offsetVal * (curVal / 100)
    },
    curBoxW (i) {
      const curVal = this.itemValues[0].value === 0 && !this.itemValues.some(item => item.value > 0) ? parseInt(100 / this.itemValues.length) : this.itemValues[i].value
      return (curVal / 100) * this.totalClientWidth
    }
  }
}
</script>

<style scoped lang="less">
    .div-content{
      width:100%;
      margin-bottom: 20px;
      margin-right: 4%;
      position: relative;

      > span{
        display: block;
        font-size: 14px;
        height: 40px;
      }

      .front-trapezoid{
        width: 200px;
        height: 50px;
        position: relative;
        background: linear-gradient(to right, #c02425, #f0cb35);
        clip-path: polygon(0% 0%,100% 34px,100% 100%, 0% 100%);
      }
      .behind-trapezoid{
        width: 40px;
        height: 16px;
        margin-left: 1px;/*共性*/
        position: relative;
        background: linear-gradient(to right, #c02425, #f0cb35);
        clip-path: polygon(0% 0%,100% 60%,100% 100%, 0% 100%);
      }

      .txt-wrap{
        display: flex;
        .left-text{
          position: absolute;
          bottom: -5px;
          left: 4px;
          font-size: 12px;
          .series-value{
            color: #fff;
          }
          .series-name{
            display: block;
            color: #333333;
          }
        }
        .middle-text{
          position: absolute;
          bottom: -5px;
          font-size: 12px;
          .series-value{
            color: #fff;
          }
          .series-name{
            display: block;
            color: #333333;
          }
        }
        .right-text{
          position: absolute;
          right: 4px;
          bottom: -5px;
          font-size: 12px;
          text-align: right;
          .series-value{
            color: #fff;
          }
          .series-name{
            display: block;
            color: #333333;
          }
        }
      }

      .triangle{
        margin: 4px auto 0;
        width: 0px;
        height: 0px;
        border-width:0px 6px 10px 6px;
        border-style:none solid solid solid;
        border-color: transparent transparent #9b9b9b
      }
    }
</style>

本人理解的程度中度,如有更好的理解方法,欢迎评论留言,谢谢

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值