直接创建timeLine.vue 文件,将代码复制进去即可。(css引用的播放和切换按钮自行修改)
<template>
<div class="timeLineBox" :style="{ width: prop.step * prop.itemMargin + '%' }">
<div class="control">
<div @click="doPrev"></div>
<div :class="{ stop: isStart }" @click="doPlay"></div>
<div @click="doNext"></div>
</div>
<div class="timeLine">
<el-slider
:format-tooltip="formatToolTip"
@change="sliderChange"
v-model="value"
:marks="marks"
:max="max"
/>
</div>
</div>
</template>
<script lang="ts" setup>
/**
* 使用方法
* @param {Array} timeLineData 时间轴需要展示的时间数组
* @param {Number} step 时间轴每一页展示多少个
* @param {Number} itemMargin 每一页中每个时间点相距距离
* stopPlay 外部调用停止时间轴方法
* getCurrentIndex 在外部可以获取到当前播放的时间值
*/
import { ref, onMounted, watch } from "vue";
let prop = defineProps(["timelineData", "step", "itemMargin"]);
const emit = defineEmits(["getCurrentIndex"]);
let value = ref(0);
let max = ref(0);
let isStart = ref(false);
let timmer = ref();
let marks = ref({});
// 多少个时间点
let length = ref();
// 当前下标
let current = ref(0);
// 一页显示7个
let size = ref(7);
// 当前页数
let currentPage = ref(1);
// 总页数
let totalPage = ref(1);
// 当前页有几个时间点
let currentNums = ref();
//用来存 指定显示 那个时间点
let zdNowIdx = ref("");
onMounted(() => {
init();
start();
});
const doPlay = () => {
isStart.value = !isStart.value;
start();
};
// 停止播放
const stopPlay = () => {
isStart.value = false;
start();
};
const doPrev = () => {
if (current.value === 0) {
current.value = 0;
value.value = 0;
} else {
current.value--;
value.value--;
}
emit("getCurrentIndex", marks.value[current.value]);
isStart.value = false;
start();
};
const doNext = () => {
if (current.value === currentNums.value - 1) {
value.value = current.value;
} else {
current.value++;
value.value++;
}
emit("getCurrentIndex", marks.value[current.value]);
isStart.value = false;
start();
};
const sliderChange = (v) => {
current.value = v;
emit("getCurrentIndex", marks.value[current.value]);
isStart.value = false;
start();
};
const formatToolTip = (v) => {
if (marks.value[v]) {
return marks.value[v].label;
}
};
const init = () => {
// slider个数,0也算 所以-1
size.value = prop.step;
max.value = size.value - 1;
length.value = prop.timelineData.length;
// 得出总页数
if (length.value % size.value === 0) {
totalPage.value = length.value / max.value;
} else {
totalPage.value = Math.ceil(length.value / max.value);
}
let obj = {};
if (length.value > size.value) {
prop.timelineData
.slice((currentPage.value - 1) * size.value, size.value * currentPage.value)
.forEach((item, index) => {
obj[index] = { label: item.name, value: item.value };
});
// 当前页有几个时间段
currentNums.value = prop.timelineData.slice(
(currentPage.value - 1) * size.value,
size.value * currentPage.value
).length;
} else {
prop.timelineData.forEach((item, index) => {
obj[index] = { label: item.name, value: item.value };
});
// 当前页有几个时间段
currentNums.value = prop.timelineData.length;
}
marks.value = obj;
};
let start = () => {
if (isStart.value) {
clearInterval(timmer.value);
timmer.value = null;
timmer.value = setInterval(() => {
if (current.value > currentNums.value - 1) {
current.value = 0;
// 总页数大于1的话说明要走分页 第二页操作
if (totalPage.value > 1) {
currentPage.value++;
// 当前页大于总页数的话说明 当前页该显示第一页 了
if (currentPage.value > totalPage.value) {
currentPage.value = 1;
}
getFormatData();
}
}
emit("getCurrentIndex", marks.value[current.value]);
// ++之前返回的是当前时间轴显示的日期数据
value.value = current.value++;
}, 1000);
} else {
clearInterval(timmer.value);
timmer.value = null;
}
};
const getFormatData = () => {
let obj = {};
prop.timelineData
.slice((currentPage.value - 1) * size.value, size.value * currentPage.value)
.forEach((item, index) => {
obj[index] = { label: item.name, value: item.value };
});
// 当前页有几个时间段
currentNums.value = prop.timelineData.slice(
(currentPage.value - 1) * size.value,
size.value * currentPage.value
).length;
max.value = currentNums.value - 1;
marks.value = obj;
};
// 设置默认进来的下标; 设置变量存起来,在watch中设置。因为当前方法获取到的是timelinedata未改变之前的数据
const initCurrentIndex = (v) => {
zdNowIdx.value = v;
let idx = prop.timelineData.findIndex((item) => item.name === v);
value.value = idx;
current.value = idx;
};
watch(
() => prop.timelineData,
(newVal, oldVal) => {
init();
initCurrentIndex(zdNowIdx.value);
}
);
defineExpose({
stopPlay,
initCurrentIndex,
});
</script>
<style lang="scss" scoped>
.stop {
background: url("@/assets/images/oneMap/stop.png") no-repeat !important;
background-size: 100% 100%;
}
:deep(.el-slider__marks-text) {
background: rgba(255, 255, 255, 0.16);
border-radius: 3px 3px 3px 3px;
opacity: 1;
font-size: 12px;
padding: 5px;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #ffffff;
line-height: 16px;
}
.timeLineBox {
position: absolute;
bottom: 20px;
height: 56px;
width: 528px;
left: 52%;
transform: translateX(-50%);
background: rgba(34, 79, 141, 0.64);
box-shadow: inset 0px 0px 14px 0px rgba(72, 254, 254, 0.4);
border-radius: 4px 4px 4px 4px;
opacity: 1;
border: 1px solid rgba(255, 255, 255, 0.08);
display: flex;
.control {
display: flex;
align-items: center;
justify-content: space-around;
width: 108px;
height: 100%;
background: rgba(0, 0, 0, 0.3);
border-radius: 4px 0px 0px 4px;
opacity: 1;
border: 1px solid;
border-image: linear-gradient(180deg, rgba(70, 132, 255, 1), rgba(82, 138, 250, 0.48))
1 1;
div {
cursor: pointer;
width: 28px;
height: 28px;
background-color: pink;
&:nth-child(1) {
background: url("@/assets/images/oneMap/prev.png") no-repeat;
background-size: 100% 100%;
}
&:nth-child(2) {
background: url("@/assets/images/oneMap/start.png") no-repeat;
background-size: 100% 100%;
}
&:nth-child(3) {
background: url("@/assets/images/oneMap/next.png") no-repeat;
background-size: 100% 100%;
}
}
}
.timeLine {
flex: 1;
background: rgba(34, 79, 141, 0.64);
box-shadow: inset 0px 0px 14px 0px rgba(72, 254, 254, 0.4);
opacity: 1;
border: 1px solid rgba(255, 255, 255, 0.08);
padding: 31px;
padding-top: 0;
}
}
</style>
父组件调用如下。传入的参数使用含义在上面的时间轴代码中有注释
效果如下。
如果当前返回的值和时间轴不匹配的话,将initCurrentIndex方法注释掉试一下。