modelAirDoorSVG.vue 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. <template>
  2. <div class="air-door-container" @click="toggleDoor">
  3. <!-- 添加3D变换专用包装器 -->
  4. <div class="svg-3d-wrapper">
  5. <svg viewBox="0 0 800 400" width="800" height="400">
  6. <!-- 定义渐变和图案 -->
  7. <defs>
  8. <linearGradient id="tunnelGradient" x1="0%" y1="0%" x2="100%" y2="0%">
  9. <stop offset="0%" stop-color="#222" />
  10. <stop offset="50%" stop-color="#444" />
  11. <stop offset="100%" stop-color="#222" />
  12. </linearGradient>
  13. <pattern id="tunnelBottomPattern" width="40" height="40" patternUnits="userSpaceOnUse">
  14. <!-- <rect width="40" height="40" fill="#333" fill-opacity="0.8" /> -->
  15. </pattern>
  16. </defs>
  17. <!-- 巷道顶部 - 只有左上和右上圆角 -->
  18. <path d="M0,20 A20,20 0 0 1 20,0 H780 A20,20 0 0 1 800,20 V120 H0 Z"
  19. fill="url(#tunnelGradient)" />
  20. <!-- 巷道底部 -->
  21. <rect x="0" y="120" width="800" height="300" fill="url(#tunnelBottomPattern)" />
  22. <!-- 门框 -->
  23. <rect x="8" y="128" width="784" height="272" rx="20" ry="10" fill="none" stroke="#8B4513" stroke-width="8" />
  24. <!-- 左门 -->
  25. <g
  26. :class="{ 'left-door-open': isOpen, 'left-door-closed': !isOpen }"
  27. transform-origin="left center"
  28. >
  29. <rect
  30. x="16"
  31. y="136"
  32. width="384"
  33. height="256"
  34. fill="#A9A9A9"
  35. stroke="#696969"
  36. stroke-width="2"
  37. />
  38. <line x1="200" y1="146" x2="200" y2="392" stroke="#696969" stroke-width="1" />
  39. <line x1="16" y1="264" x2="399" y2="264" stroke="#696969" stroke-width="1" />
  40. </g>
  41. <!-- 右门 -->
  42. <g
  43. :class="{ 'right-door-open': isOpen, 'right-door-closed': !isOpen }"
  44. transform-origin="right center"
  45. >
  46. <rect
  47. x="400"
  48. y="136"
  49. width="384"
  50. height="256"
  51. fill="#A9A9A9"
  52. stroke="#696969"
  53. stroke-width="2"
  54. />
  55. <line x1="584" y1="146" x2="584" y2="392" stroke="#696969" stroke-width="1" />
  56. <line x1="400" y1="264" x2="783" y2="264" stroke="#696969" stroke-width="1" />
  57. </g>
  58. </svg>
  59. </div>
  60. <div class="status">
  61. {{ isOpen ? '风门开启' : '风门关闭' }}
  62. </div>
  63. </div>
  64. </template>
  65. <script lang="ts" setup>
  66. import { ref } from 'vue';
  67. const isOpen = ref(false);
  68. const toggleDoor = () => {
  69. isOpen.value = !isOpen.value;
  70. };
  71. </script>
  72. <style scoped>
  73. .air-door-container {
  74. position: relative;
  75. width: 800px;
  76. height: 400px;
  77. margin: 0 auto;
  78. }
  79. .svg-3d-wrapper {
  80. width: 100%;
  81. height: 100%;
  82. perspective: 800px; /* 透视距离 */
  83. transform-style: preserve-3d;
  84. }
  85. svg {
  86. display: block;
  87. overflow: visible; /* 确保旋转内容不被裁剪 */
  88. transform-style: preserve-3d;
  89. }
  90. /* 门元素3D优化 */
  91. g {
  92. transform-box: fill-box;
  93. backface-visibility: visible;
  94. will-change: transform; /* 性能优化 */
  95. }
  96. /* 左门打开动画 */
  97. @keyframes leftDoorOpen {
  98. from { transform: translateZ(1px) rotateY(0deg); }
  99. to { transform: translateZ(1px) rotateY(-75deg); }
  100. }
  101. /* 左门关闭动画 */
  102. @keyframes leftDoorClose {
  103. from { transform: translateZ(1px) rotateY(-75deg); }
  104. to { transform: translateZ(1px) rotateY(0deg); }
  105. }
  106. /* 右门打开动画 */
  107. @keyframes rightDoorOpen {
  108. from { transform: translateZ(1px) rotateY(0deg); }
  109. to { transform: translateZ(1px) rotateY(-75deg); }
  110. }
  111. /* 右门关闭动画 */
  112. @keyframes rightDoorClose {
  113. from { transform: translateZ(1px) rotateY(-75deg); }
  114. to { transform: translateZ(1px) rotateY(0deg); }
  115. }
  116. /* 左门打开状态 */
  117. .left-door-open {
  118. animation: leftDoorOpen 2s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  119. }
  120. /* 左门关闭状态 */
  121. .left-door-closed {
  122. animation: leftDoorClose 2s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  123. }
  124. /* 右门打开状态 */
  125. .right-door-open {
  126. animation: rightDoorOpen 2s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  127. }
  128. /* 右门关闭状态 */
  129. .right-door-closed {
  130. animation: rightDoorClose 2s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  131. }
  132. .status {
  133. position: absolute;
  134. top: 10px;
  135. left: 0;
  136. width: 100%;
  137. text-align: center;
  138. font-size: 18px;
  139. font-weight: bold;
  140. color: #fff;
  141. text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
  142. pointer-events: none; /* 防止遮挡点击事件 */
  143. }
  144. </style>