refactor(animation): 优化SVG容器布局并添加窗口大小监听
调整了SVG容器的布局和样式,使其更适应不同屏幕尺寸。添加了窗口大小变化的监听功能,动态更新SVG的viewBox属性,确保内容始终居中且比例正确。同时优化了按钮的定位和样式,提升了用户体验。
Showing
1 changed file
with
82 additions
and
15 deletions
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-03-28 09:23:04 | 2 | * @Date: 2025-03-28 09:23:04 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-03-28 21:28:07 | 4 | + * @LastEditTime: 2025-03-28 21:56:54 |
| 5 | * @FilePath: /mlaj/src/views/animation.vue | 5 | * @FilePath: /mlaj/src/views/animation.vue |
| 6 | * @Description: 贝塞尔曲线动画路径组件 | 6 | * @Description: 贝塞尔曲线动画路径组件 |
| 7 | * | 7 | * |
| ... | @@ -13,7 +13,7 @@ | ... | @@ -13,7 +13,7 @@ |
| 13 | --> | 13 | --> |
| 14 | <template> | 14 | <template> |
| 15 | <div class="animation-container"> | 15 | <div class="animation-container"> |
| 16 | - <div style="position: fixed; top: 0; right: 0"> | 16 | + <div style="position: fixed; top: 100px; right: 0; z-index: 999;"> |
| 17 | <button | 17 | <button |
| 18 | class="next-button" | 18 | class="next-button" |
| 19 | @click="nextStep" | 19 | @click="nextStep" |
| ... | @@ -22,7 +22,13 @@ | ... | @@ -22,7 +22,13 @@ |
| 22 | 下一步 | 22 | 下一步 |
| 23 | </button> | 23 | </button> |
| 24 | </div> | 24 | </div> |
| 25 | - <svg width="100%" height="100%" viewBox="0 0 1000 1200" class="animation-svg"> | 25 | + <svg |
| 26 | + width="100%" | ||
| 27 | + height="100%" | ||
| 28 | + :viewBox="svgViewBox" | ||
| 29 | + class="animation-svg" | ||
| 30 | + preserveAspectRatio="xMidYMid meet" | ||
| 31 | + > | ||
| 26 | <!-- 定义箭头标记 --> | 32 | <!-- 定义箭头标记 --> |
| 27 | <defs> | 33 | <defs> |
| 28 | <marker | 34 | <marker |
| ... | @@ -102,7 +108,7 @@ | ... | @@ -102,7 +108,7 @@ |
| 102 | </template> | 108 | </template> |
| 103 | 109 | ||
| 104 | <script setup> | 110 | <script setup> |
| 105 | -import { ref, computed } from "vue"; | 111 | +import { ref, computed, onMounted, onUnmounted } from "vue"; |
| 106 | import img_svg1 from "@/assets/1.svg"; | 112 | import img_svg1 from "@/assets/1.svg"; |
| 107 | import img_svg2 from "@/assets/2.svg"; | 113 | import img_svg2 from "@/assets/2.svg"; |
| 108 | import img_svg3 from "@/assets/3.svg"; | 114 | import img_svg3 from "@/assets/3.svg"; |
| ... | @@ -273,33 +279,94 @@ const svgObj = [ | ... | @@ -273,33 +279,94 @@ const svgObj = [ |
| 273 | }, | 279 | }, |
| 274 | }, | 280 | }, |
| 275 | ]; | 281 | ]; |
| 282 | +// 计算SVG容器尺寸 | ||
| 283 | +const containerSize = computed(() => { | ||
| 284 | + const container = document.querySelector('.animation-container'); | ||
| 285 | + if (!container) return { width: 0, height: 0 }; | ||
| 286 | + return { | ||
| 287 | + width: container.clientWidth, | ||
| 288 | + height: container.clientHeight | ||
| 289 | + }; | ||
| 290 | +}); | ||
| 291 | + | ||
| 292 | +// 监听窗口大小变化 | ||
| 293 | +const updateContainerSize = () => { | ||
| 294 | + containerSize.value = { | ||
| 295 | + width: document.querySelector('.animation-container')?.clientWidth || 0, | ||
| 296 | + height: document.querySelector('.animation-container')?.clientHeight || 0 | ||
| 297 | + }; | ||
| 298 | +}; | ||
| 299 | + | ||
| 300 | +onMounted(() => { | ||
| 301 | + updateContainerSize(); | ||
| 302 | + window.addEventListener('resize', updateContainerSize); | ||
| 303 | +}); | ||
| 304 | + | ||
| 305 | +onUnmounted(() => { | ||
| 306 | + window.removeEventListener('resize', updateContainerSize); | ||
| 307 | +}); | ||
| 308 | + | ||
| 309 | +// 更新svgViewBox计算属性 | ||
| 310 | +const svgViewBox = computed(() => { | ||
| 311 | + const maxX = Math.max(...points.value.map(p => p.x)); | ||
| 312 | + const maxY = Math.max(...points.value.map(p => p.y)); | ||
| 313 | + const minX = Math.min(...points.value.map(p => p.x)); | ||
| 314 | + const minY = Math.min(...points.value.map(p => p.y)); | ||
| 315 | + const padding = 100; | ||
| 316 | + | ||
| 317 | + // 计算内容的宽高比 | ||
| 318 | + const contentWidth = maxX - minX + padding * 2; | ||
| 319 | + const contentHeight = maxY - minY + padding * 2; | ||
| 320 | + const contentRatio = contentWidth / contentHeight; | ||
| 321 | + | ||
| 322 | + // 获取容器的宽高比 | ||
| 323 | + const containerRatio = containerSize.value.width / containerSize.value.height; | ||
| 324 | + | ||
| 325 | + // 根据宽高比调整viewBox | ||
| 326 | + if (containerRatio > contentRatio) { | ||
| 327 | + // 容器更宽,以高度为基准 | ||
| 328 | + const adjustedWidth = contentHeight * containerRatio; | ||
| 329 | + const extraPadding = (adjustedWidth - contentWidth) / 2; | ||
| 330 | + return `${minX - padding - extraPadding} ${minY - padding} ${adjustedWidth} ${contentHeight}`; | ||
| 331 | + } else { | ||
| 332 | + // 容器更高,以宽度为基准 | ||
| 333 | + const adjustedHeight = containerRatio ? contentWidth / containerRatio : contentHeight; | ||
| 334 | + const extraPadding = isFinite(adjustedHeight) ? (adjustedHeight - contentHeight) / 2 : 0; | ||
| 335 | + return `${minX - padding} ${minY - padding - extraPadding} ${contentWidth} ${adjustedHeight}`; | ||
| 336 | + } | ||
| 337 | +}); | ||
| 276 | </script> | 338 | </script> |
| 277 | 339 | ||
| 278 | <style scoped> | 340 | <style scoped> |
| 279 | .animation-container { | 341 | .animation-container { |
| 280 | - display: flex; | 342 | + position: relative; |
| 281 | - flex-direction: column; | 343 | + width: 100%; |
| 282 | - justify-content: center; | 344 | + height: 100vh; |
| 283 | - align-items: center; | ||
| 284 | - min-height: 100vh; | ||
| 285 | background: #f5f5f5; | 345 | background: #f5f5f5; |
| 286 | - padding: 1.25rem; | 346 | + padding: 1rem; |
| 287 | box-sizing: border-box; | 347 | box-sizing: border-box; |
| 348 | + overflow: hidden; | ||
| 288 | } | 349 | } |
| 289 | 350 | ||
| 290 | .animation-svg { | 351 | .animation-svg { |
| 291 | - max-width: 100%; | 352 | + position: absolute; |
| 292 | - max-height: 100vh; | 353 | + top: 0; |
| 293 | - margin: 0 auto; | 354 | + left: 0; |
| 355 | + width: 100%; | ||
| 356 | + height: 100%; | ||
| 357 | + margin: 0; | ||
| 358 | + display: block; | ||
| 294 | } | 359 | } |
| 295 | 360 | ||
| 296 | @media screen and (max-width: 768px) { | 361 | @media screen and (max-width: 768px) { |
| 297 | .animation-container { | 362 | .animation-container { |
| 298 | - padding: 0.9375rem; | 363 | + padding: 0.5rem; |
| 299 | } | 364 | } |
| 300 | 365 | ||
| 301 | .animation-svg { | 366 | .animation-svg { |
| 302 | - height: 80vh; | 367 | + width: 100%; |
| 368 | + height: auto; | ||
| 369 | + min-height: 90vh; | ||
| 303 | } | 370 | } |
| 304 | 371 | ||
| 305 | .next-button { | 372 | .next-button { | ... | ... |
-
Please register or login to post a comment