|
@@ -0,0 +1,297 @@
|
|
|
+<!DOCTYPE html>
|
|
|
+<html lang="en">
|
|
|
+
|
|
|
+<head>
|
|
|
+ <meta charset="UTF-8" />
|
|
|
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
|
+ <title>纯 JS 楼层切换控件</title>
|
|
|
+ <style>
|
|
|
+ .card_floor_control {
|
|
|
+ user-select: none;
|
|
|
+ position: absolute;
|
|
|
+ top: 100px;
|
|
|
+ right: calc(25% + 10px);
|
|
|
+ width: 100px;
|
|
|
+ height: auto;
|
|
|
+ font-family: Arial, sans-serif;
|
|
|
+ }
|
|
|
+
|
|
|
+ .floor_control {
|
|
|
+ position: relative;
|
|
|
+ cursor: pointer;
|
|
|
+ width: 100px;
|
|
|
+ height: 40px;
|
|
|
+ background-color: rgba(34, 91, 153, 0.3);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ color: #ffffff;
|
|
|
+ border-radius: 4px;
|
|
|
+ user-select: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .triangle:after {
|
|
|
+ content: '';
|
|
|
+ display: inline-block;
|
|
|
+ margin-left: 20px;
|
|
|
+ width: 10px;
|
|
|
+ height: 10px;
|
|
|
+ border-top: 1px solid #ffffff;
|
|
|
+ border-right: 1px solid #ffffff;
|
|
|
+ transform: rotate(135deg);
|
|
|
+ -webkit-transform: rotate(135deg);
|
|
|
+ }
|
|
|
+
|
|
|
+ .all_building_floor {
|
|
|
+ position: relative;
|
|
|
+ margin-top: 10px;
|
|
|
+ display: none;
|
|
|
+ flex-direction: column;
|
|
|
+ right: 0px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .box_item_bf {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 18px;
|
|
|
+ position: relative;
|
|
|
+ user-select: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .floor_box {
|
|
|
+ cursor: pointer;
|
|
|
+ color: rgb(108, 111, 113);
|
|
|
+ --c: rgb(108, 111, 113);
|
|
|
+ --n: 4;
|
|
|
+ --t: 2px;
|
|
|
+ --d: 45deg;
|
|
|
+ aspect-ratio: 1;
|
|
|
+ width: 60px;
|
|
|
+ height: 60px;
|
|
|
+ border-radius: 50%;
|
|
|
+ background-color: rgb(1, 6, 19);
|
|
|
+ margin-bottom: 10px;
|
|
|
+ position: relative;
|
|
|
+ }
|
|
|
+
|
|
|
+ .floor_box::after {
|
|
|
+ content: "";
|
|
|
+ position: absolute;
|
|
|
+ top: 2.5px;
|
|
|
+ left: 2.5px;
|
|
|
+ width: 55px;
|
|
|
+ height: 55px;
|
|
|
+ border-radius: 50%;
|
|
|
+ padding: var(--t);
|
|
|
+ background: var(--c);
|
|
|
+ -webkit-mask:
|
|
|
+ linear-gradient(#0000 0 0) content-box,
|
|
|
+ repeating-conic-gradient(from calc(var(--d)/2), #000 0 calc(360deg/var(--n) - var(--d)), #0000 0 calc(360deg/var(--n)));
|
|
|
+ -webkit-mask-composite: source-in;
|
|
|
+ mask:
|
|
|
+ linear-gradient(#0000 0 0) content-box,
|
|
|
+ repeating-conic-gradient(from calc(var(--d)/2), #000 0 calc(360deg/var(--n) - var(--d)), #0000 0 calc(360deg/var(--n)));
|
|
|
+ mask-composite: intersect;
|
|
|
+ }
|
|
|
+
|
|
|
+ .activate_box {
|
|
|
+ cursor: pointer;
|
|
|
+ color: #fff;
|
|
|
+ --c: #fff;
|
|
|
+ --n: 4;
|
|
|
+ --t: 2px;
|
|
|
+ --d: 45deg;
|
|
|
+ aspect-ratio: 1;
|
|
|
+ width: 60px;
|
|
|
+ height: 60px;
|
|
|
+ border-radius: 50%;
|
|
|
+ background-color: rgb(1, 6, 19);
|
|
|
+ margin-bottom: 10px;
|
|
|
+ position: relative;
|
|
|
+ }
|
|
|
+
|
|
|
+ .activate_box::after {
|
|
|
+ content: "";
|
|
|
+ position: absolute;
|
|
|
+ top: 2.5px;
|
|
|
+ left: 2.5px;
|
|
|
+ width: 55px;
|
|
|
+ height: 55px;
|
|
|
+ border-radius: 50%;
|
|
|
+ padding: var(--t);
|
|
|
+ background: var(--c);
|
|
|
+ -webkit-mask:
|
|
|
+ linear-gradient(#0000 0 0) content-box,
|
|
|
+ repeating-conic-gradient(from calc(var(--d)/2), #000 0 calc(360deg/var(--n) - var(--d)), #0000 0 calc(360deg/var(--n)));
|
|
|
+ -webkit-mask-composite: source-in;
|
|
|
+ mask:
|
|
|
+ linear-gradient(#0000 0 0) content-box,
|
|
|
+ repeating-conic-gradient(from calc(var(--d)/2), #000 0 calc(360deg/var(--n) - var(--d)), #0000 0 calc(360deg/var(--n)));
|
|
|
+ mask-composite: intersect;
|
|
|
+ animation: rotate 5s linear infinite;
|
|
|
+ }
|
|
|
+
|
|
|
+ @keyframes rotate {
|
|
|
+ from {
|
|
|
+ transform: rotate(0deg);
|
|
|
+ }
|
|
|
+
|
|
|
+ to {
|
|
|
+ transform: rotate(360deg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ </style>
|
|
|
+</head>
|
|
|
+
|
|
|
+<body>
|
|
|
+ <div class="card_floor_control">
|
|
|
+ <div class="floor_control" id="floorControl">
|
|
|
+ 全部<span class="triangle"></span>
|
|
|
+ </div>
|
|
|
+ <div class="all_building_floor" id="allBuildingFloor"></div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <script>
|
|
|
+ (function () {
|
|
|
+ const arrData = [
|
|
|
+ { title: '全部', id: 'all' },
|
|
|
+ {
|
|
|
+ title: 'A栋', id: 'A', list: [
|
|
|
+ { title: '3F', id: '3' },
|
|
|
+ { title: '2F', id: '2' },
|
|
|
+ { title: '1F', id: '1' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: 'B栋', id: 'B', list: [
|
|
|
+ { title: '3F', id: '3' },
|
|
|
+ { title: '2F', id: '2' },
|
|
|
+ { title: '1F', id: '1' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: 'C栋', id: 'C', list: [
|
|
|
+ { title: '4F', id: '4' },
|
|
|
+ { title: '3F', id: '3' },
|
|
|
+ { title: '2F', id: '2' },
|
|
|
+ { title: '1F', id: '1' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: 'D栋', id: 'D', list: [
|
|
|
+ { title: '2F', id: '2' },
|
|
|
+ { title: '1F', id: '1' }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+
|
|
|
+ // 当前选中项id 和 title
|
|
|
+ let currentId = 'all';
|
|
|
+ let currentTitle = '全部';
|
|
|
+
|
|
|
+ const floorControl = document.getElementById('floorControl');
|
|
|
+ const allBuildingFloor = document.getElementById('allBuildingFloor');
|
|
|
+
|
|
|
+ // 控制楼层列表容器显示/隐藏
|
|
|
+ let showList = false;
|
|
|
+
|
|
|
+ floorControl.addEventListener('click', () => {
|
|
|
+ showList = !showList;
|
|
|
+ if (showList) {
|
|
|
+ allBuildingFloor.style.display = 'flex';
|
|
|
+ // 切换显示当前选中项对应的列表
|
|
|
+ renderList();
|
|
|
+ } else {
|
|
|
+ allBuildingFloor.style.display = 'none';
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 渲染列表区域
|
|
|
+ function renderList() {
|
|
|
+ allBuildingFloor.innerHTML = '';
|
|
|
+
|
|
|
+ // 找到当前id对应的item,和列表数据
|
|
|
+ let listData = [];
|
|
|
+
|
|
|
+ if (currentId === 'all') {
|
|
|
+ // 如果是全部,展示顶层选项
|
|
|
+ listData = arrData;
|
|
|
+ } else {
|
|
|
+ const currentBuilding = arrData.find(item => item.id === currentId);
|
|
|
+ if (currentBuilding && currentBuilding.list) {
|
|
|
+ listData = currentBuilding.list;
|
|
|
+ } else {
|
|
|
+ // 防止找不到对应list
|
|
|
+ listData = [];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ listData.forEach(item => {
|
|
|
+ const div = document.createElement('div');
|
|
|
+ div.classList.add('box_item_bf');
|
|
|
+
|
|
|
+ if (item.id === currentId) {
|
|
|
+ div.classList.add('activate_box');
|
|
|
+ } else {
|
|
|
+ div.classList.add('floor_box');
|
|
|
+ }
|
|
|
+
|
|
|
+ div.textContent = item.title;
|
|
|
+
|
|
|
+ div.addEventListener('click', (e) => {
|
|
|
+ e.stopPropagation(); // 阻止事件冒泡,避免触发外层点击
|
|
|
+ switchoverItem(item);
|
|
|
+ });
|
|
|
+
|
|
|
+ allBuildingFloor.appendChild(div);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 切换选中项
|
|
|
+ function switchoverItem(item) {
|
|
|
+ // 如果点了目前已经选中的项,列表隐藏
|
|
|
+ if (item.id === currentId) {
|
|
|
+ showList = false;
|
|
|
+ allBuildingFloor.style.display = 'none';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ currentId = item.id;
|
|
|
+ currentTitle = item.title;
|
|
|
+
|
|
|
+ // 改变按钮显示文字
|
|
|
+ floorControl.childNodes[0].nodeValue = currentTitle;
|
|
|
+
|
|
|
+ // 如果选中的是“全部”,则展示arrData顶层数据
|
|
|
+ if (currentId === 'all') {
|
|
|
+ // 当前激活项就是全部
|
|
|
+ renderList();
|
|
|
+ } else {
|
|
|
+ // 判断当前id是否是顶层id,如果在顶层arrData里则显示对应list,否则显示arrData顶层选项
|
|
|
+ const isTopLevel = arrData.some(it => it.id === currentId);
|
|
|
+
|
|
|
+ if (isTopLevel) {
|
|
|
+ renderList();
|
|
|
+ } else {
|
|
|
+ // 当前选中的是具体楼层,这时列表显示 arrData 顶层,方便切换楼栋
|
|
|
+ currentId = 'all'; // 也可以这样处理,或者直接关闭列表
|
|
|
+ currentTitle = '全部';
|
|
|
+ floorControl.childNodes[0].nodeValue = currentTitle;
|
|
|
+ showList = false;
|
|
|
+ allBuildingFloor.style.display = 'none';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化显示
|
|
|
+ floorControl.childNodes[0].nodeValue = currentTitle;
|
|
|
+
|
|
|
+ // 说明:初始时列表隐藏,点击按钮显示顶层选项,也就是arrData。
|
|
|
+
|
|
|
+ })();
|
|
|
+ </script>
|
|
|
+
|
|
|
+</body>
|
|
|
+
|
|
|
+</html>
|