最近碰到一个需求梯形占比,一开始两个数据写死的,后面变成三个数据需要动态计算高度
本人的理解:
1、求它的方向 (需求点:第一个值和最后一个值比较大小)
true:从左至右 false:从右至左
2、求它的高度:(最大值和最小值是确定50和16),
2-1.求它的偏差值:50-16,
2-2.求它上一个梯形的高度:第一个梯形的值至有两种情况要么最高50要么最低16
2-3.求当前梯形的高从左至右(true), 上一个梯形高度 - 偏差值 * (当前高度 / 100 )
从右至左(false), 上一个梯形高度 + 偏差值 * (当前高度 / 100)
3、图形用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>
本人理解的程度中度,如有更好的理解方法,欢迎评论留言,谢谢