feat: remake machanicalbutton
This commit is contained in:
		@@ -1,37 +1,52 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <svg xmlns="http://www.w3.org/2000/svg" :width="props.width" :height="props.height" viewBox="0 0 1600 1600">
 | 
			
		||||
    <def>
 | 
			
		||||
      <filter id="shadow" x="-50%" y="-50%" width="200%" height="200%">
 | 
			
		||||
        <feGaussianBlur in="SourceAlpha" stdDeviation="20" result="blur" />
 | 
			
		||||
        <feColorMatrix result="bluralpha" type="matrix" :values="colorMatrix" />
 | 
			
		||||
        <feOffset in="bluralpha" dx="20" dy="20" result="offsetBlur" />
 | 
			
		||||
        <feMerge>
 | 
			
		||||
          <feMergeNode in="offsetBlur" />
 | 
			
		||||
          <feMergeNode in="SourceGraphic" />
 | 
			
		||||
        </feMerge>
 | 
			
		||||
      </filter>
 | 
			
		||||
    </def>
 | 
			
		||||
    <g>
 | 
			
		||||
  <div class="relative" :style="{width: `${props.width}px`, height: `${props.height}px`}">
 | 
			
		||||
    <!-- 简化的SVG按钮 -->
 | 
			
		||||
    <svg xmlns="http://www.w3.org/2000/svg" :width="props.width" :height="props.height" viewBox="0 0 1600 1600">
 | 
			
		||||
      <defs>
 | 
			
		||||
        <filter id="btn-shadow">
 | 
			
		||||
          <feGaussianBlur in="SourceAlpha" stdDeviation="20" result="blur" />
 | 
			
		||||
          <feColorMatrix result="bluralpha" type="matrix" :values="colorMatrix" />
 | 
			
		||||
          <feOffset in="bluralpha" dx="20" dy="20" result="offsetBlur" />
 | 
			
		||||
          <feMerge>
 | 
			
		||||
            <feMergeNode in="offsetBlur" />
 | 
			
		||||
            <feMergeNode in="SourceGraphic" />
 | 
			
		||||
          </feMerge>
 | 
			
		||||
        </filter>
 | 
			
		||||
        <linearGradient id="normal" gradientTransform="rotate(45 0 0)">
 | 
			
		||||
          <stop stop-color="#4b4b4b" offset="0" />
 | 
			
		||||
          <stop stop-color="#171717" offset="1" />
 | 
			
		||||
        </linearGradient>
 | 
			
		||||
        <linearGradient id="pressed" gradientTransform="rotate(45 0 0)">
 | 
			
		||||
          <stop stop-color="#171717" offset="0" />
 | 
			
		||||
          <stop stop-color="#4b4b4b" offset="1" />
 | 
			
		||||
        </linearGradient>
 | 
			
		||||
      </defs>
 | 
			
		||||
      
 | 
			
		||||
      <!-- 按钮底座 -->
 | 
			
		||||
      <rect width="800" height="800" x="400" y="400" fill="#464646" rx="20" />
 | 
			
		||||
      <rect width="700" height="700" x="450" y="450" fill="#eaeaea" rx="20" />
 | 
			
		||||
      
 | 
			
		||||
      <!-- 装饰螺丝 -->
 | 
			
		||||
      <circle r="20" cx="1075" cy="1075" fill="#171717" />
 | 
			
		||||
      <circle r="20" cx="1075" cy="525" fill="#171717" />
 | 
			
		||||
      <circle r="20" cx="525" cy="525" fill="#171717" />
 | 
			
		||||
      <circle r="20" cx="525" cy="1075" fill="#171717" />
 | 
			
		||||
    </g>
 | 
			
		||||
    <g>
 | 
			
		||||
      <linearGradient id="normal" gradientTransform="rotate(45 0 0)">
 | 
			
		||||
        <stop stop-color="#4b4b4b" offset="0" />
 | 
			
		||||
        <stop stop-color="#171717" offset="1" />
 | 
			
		||||
      </linearGradient>
 | 
			
		||||
      <linearGradient id="pressed" gradientTransform="rotate(45 0 0)">
 | 
			
		||||
        <stop stop-color="#171717" offset="0" />
 | 
			
		||||
        <stop stop-color="#4b4b4b" offset="1" />
 | 
			
		||||
      </linearGradient>
 | 
			
		||||
      <circle class="shadow" r="220" cx="800" cy="800" fill="black" />
 | 
			
		||||
      <circle class="light" @mousedown="toggleButtonState(true)" @mouseup="toggleButtonState(false)" 
 | 
			
		||||
        @contextmenu.prevent="openContextMenu($event)" :r="btnHeight" cx="800" cy="800"
 | 
			
		||||
        fill-opacity="0.9" style="pointer-events: auto;"/>
 | 
			
		||||
      
 | 
			
		||||
      <!-- 按钮主体 -->
 | 
			
		||||
      <circle r="220" cx="800" cy="800" fill="black" filter="url(#btn-shadow)" />
 | 
			
		||||
      <circle 
 | 
			
		||||
        :r="btnHeight" 
 | 
			
		||||
        cx="800" 
 | 
			
		||||
        cy="800" 
 | 
			
		||||
        :fill="isKeyPressed ? 'url(#pressed)' : 'url(#normal)'" 
 | 
			
		||||
        fill-opacity="0.9"
 | 
			
		||||
        @mousedown="toggleButtonState(true)" 
 | 
			
		||||
        @mouseup="toggleButtonState(false)" 
 | 
			
		||||
        @contextmenu.prevent="openContextMenu($event)"
 | 
			
		||||
        style="pointer-events: auto; transition: all 20ms ease-in-out;"
 | 
			
		||||
      />
 | 
			
		||||
      
 | 
			
		||||
      <!-- 按键文字 -->
 | 
			
		||||
      <text 
 | 
			
		||||
        v-if="bindKey"
 | 
			
		||||
        x="800" 
 | 
			
		||||
@@ -40,20 +55,23 @@
 | 
			
		||||
        text-anchor="middle"
 | 
			
		||||
        dominant-baseline="central"
 | 
			
		||||
        fill="#ccc"
 | 
			
		||||
        style="font-family: Arial; filter: url(#shadow); user-select: none; pointer-events: none; mix-blend-mode: overlay;"
 | 
			
		||||
        style="font-family: Arial; filter: url(#btn-shadow); user-select: none; pointer-events: none; mix-blend-mode: overlay;"
 | 
			
		||||
      >
 | 
			
		||||
        {{ bindKey.toUpperCase() }}
 | 
			
		||||
      </text>
 | 
			
		||||
    </g>
 | 
			
		||||
  </svg>
 | 
			
		||||
  <div v-if="showContextMenu"
 | 
			
		||||
       :style="{ top: contextMenuY + 'px', left: contextMenuX + 'px' }"
 | 
			
		||||
       @click.stop
 | 
			
		||||
       class="fixed z-50 border border-base-300 bg-base-100 rounded-md shadow-lg">
 | 
			
		||||
    <div class="px-4 py-2 cursor-pointer whitespace-nowrap text-base-content hover:bg-base-200"
 | 
			
		||||
         @click="startBinding">
 | 
			
		||||
      <span v-if="isBinding">请输入</span>
 | 
			
		||||
      <span v-else>绑定按键: {{ bindKey ? bindKey.toUpperCase() : '未绑定' }}</span>
 | 
			
		||||
    </svg>
 | 
			
		||||
    
 | 
			
		||||
    <!-- 使用DaisyUI的卡片组件实现上下文菜单 -->
 | 
			
		||||
    <div v-if="showContextMenu" 
 | 
			
		||||
         class="card card-compact fixed z-50 shadow-lg bg-base-100 border border-base-300"
 | 
			
		||||
         :style="{ top: contextMenuY + 'px', left: contextMenuX + 'px' }"
 | 
			
		||||
         @click.stop>
 | 
			
		||||
      <div class="card-body p-0">
 | 
			
		||||
        <button class="btn btn-ghost justify-start normal-case w-full h-full" @click="startBinding">
 | 
			
		||||
          <span v-if="isBinding">请输入</span>
 | 
			
		||||
          <span v-else>绑定按键: {{ bindKey ? bindKey.toUpperCase() : '未绑定' }}</span>
 | 
			
		||||
        </button>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
@@ -65,29 +83,28 @@ interface Props {
 | 
			
		||||
  width?: string | number
 | 
			
		||||
  height?: string | number
 | 
			
		||||
}
 | 
			
		||||
const bindKey = ref('');
 | 
			
		||||
let isKeyPressed = false;
 | 
			
		||||
 | 
			
		||||
const props = withDefaults(defineProps<Props>(), {
 | 
			
		||||
  width: 160,
 | 
			
		||||
  height: 160,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const btnHeight = ref(200)
 | 
			
		||||
 | 
			
		||||
const colorMatrix = ref("1 0 0 0   0  0 1 0 0   0  0 0 1 0   0  0 0 0 1   0")
 | 
			
		||||
 | 
			
		||||
function toggleButtonState(isPressed: boolean) {
 | 
			
		||||
  btnHeight.value = isPressed ? 210 : 200;
 | 
			
		||||
  colorMatrix.value = isPressed 
 | 
			
		||||
    ? "1 0 0 0   0  0 1 0 0   0  0 0 1 0   0  0 0 0 0.7   0"
 | 
			
		||||
    : "1 0 0 0   0  0 1 0 0   0  0 0 1 0   0  0 0 0 1   0";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const bindKey = ref('');
 | 
			
		||||
let isKeyPressed = false;
 | 
			
		||||
const btnHeight = ref(200);
 | 
			
		||||
const colorMatrix = ref("1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 1 0");
 | 
			
		||||
const isBinding = ref(false);
 | 
			
		||||
const showContextMenu = ref(false);
 | 
			
		||||
const contextMenuX = ref(0);
 | 
			
		||||
const contextMenuY = ref(0);
 | 
			
		||||
 | 
			
		||||
function toggleButtonState(isPressed: boolean) {
 | 
			
		||||
  btnHeight.value = isPressed ? 210 : 200;
 | 
			
		||||
  colorMatrix.value = isPressed 
 | 
			
		||||
    ? "1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 0.7 0"
 | 
			
		||||
    : "1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 1 0";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function openContextMenu(e: MouseEvent) {
 | 
			
		||||
  contextMenuX.value = e.clientX;
 | 
			
		||||
  contextMenuY.value = e.clientY;
 | 
			
		||||
@@ -135,21 +152,3 @@ onUnmounted(() => {
 | 
			
		||||
  window.removeEventListener('click', closeContextMenu);
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped lang="postcss">
 | 
			
		||||
circle {
 | 
			
		||||
  transition: all 20ms ease-in-out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.shadow {
 | 
			
		||||
  filter: url(#shadow);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.light:active {
 | 
			
		||||
  fill: url(#pressed);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.light {
 | 
			
		||||
  fill: url(#normal);
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user