<!-- * 轮播图 --> <script setup lang="ts"> import { ref, reactive, onMounted, onBeforeUnmount, watch, nextTick, markRaw, } from 'vue'; import { isEmpty } from 'src/common/utils'; import * as echarts from 'echarts'; interface Props { itemKey: string; list: any[]; times?: number; navigation?: boolean; // 小圆点点击导航 } const props = withDefaults(defineProps<Props>(), { itemKey: 'id', list: () => [], times: 10000, navigation: false, }); const DOM_PIE_KEY = 'chart-pie-dom-'; const pointActive = ref<any>(1); const lastIndex = ref(0); const timer = ref<any>(null); const state = reactive({ // vue3中使用proxy的方式监听响应式,chart会被在vue内部转换成响应式对象,从而在resize 的时候获取不到 // markRaw 标记一个对象,使其永远不会再成为响应式对象 domMap: markRaw(new Map()), }); watch( () => props.list, () => { listChange(); } ); onMounted(() => { // }); onBeforeUnmount(() => { offTimer(); }); // list改变 function listChange() { // console.log('listChange=', props.list); offTimer(); // 清除定时器 if (isEmpty(props.list)) { resetIndex(); state.domMap.clear(); } else { // 如果当前下标 > list长度,则下标重新开始 if (pointActive.value > props.list.length) { resetIndex(); } nextTick(() => { const domMap = new Map(); props.list.map((item: any) => { const id = `${DOM_PIE_KEY}${item[props.itemKey]}`; const hasDom = state.domMap.has(id); const dom: any = hasDom ? state.domMap.get(id).dom : document.getElementById(id); const option = item.option || null; let myChart: any; const condition = pointActive.value === item[props.itemKey]; if (hasDom) { if (state.domMap.get(id).myChart) { myChart = state.domMap.get(id).myChart; } else { if (condition) { myChart = echarts.init(dom); } else { myChart = null; } } } else { if (condition) { myChart = echarts.init(dom); } else { myChart = null; } } if (condition && option) { // console.log('设置了', myChart); myChart.resize(); myChart.clear(); myChart.setOption(option); } const val = { dom, option, id, myChart, }; if (state.domMap) domMap.set(id, val); return val; }); if (state.domMap.size < domMap.size) { for (const [key, value] of domMap) { if (state.domMap.has(key)) { state.domMap.set(key, domMap.get(key)); } else { state.domMap.set(key, value); } } } else if (state.domMap.size > domMap.size) { for (const key of state.domMap.keys()) { if (domMap.has(key)) { state.domMap.set(key, domMap.get(key)); } else { state.domMap.delete(key); } } } else { for (const key of domMap.keys()) { state.domMap.set(key, domMap.get(key)); } } // console.log('state.domMap =', state.domMap); onStartTimer(); }); } } function onStartTimer() { timer.value = setTimeout(() => { const maxLength = props.list.length; if (pointActive.value >= maxLength) { pointActive.value = 1; lastIndex.value = 0; } else { pointActive.value++; lastIndex.value++; } nextTick(() => { renderChart(); onStartTimer(); }); }, props.times); } function onClickPoint(val: any, index: number) { if (index === lastIndex.value) return; offTimer(); if (index > lastIndex.value) { // 右 } else if (index < lastIndex.value) { // 左 } pointActive.value = val[props.itemKey]; lastIndex.value = index; nextTick(() => { renderChart(); onStartTimer(); }); } function renderChart() { const key = `${DOM_PIE_KEY}${pointActive.value}`; const currentDom = state.domMap.get(key); // console.log('currentDom =', currentDom); if (currentDom.myChart) { if (currentDom.option) { currentDom.myChart.clear(); currentDom.myChart.setOption(currentDom.option); } } else { const myChart = echarts.init(currentDom.dom); if (currentDom.option) { myChart.clear(); myChart.setOption(currentDom.option); } const val = { dom: currentDom.dom, option: currentDom.option, id: currentDom.id, myChart, }; state.domMap.set(key, val); } // console.log('domMap =', state.domMap); // console.log('pointActive =', pointActive.value); } function offTimer() { if (timer.value) { clearTimeout(timer.value); timer.value = null; } } function resetIndex() { pointActive.value = 1; lastIndex.value = 0; } </script> <template> <div class="carousel-box"> <div class="carousel"> <div class="carousel-content"> <div class="carousel-item-box"> <!-- item --> <template v-for="(i, index) in props.list" :key="index"> <div class="item-box opacity-in" v-show="pointActive === i[props.itemKey]" > <!-- some item --> <div class="my-item-box center"> <div class="fit" :id="`${DOM_PIE_KEY}${i[props.itemKey]}`" ></div> </div> </div> </template> </div> </div> <div class="carousel-points" v-if="props.navigation"> <div class="point" :class="{ 'active-point': pointActive === i[props.itemKey] }" v-for="(i, index) in props.list" :key="index" @click="onClickPoint(i, index)" ></div> </div> </div> </div> </template> <style lang="scss" scoped> @keyframes opacity-in { 0% { opacity: 0.5; } 100% { opacity: 1; } } .carousel-box { width: 100%; height: 100%; } .carousel { width: 100%; height: 100%; border: 1px solid $primary; overflow: hidden; display: flex; flex-direction: column; flex-wrap: nowrap; justify-content: space-between; } .carousel-content { flex: 1; overflow: hidden; } .carousel-item-box { width: 100%; height: 100%; overflow: hidden; position: relative; } .item-box { width: 100%; height: 100%; overflow: auto; position: absolute; background-color: #fff; top: 0; left: 0; } .opacity-in { animation: opacity-in 0.45s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; } .my-item-box { width: 100%; height: 100%; color: #000; font-size: 40px; } .carousel-points { display: flex; flex-direction: row; flex-wrap: wrap; justify-content: center; align-items: center; padding: 4px; padding-bottom: 0px; } .point { width: 12px; height: 12px; margin-right: 4px; margin-bottom: 4px; border-radius: 50%; border: 1px solid $primary; cursor: pointer; transition: all 0.5s; } .active-point { height: 10px; width: 30px; border-radius: 10px; background-color: $primary; } </style>