网站做信息流,网上接外包项目,医院网站建设目的,漯河做网站优化项目背景#xff1a;需要实现道路情况鱼骨图#xff0c;根据上下行道路分别显示对应的道路情况和沿路设施状况#xff0c;箭头根据所示方向平滑移动 1.封装组件#xff0c;创建FishboneDiagram.vue文件
templatediv classfishedOneBox flex items-cente…项目背景需要实现道路情况鱼骨图根据上下行道路分别显示对应的道路情况和沿路设施状况箭头根据所示方向平滑移动 1.封装组件创建FishboneDiagram.vue文件
templatediv classfishedOneBox flex items-centerdiv clickscrollContent(left) classleft cursor-pointer leftButtonBox pl-20px box-border w-60px p-15px box-border h-165px flex justify-center items-centerbutton classtext-(16px #7BA9FA) font-bold leading-20px哈密方向/button/divdiv classcontent refscrollContainersdiv classupList mb-6px!-- 上行公路 --div classroaddiv classupRoadItem relative v-for(item, index) in upList :keyindex:style{ width: sectionWidth, background: item.status 1 ? #4ACF50 : #D6D6D6, borderLeft: getLeftBorder(index, upList), borderRight: getRightBorder(index, upList) }img classarrows arrow-lefts src//assets/images/iconLook/left_arrow.png altimg classarrow arrow-left src//assets/images/iconLook/left_arrow.png alt!-- 桩号 --div classtext-(12px #999999) absolute right--30px top--18px{{ item.id }}/div!-- 路侧设备服务区互通 --div classabsolute top--51px v-ifitem.staketype ! 4 item.staketype ! 5div classflex rounded-16px pl-7px pr-7px pt-3px pb-3px :classitem.staketype 2 ? bg-#E4FEE0 : bg-#E4EEFFimg classw-20px h-20px :srcgetImageUrl(item.staketype 1 ? tollStation:item.staketype 2 ? service:item.staketype 3 ? interFlow : , fishBoneIcon) altspan classtext-(14px #333333) ml-2px{{ item.stakename }}/span/divdiv classline w-full flex justify-centerimg classw-5px h-24px :srcgetImageUrl(${item.staketype 2 ? line_up_green : line_up_blue}, fishBoneIcon) alt/div/div/div/div/divdiv classdownList!-- 下行公路 --div classroaddiv classupRoadItem relative v-for(item, index) in downList :keyindex:style{ width: downSectionWidth, background: item.status 1 ? #4ACF50 : #D6D6D6, borderLeft: getLeftBorder(index, downList), borderRight: getRightBorder(index, downList) }img classdownArrow arrow-right src//assets/images/iconLook/right_arrow.png altimg classdownArrows arrow-rights src//assets/images/iconLook/right_arrow.png alt!-- 桩号 --div classtext-(12px #999999) absolute right--30px bottom--20px{{ item.id }}/div!-- 下行路侧设备服务区互通 --div classabsolute bottom--51px v-ifitem.staketype ! 4 item.staketype ! 5div classline w-full flex justify-centerimg classw-5px h-24px :srcgetImageUrl(${item.staketype 2 ? line_down_green : line_down_blue}, fishBoneIcon) alt/divdiv classflex rounded-16px pl-7px pr-7px pt-3px pb-3px :classitem.staketype 2 ? bg-#E4FEE0 : bg-#E4EEFFimg classw-20px h-20px :srcgetImageUrl(item.staketype 1 ? tollStation:item.staketype 2 ? service:item.staketype 3 ? interFlow : , fishBoneIcon) altspan classtext-(14px #333333) ml-2px{{ item.stakename }}/span/div/div/div/div/div/divdiv clickscrollContent(right) classright cursor-pointer rightButtonBox pr-20px box-border w-60px h-165px flex justify-center p-15px box-border items-centerbutton classtext-(16px #7BA9FA) font-bold leading-20px 星星峡方向/button/div/div
/templatescript setup langts
import { onMounted, ref, computed } from vue
import { getImageUrl } from //utils
const myTimeout ref()
const scrollTimeout ref()
const scrollContainers ref()
const maxScroll ref()const props defineProps({value: {type: Object,default: {}}
});// 上行
const upList computed(() {return props.value.upList
})// 下行
const downList computed(() {return props.value.downList
})
// 根据路段设施数量确定区间宽度
const sectionWidth computed(() {const widthShow scrollContainers.value?.offsetWidth upList.value.length * 120let sectionWidth: any 120pxif(upList.value.length downList.value.length) {if (widthShow) {sectionWidth (scrollContainers.value?.offsetWidth / upList.value.length) px} else {sectionWidth 120px}} else {const width downList.value.length * 120sectionWidth (width / upList.value.length) px}return sectionWidth
})
const downSectionWidth computed(() {const downWidthShow scrollContainers.value?.offsetWidth downList.value.length * 120let downSectionWidth: any 120pxif(downList.value.length upList.value.length) {if (downWidthShow) {downSectionWidth (scrollContainers.value?.offsetWidth / downList.value.length) px} else {downSectionWidth 120px}} else {const width upList.value.length * 120downSectionWidth (width / downList.value.length) px}return downSectionWidth
})
// 边框逻辑
const getLeftBorder (index: number, list: any[]) {if (index 0) return ; // 第一个元素始终显示左边框const prevItem list[index - 1];const current list[index];if (prevItem.end 1 current.start 1) {return ; // 当前元素的左边框不显示} else if (current.start 1) {return 2px solid #ffffff;}return ;
};const getRightBorder (index: number, list: any[]) {if (index list.length - 1) return ; // 最后一个元素始终显示右边框const current list[index];const nextItem list[index 1];if (current.end 1 nextItem.start 1) {return ; // 当前元素的右边框不显示} else if (current.end 1) {return 2px solid #ffffff;}return ;
};
const scrollContent (direction: any) {// 清除之前的防抖计时器如果存在clearTimeout(scrollTimeout.value);scrollTimeout.value setTimeout(() {clearTimeout(myTimeout.value)const scrollContainer scrollContainers.value;const scrollStep 40; // 每次滚动的步长const scrollInterval setInterval(() {if (direction left) {if (scrollContainer?.scrollLeft 0) {scrollContainer.scrollLeft - scrollStep;} else {clearInterval(scrollInterval);}} else {if (scrollContainer?.scrollLeft maxScroll.value) {scrollContainer.scrollLeft scrollStep;} else {clearInterval(scrollInterval);}}}, 20); // 滚动间隔时间数值越小滚动越快myTimeout.value setTimeout(() {clearInterval(scrollInterval)}, 200)}, 200)
}
const updateScrollRange () {const scrollContainer scrollContainers.value;// maxScroll.value scrollContainer?.scrollWidth - scrollContainer?.clientWidth;maxScroll.value scrollContainer?.scrollWidth
}
onMounted(() {updateScrollRange()
})
/scriptstyle langscss scoped
.fishedOneBox {width: 100%;height: 200px;
}
.leftButtonBox{background: url(//assets/images/leftButtonBox.png) no-repeat left center;background-size: 60% 100%;
}
.rightButtonBox{background: url(//assets/images/rightButtonBox.png) no-repeat right center;background-size: 60% 100%;
}
.content {display: flex;height: 186px;flex-direction: column;justify-content: center;width: calc(100% - 120px);overflow-x: scroll;overflow-y: hidden;.upList,.downList {display: flex;align-items: center;justify-content: flex-start; /* 确保子项目从左到右排列 */position: relative;.road {display: flex;background: #D6D6D6;.upRoadItem {background: #4ACF50;height: 30px;display: flex;align-items: center;//border-left: 1px solid #ffffff;//border-right: 1px solid #ffffff;.arrow {display: inline-block;position: relative;pointer-events: none;}.arrows {display: inline-block;position: relative;pointer-events: none;}.downArrow {display: inline-block;position: relative;pointer-events: none;}.downArrows {display: inline-block;position: relative;pointer-events: none;}}}}
}
.arrow-right {animation: moveRight 2.5s infinite linear forwards;
}
.arrow-rights {animation: moveRights 2.5s infinite linear forwards;
}
.arrow-left {animation: moveLeft 2.5s infinite linear forwards;
}
.arrow-lefts {animation: moveLefts 2.5s infinite linear forwards;
}
keyframes moveRight {0% {left: -55%;}100% {left: 45%;}
}
keyframes moveRights {0% {left: -5%;}100% {left: 95%;}
}
keyframes moveLeft {0% {left: 40%;}100% {left: -60%;}
}
keyframes moveLefts {0% {left: 95%;}100% {left: -5%;}
}
/style2. 引用组件
template
Fishbone :valuedataInfoList /
/templatescript setup langts
import Fishbone from ./Fishbone.vue// 鱼骨图上行下行数据
const dataInfoList: any ref({upList: [],downList: []
})
/script
小结
1. 根据上行下行数据画出上行下行路段
2. 再根据每段路中是否存在一些设备设施通过v-if渲染出来
3. 道路状况也可以根据当前此段道路的拥堵情况渲染不同的颜色
4. 路段动画根据图标方向对图标做left或者right的平移动画
根据自身业务情况适当修改鱼骨图可以根据业务方向继续延伸希望大家能有一点思路我也是从毫无头绪慢慢画出来又get到一个新的知识点希望大家多多指正一起加油