1313 @touchstart =" handlePlayerClick"
1414 ></div >
1515 <transition name =" fade" >
16- <div v-if =" showDialog" class =" chatdialog" >{{ currentDialog }}</div >
16+ <div v-if =" showDialog" class =" chatdialog-container" >
17+ <div class =" chatdialog-triangle" ></div >
18+ <div class =" chatdialog" >{{ currentDialog }}</div >
19+ </div >
1720 </transition >
1821 </template >
1922</template >
2023
2124<script setup lang="js">
22- import { onMounted , ref , onUnmounted , watch , computed } from ' vue'
25+ import { onMounted , ref , watch , computed } from ' vue'
2326
2427import { useStore } from ' ../../store'
2528const { state } = useStore ()
@@ -132,11 +135,16 @@ const AudioManager = {
132135 context: null ,
133136 buffers: new Map (),
134137 currentSource: null ,
138+ gainNode: null ,
135139
136140 initialize () {
137141 if (! clientReady .value ) return
138142 if (! this .context ) {
139143 this .context = new (window .AudioContext || window .webkitAudioContext )();
144+ // 创建增益节点,设置音量为50%
145+ this .gainNode = this .context .createGain ();
146+ this .gainNode .gain .value = 0.5 ;
147+ this .gainNode .connect (this .context .destination );
140148 }
141149 },
142150
@@ -167,7 +175,8 @@ const AudioManager = {
167175 return new Promise ((resolve ) => {
168176 const source = this .context .createBufferSource ();
169177 source .buffer = buffer;
170- source .connect (this .context .destination );
178+ // 连接到增益节点而不是直接连接到目标
179+ source .connect (this .gainNode );
171180 source .onended = () => {
172181 if (this .currentSource === source) {
173182 this .currentSource = null ;
@@ -216,7 +225,7 @@ const preloadAudio = async () => {
216225}
217226
218227const handleScroll = () => {
219- if (! clientReady .value ) return
228+ if (! clientReady .value || ! playerContainer . value ) return
220229 const bottomReached = window .innerHeight + window .scrollY + 1 >= document .body .offsetHeight
221230 const chatDialog = document .querySelector (' .chatdialog' )
222231
@@ -476,12 +485,22 @@ const isDarkMode = computed(() => state.darkMode === 'dark')
476485const isEnabled = computed (() => state .SpinePlayerEnabled )
477486const currentAssets = computed (() => spineAssets[currentCharacter .value ])
478487
488+ // 事件委托
489+ const handleEvents = (event ) => {
490+ if (event .type === ' scroll' ) {
491+ handleScroll ()
492+ } else if ([' mousemove' , ' touchmove' ].includes (event .type )) {
493+ moveBonesHandler? .(event )
494+ }
495+ }
496+
479497// 统一的清理函数
480498const cleanup = () => {
481499 if (blinkInterval) clearTimeout (blinkInterval)
482500 if (eyeControlTimer) clearTimeout (eyeControlTimer)
483501
484- // 清理鼠标移动监听
502+ // 清理监听事件
503+ window .removeEventListener (' scroll' , handleEvents)
485504 if (moveBonesHandler && ! isMobileDevice ()) {
486505 window .removeEventListener (' mousemove' , moveBonesHandler)
487506 moveBonesHandler = null
@@ -525,21 +544,22 @@ const initializeCharacter = async () => {
525544
526545const debouncedInitialize = debounce (initializeCharacter, 300 )
527546
528- // 监听器
547+ // 监听主题切换和spine开关
529548watch ([isDarkMode, isEnabled], async ([dark , enabled ], [prevDark , prevEnabled ]) => {
530- if (enabled !== prevEnabled || (enabled && dark !== prevDark)) {
549+ if (enabled !== prevEnabled) {
550+ if (enabled) {
551+ // 启用时初始化
552+ debouncedInitialize ()
553+ } else {
554+ // 禁用时清理资源
555+ cleanup ()
556+ }
557+ } else if (enabled && dark !== prevDark) {
558+ // 主题变更且启用状态下重新初始化
531559 debouncedInitialize ()
532560 }
533561}, { immediate: true })
534562
535- // 事件委托
536- const handleEvents = (event ) => {
537- if (event .type === ' scroll' ) {
538- handleScroll ()
539- } else if ([' mousemove' , ' touchmove' ].includes (event .type )) {
540- moveBonesHandler? .(event )
541- }
542- }
543563
544564onMounted (() => {
545565 // 设置客户端就绪状态
@@ -556,12 +576,6 @@ onMounted(() => {
556576 debouncedInitialize ()
557577 }
558578})
559-
560- onUnmounted (() => {
561- window .removeEventListener (' scroll' , handleEvents)
562- window .removeEventListener (' mousemove' , handleEvents)
563- cleanup ()
564- })
565579< / script>
566580
567581< style scoped lang= " less" >
@@ -576,33 +590,39 @@ onUnmounted(() => {
576590 transition: all 1s ;
577591 cursor: pointer;
578592}
579- .chatdialog {
593+ .chatdialog - container {
580594 position: fixed;
581595 bottom: 10vw ;
582596 left: 2vw ;
597+ z- index: 101 ;
598+ transition: all 1s ;
599+ pointer- events: none;
600+ filter: drop- shadow (0 0 3px rgba (36 , 36 , 36 , 0.6 ));
601+ }
602+
603+ .chatdialog - triangle {
604+ position: absolute;
605+ left: 2vw ;
606+ top: - 10px ;
607+ width: 0 ;
608+ height: 0 ;
609+ border- left: 10px solid transparent;
610+ border- right: 10px solid transparent;
611+ border- bottom: 10px solid rgba (255 , 255 , 255 , 0.9 );
612+ z- index: 101 ;
613+ }
614+
615+ .chatdialog {
583616 background- color: rgba (255 , 255 , 255 , 0.9 );
584617 border- radius: 25px ;
585618 padding: 12px 24px ;
586- box- shadow: 0 2px 8px rgba (0 , 0 , 0 , 0.15 );
587- z- index: 101 ;
588619 word- wrap: break - word;
589620 white- space: pre- wrap;
590621 line- height: 1.4 ;
591622 color: #000000 ;
592623 font- size: 0 .8vw ;
593624 user- select: none;
594- transition: all 1s ;
595-
596- & : after {
597- content: ' ' ;
598- position: absolute;
599- left: 2vw ;
600- top: - 8px ;
601- border- left: 10px solid transparent;
602- border- right: 10px solid transparent;
603- border- bottom: 10px solid rgba (255 , 255 , 255 , 0.9 );
604- border- top: none;
605- }
625+ pointer- events: auto;
606626}
607627
608628// 添加淡入淡出动画
@@ -617,19 +637,22 @@ onUnmounted(() => {
617637}
618638
619639@media (max - width : 768px ) {
620- .chatdialog {
640+ .chatdialog - container {
621641 left: 2vh ;
622642 bottom: 10vh ;
643+ }
644+
645+ .chatdialog {
623646 min- width: auto;
624647 padding: 12px 20px ;
625648 font- size: 1vh ;
626649 border- radius: 20px ;
650+ }
627651
628- & : after {
629- left: 35px ;
630- border- width: 8px ;
631- top: - 7px ;
632- }
652+ .chatdialog - triangle {
653+ left: 35px ;
654+ border- width: 8px ;
655+ top: - 8px ;
633656 }
634657
635658 .playerContainer {
0 commit comments