济菏高速业务端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

250 lines
5.6 KiB

1 year ago
<template>
<div :class='["TimeLine1", { "auto-size": autoSize }]'>
1 year ago
<!-- 节点 -->
<div class="node" v-for="(item, index) in data" ref="nodeRefs">
<span class="top-label">
<slot name="bottom-label" :data="item">
{{ item.time }}
</slot>
</span>
<div class="center">
<div class="line" v-if="!index" :style="{ width: `${nodeLinesWidth[-1]}px` }" />
1 year ago
<div class="circle" :style="{ '--active-color': !item.isActive ? normalColor : activeColor }"></div>
<div class="line" :style="{
width: `${nodeLinesWidth[index]}px`,
borderImage: getBorderImageStyle(item),
}" />
</div>
<slot name="bottom-label" :data="item">
<div class="bottom"
:style="{ backgroundImage: `url(${require(`./images/bg${item.isActive ? '-active' : ''}.svg`)})` }">
{{ item.label }}
</div>
</slot>
</div>
</div>
</template>
<script>
function getPositionAtCenter(element) {
const { top, left } = element.getBoundingClientRect();
return {
x: left,
y: top
};
}
function getDistanceBetweenElements(domA, domB) {
const domAPosition = getPositionAtCenter(domA);
const domBPosition = getPositionAtCenter(domB);
return Math.hypot(domAPosition.x - domBPosition.x, domAPosition.y - domBPosition.y);
}
1 year ago
export default {
name: 'TimeLine1',
props: {
data: {
type: Array,
default: () => Array.from({ length: 15 }).map(() => ({
time: "16.36",
label: "阿发",
isActive: false
}))
},
activeColor: {
type: String,
default: "#39D5BF"
},
normalColor: {
type: String,
default: "#ccc"
},
lineActiveColor: {
type: String,
default: "linear-gradient(90deg, rgba(59, 216, 188, 1), rgba(29, 171, 215, 1)}) 2 2"
},
lineNormalColor: {
type: String,
default: null
},
// 自动适配宽度
autoSize: {
type: Boolean,
default: true
},
filterDistance: {
type: Function,
default: null
1 year ago
}
},
data() {
return {
nodeLinesWidth: {}
1 year ago
}
},
watch: {
data: {
handler(data) {
const nodeLinesWidth = [];
const filterDistance = this.filterDistance || (num => num);
const removeDistance = 20 + 4 * 2;
function getDistance(index) {
return filterDistance(getDistanceBetweenElements(
this.$refs.nodeRefs[index].querySelector('.center'),
this.$refs.nodeRefs[index + 1].querySelector('.center'))
) - removeDistance
}
function getSpecialDistance(index) {
return filterDistance(getDistanceBetweenElements(
this.$refs.nodeRefs[index].parentElement,
this.$refs.nodeRefs[index].querySelector('.center'))
) - removeDistance
1 year ago
}
1 year ago
this.$nextTick(() => {
1 year ago
data.forEach((_, index) => {
if (index === data.length - 1) {
if (this.autoSize) {
nodeLinesWidth[-1] = getSpecialDistance.call(this, 0)
nodeLinesWidth[data.length - 1] = getSpecialDistance.call(this, data.length - 1)
}
return
};
1 year ago
nodeLinesWidth[index] = getDistance.call(this, index);
1 year ago
});
this.nodeLinesWidth = nodeLinesWidth;
})
},
immediate: true
}
},
methods: {
getBorderImageStyle(item) {
const linearColor = item.isActive ? (this.lineActiveColor || `${this.activeColor}, ${this.activeColor}`) : (this.lineNormalColor || `${this.normalColor}, ${this.normalColor}`)
return `linear-gradient(90deg, ${linearColor}) 2 2`
}
}
}
</script>
<style lang='scss' scoped>
div.auto-size {
justify-content: space-between;
overflow: hidden;
.node {
flex: 1;
min-width: auto !important;
&:first-child,
&:last-child {
.line {
width: auto;
}
}
}
}
1 year ago
.TimeLine1 {
color: #fff;
width: auto;
font-size: 14px;
font-family: PingFang SC, PingFang SC;
1 year ago
font-weight: 400;
color: #FFFFFF;
display: flex;
flex-wrap: nowrap;
overflow: auto;
gap: 15px;
padding-left: 15px;
.node {
display: flex;
align-items: center;
// justify-content: center;
flex-direction: column;
gap: 3px;
min-width: 90px;
1 year ago
.top-label {
line-height: 16px;
height: 24px;
}
.center {
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
position: relative;
.circle {
border: 1px solid var(--active-color, #39D5BF);
border-radius: 50%;
width: 15px;
height: 15px;
position: relative;
&::before {
content: "";
position: absolute;
width: 72%;
height: 72%;
border-radius: 50%;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: var(--active-color, #39D5BF);
}
}
.line {
position: absolute;
height: 0px;
opacity: 1;
border: 2px solid;
border-image: linear-gradient(90deg, rgba(59, 216, 188, 1), rgba(29, 171, 215, 1)) 2 2;
&:first-child {
right: calc(50% + 8px + 6px);
}
&:last-child {
left: calc(50% + 8px + 6px);
}
}
}
&:first-child,
&:last-child {
.line {
width: 60px;
}
}
.bottom {
line-height: 16px;
background-repeat: no-repeat;
background-size: 100% 100%;
min-width: 90px;
text-align: center;
padding: 6px 12px;
height: 27px;
}
}
}
</style>