旋转菜单

59 阅读3分钟

1.hover实现方式

动画5.gif

<template>
  <div class="global-setting" ref="dragElement">
    <div class="setting-sub-btn" style="--i: 0">
      <el-popover placement="bottom" title="字体大小" :width="200" trigger="click">
        <div style="font-size: 20px" class="font-setting flex-center">
          <div class="font-btn" @click="reduceSize">A -</div>
          <input v-model="globalFontSize" class="font-global-size" />
          <div class="font-btn" @click="globalFontSize += 2">A +</div>
        </div>
        <template #reference>
          <SvgIcon class="icon" name="font-size" :iconStyle="{ width: '1.6rem', height: '1.6rem' }" />
        </template>
      </el-popover>
    </div>
    <div class="setting-sub-btn padding-6" style="--i: 1">
      <el-icon v-if="type === 'dark'" color="#b8b8b8" class="icon" size="24px"><MoonNight /></el-icon>
      <el-icon v-else color="#b8b8b8" class="icon" size="24px"><Sunny /></el-icon>
      <!-- <SvgIcon name="moon" :iconStyle="{ width: '1.5rem', height: '1.5rem' }" /> -->
    </div>
    <div class="setting-sub-btn padding-6" style="--i: 2">
      <el-icon color="#b8b8b8" class="icon" size="24px"><Edit /></el-icon>
    </div>
    <div class="setting-sub-btn padding-6" style="--i: 3">
      <SvgIcon class="icon" name="progress" :iconStyle="{ width: '1.6rem', height: '1.6rem' }" />
    </div>
    <div class="setting-btn padding-6">
      <el-icon class="icon" size="32px"><Setting /></el-icon>
    </div>
  </div>
</template>

<script setup>
let globalFontSize = ref(0)
let type = ref('light')
watch(
  () => globalFontSize.value,
  val => {
    // 挂载时,加载文件内容,并初始化
    if (val >= 0) {
      const root = document.querySelector(':root')
      console.log('root', root)
      root.style.setProperty('--font-base-size', val + 'px')
    }
  },
  { immediate: true }
)
const reduceSize = () => {
  if (globalFontSize.value === 0) return
  globalFontSize.value -= 2
}
const themeProperty = {
  light: {
    '--theme-bg': '#ffffff',
    '--font-color': '#03103a',
    '--scroll-thumb-bgColor': '#acaeb1',
    '--el-color-primary': '#6241d5',
    '--el-color-primary-light-3': '#c5b8f5',
    '--pre-image': `url(${new URL(`../../assets/img/taskBg.png`, import.meta.url).href})`
  },

  dark: {
    '--theme-bg': '#262627',
    '--font-color': '#fff',
    '--scroll-thumb-bgColor': '#c0c2c5',
    '--el-color-primary': '#6241d5',
    '--el-color-primary-light-3': '#c5b8f5',
    '--pre-image': `url(${new URL(`../../assets/img/taskBg-dark.jpeg`, import.meta.url).href})`
  }
}
onMounted(() => {
  type.value = localStorage.getItem('theme') || 'light'
  if (type.value === 'dark') {
    document.documentElement.classList.add('dark')
  } else {
    document.documentElement.classList.remove('dark')
  }
  changeeProperty()
  move()
})
const toggle = () => {
  if (type.value === 'light') {
    type.value = 'dark'
    document.documentElement.classList.add('dark')
  } else {
    type.value = 'light'
    document.documentElement.classList.remove('dark')
  }
  localStorage.setItem('theme', type.value)
  changeeProperty()
}
const changeeProperty = () => {
  const theme = themeProperty[type.value]
  for (const [key, value] of Object.entries(theme)) {
    document.documentElement.style.setProperty(key, value)
  }
}
let dragElement = ref(null)
let startX = ref(0)
let startY = ref(0)
const move = () => {
  // 鼠标按下时记录初始位置
  function handleMouseDown(event) {
    event.preventDefault()
    startX.value = event.clientX - dragElement.value.offsetLeft
    startY.value = event.clientY - dragElement.value.offsetTop

    window.addEventListener('mousemove', handleMouseMove)
    window.addEventListener('mouseup', handleMouseUp)
  }

  // 移动元素到新位置
  function handleMouseMove(event) {
    event.preventDefault()
    dragElement.value.style.left = event.clientX - startX.value + 'px'
    dragElement.value.style.top = event.clientY - startY.value + 'px'
  }

  // 松开鼠标后取消事件监听器
  function handleMouseUp() {
    window.removeEventListener('mousemove', handleMouseMove)
    window.removeEventListener('mouseup', handleMouseUp)
  }

  dragElement.value.addEventListener('mousedown', handleMouseDown)
}
</script>

<style lang="scss" scoped>
.global-setting {
  cursor: move;
  position: absolute;
  top: 58%;
  right: 2vh;
  z-index: 99;
  width: 140px;
  height: 140px;
  border-radius: 50%;
  background-color: transparent; /* 背景色 */
  display: flex; /* 弹性布局 */
  justify-content: center; /* 居中排列 */
  align-items: center;
  .setting-btn {
    position: absolute; /* 绝对定位 */
    transform: translateX(-50%);
    width: 50px;
    height: 50px;
    background: var(--theme-bg);
    display: flex; /* 弹性布局 */
    justify-content: center; /* 水平居中 */
    align-items: center; /* 垂直居中 */
    border-radius: 50%; /* 设置按钮圆形 */
    border: 1px solid #ece7e7;
    transform: rotate(0deg);
    transition: all 1.25s;
  }
  .setting-sub-btn {
    position: absolute; /* 绝对定位 */
    left: 0px;
    display: flex; /* 弹性布局 */
    flex-direction: column;
    justify-content: center; /* 水平居中 */
    align-items: center; /* 垂直居中 */
    border-radius: 50%; /* 设置span标签为圆形 */
    transform-origin: 70px;
    cursor: pointer; /* 鼠标悬停是为小手 */
    transform: rotate(0deg) translateX(55px); /* 沿着水平方向向右移动80个像素,但不进行旋转 */
    transition: all 1s;
    .icon {
      width: 33px !important;
      height: 33px !important;
      padding: 4px;
      background: var(--theme-bg);
      border-radius: 50%;
      transform: rotate(calc(360deg / -4 * var(--i))); /* 旋转工具标签 */
      border: 1px solid #ece7e7;
      transition: all 1s;
    }
  }
}

.global-setting:hover .setting-sub-btn {
  transform: rotate(calc(360deg / 4 * var(--i))); /* 旋转工具标签 */
}

.global-setting:hover .setting-btn {
  transform: rotate(90deg); /* 旋转加号标签 */
}
.font-setting {
  user-select: none;
  width: 100%;
  margin: 0 auto;
  border: 1px solid #f0f0f0;
  .font-btn {
    cursor: Pointer;
    with: 25%;
    text-align: center;
  }
  .font-global-size {
    width: 30%;
    border-left: 1px solid #f0f0f0;
    border-right: 1px solid #f0f0f0;
    padding: 0 12px;
    margin: 0 12px;
  }
}
.font-btn:hover {
  color: blue;
}
</style>

2.点击实现方式(通过label标签)

动画5.gif

<template>
  <div class="container">
    <input type="checkbox" id="menu" checked="checked" />
    <label for="menu">
      <div class="circle">
        <img src="../assets/img/bg.png" alt="" />
      </div>
    </label>
    <ul class="items">
      <li><img src="../assets/img/bg.png" alt="" /></li>
      <li><img src="../assets/img/bg.png" alt="" /></li>
      <li><img src="../assets/img/bg.png" alt="" /></li>
      <li><img src="../assets/img/bg.png" alt="" /></li>
      <li><img src="../assets/img/bg.png" alt="" /></li>
      <li><img src="../assets/img/bg.png" alt="" /></li>
    </ul>
  </div>
</template>
<style>
/* 基本配置 */
* {
  padding: 0px;
  margin: 0px;
}
body {
  display: flex;
  align-items: center;
  align-content: center;
  justify-content: space-around;
  background-color: #eceff1;
}
.container {
  margin: 0 auto;
  width: 500px;
  height: 500px;
  position: relative;
}
/* 设置基本样式 */
.circle {
  width: 70px;
  height: 70px;
  background-color: #fff;
  border-radius: 50%;

  display: flex;
  text-align: center;
  justify-content: center;
  align-content: center;
  align-items: center;
  position: absolute;
  z-index: 999;
  bottom: 10px;
  left: 10px;
}

.circle img {
  width: 100%;
}

.items li {
  width: 70px;
  height: 70px;
  background-color: #fff;
  border-radius: 50%;
  display: flex;
  text-align: center;
  position: absolute;
  bottom: 10px;
  left: 10px;
  display: block;
  transition: 1s;
  z-index: 0;
}

li img {
  width: 100%;
  margin-top: 8px;
}

/* 这是通过过渡实现的效果 */
#menu:not(:checked) ~ .items li:nth-child(1) {
  transform: rotate(0deg) translateY(-110px);
}
#menu:not(:checked) ~ .items li:nth-child(2) {
  transform: rotate(60deg) translateY(-110px);
}
#menu:not(:checked) ~ .items li:nth-child(3) {
  transform: rotate(120deg) translateY(-110px);
}
#menu:not(:checked) ~ .items li:nth-child(4) {
  transform: rotate(180deg) translateY(-110px);
}
#menu:not(:checked) ~ .items li:nth-child(5) {
  transform: rotate(240deg) translateY(-110px);
}
#menu:not(:checked) ~ .items li:nth-child(6) {
  transform: rotate(300deg) translateY(-110px);
}

.items li:nth-child(1) img {
  transform: rotate(-90deg);
}
.items li:nth-child(2) img {
  transform: rotate(-150deg);
}
.items li:nth-child(3) img {
  transform: rotate(-210deg);
}
.items li:nth-child(4) img {
  transform: rotate(-270deg);
}
.items li:nth-child(5) img {
  transform: rotate(-330deg);
}
.items li:nth-child(6) img {
  transform: rotate(-390deg);
}

#menu:not(:checked) ~ label .circle {
  animation-name: disappear;
  animation-duration: 180.5ms;
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
}

@keyframes disappear {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.2);
  }
  100% {
    transform: scale(0.85) rotate(-45deg);
  }
}

#menu:checked ~ label .circle {
  animation-name: appear;
  animation-duration: 0.5ms;
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
}

#menu:checked ~ label .circle:hover {
  width: 80px;
  height: 80px;
  bottom: 5px;
  left: 5px;
}

@keyframes appear {
  0% {
    transform: scale(0.85) rotate(45deg);
  }
  50% {
    transform: scale(1.2);
  }
  100% {
    transform: scale(1);
  }
}
</style>