@@ -2099,6 +2099,27 @@ class Live2DManager {
20992099 finalUrl = `${ item . urlBase } ?lanlan_name=${ encodeURIComponent ( lanlanName ) } ` ;
21002100 // Live2D设置页直接跳转
21012101 window . location . href = finalUrl ;
2102+ } else if ( item . id === 'voice-clone' && item . url ) {
2103+ // 声音克隆页面也需要传递 lanlan_name
2104+ const lanlanName = ( window . lanlan_config && window . lanlan_config . lanlan_name ) || '' ;
2105+ finalUrl = `${ item . url } ?lanlan_name=${ encodeURIComponent ( lanlanName ) } ` ;
2106+
2107+ // 检查是否已有该URL的窗口打开
2108+ if ( this . _openSettingsWindows [ finalUrl ] ) {
2109+ const existingWindow = this . _openSettingsWindows [ finalUrl ] ;
2110+ if ( existingWindow && ! existingWindow . closed ) {
2111+ existingWindow . focus ( ) ;
2112+ return ;
2113+ } else {
2114+ delete this . _openSettingsWindows [ finalUrl ] ;
2115+ }
2116+ }
2117+
2118+ // 打开新窗口并保存引用
2119+ const newWindow = window . open ( finalUrl , '_blank' , 'width=1000,height=800,menubar=no,toolbar=no,location=no,status=no' ) ;
2120+ if ( newWindow ) {
2121+ this . _openSettingsWindows [ finalUrl ] = newWindow ;
2122+ }
21022123 } else {
21032124 // 其他页面弹出新窗口,但检查是否已打开
21042125 // 检查是否已有该URL的窗口打开
@@ -2170,10 +2191,17 @@ class Live2DManager {
21702191 popup . style . transform = 'translateX(-10px)' ;
21712192 setTimeout ( ( ) => {
21722193 popup . style . display = 'none' ;
2173- // 重置位置
2194+ // 重置位置和样式
21742195 popup . style . left = '100%' ;
21752196 popup . style . right = 'auto' ;
21762197 popup . style . top = '0' ;
2198+ popup . style . marginLeft = '8px' ;
2199+ popup . style . marginRight = '0' ;
2200+ // 重置高度限制,确保下次打开时状态一致
2201+ if ( buttonId === 'settings' || buttonId === 'agent' ) {
2202+ popup . style . maxHeight = '200px' ;
2203+ popup . style . overflowY = 'auto' ;
2204+ }
21772205 } , 200 ) ;
21782206 } else {
21792207 // 如果隐藏,则显示
@@ -2182,52 +2210,73 @@ class Live2DManager {
21822210 popup . style . opacity = '0' ;
21832211 popup . style . visibility = 'visible' ;
21842212
2185- // 等待一帧让浏览器计算布局
2186- setTimeout ( ( ) => {
2187- const popupRect = popup . getBoundingClientRect ( ) ;
2188- const screenWidth = window . innerWidth ;
2189- const screenHeight = window . innerHeight ;
2190- const rightMargin = 20 ; // 距离屏幕右侧的安全边距
2191- const bottomMargin = 60 ; // 距离屏幕底部的安全边距(考虑系统任务栏,Windows任务栏约40-48px)
2192-
2193- // 检查是否超出屏幕右侧
2194- const popupRight = popupRect . right ;
2195- if ( popupRight > screenWidth - rightMargin ) {
2196- // 超出右边界,改为向左弹出
2197- // 获取按钮的实际宽度来计算正确的偏移
2198- const button = document . getElementById ( `live2d-btn-${ buttonId } ` ) ;
2199- const buttonWidth = button ? button . offsetWidth : 48 ;
2200- const gap = 8 ;
2201-
2202- // 让弹出框完全移到按钮左侧,不遮挡按钮
2203- popup . style . left = 'auto' ;
2204- popup . style . right = '0' ;
2205- popup . style . marginLeft = '0' ;
2206- popup . style . marginRight = `${ buttonWidth + gap } px` ;
2207- popup . style . transform = 'translateX(10px)' ; // 反向动画
2213+ // 关键:在计算位置之前,先移除高度限制,确保获取真实尺寸
2214+ if ( buttonId === 'settings' || buttonId === 'agent' ) {
2215+ popup . style . maxHeight = 'none' ;
2216+ popup . style . overflowY = 'visible' ;
2217+ }
2218+
2219+ // 等待popup内的所有图片加载完成,确保尺寸准确
2220+ const images = popup . querySelectorAll ( 'img' ) ;
2221+ const imageLoadPromises = Array . from ( images ) . map ( img => {
2222+ if ( img . complete ) {
2223+ return Promise . resolve ( ) ;
22082224 }
2225+ return new Promise ( resolve => {
2226+ img . onload = resolve ;
2227+ img . onerror = resolve ; // 即使加载失败也继续
2228+ // 超时保护:最多等待100ms
2229+ setTimeout ( resolve , 100 ) ;
2230+ } ) ;
2231+ } ) ;
2232+
2233+ Promise . all ( imageLoadPromises ) . then ( ( ) => {
2234+ // 强制触发reflow,确保布局完全更新
2235+ void popup . offsetHeight ;
22092236
2210- // 检查是否超出屏幕底部(设置弹出框或其他较高的弹出框)
2211- if ( buttonId === 'settings' || buttonId === 'agent' ) {
2212- const popupBottom = popupRect . bottom ;
2213- if ( popupBottom > screenHeight - bottomMargin ) {
2214- // 计算需要向上移动的距离
2215- const overflow = popupBottom - ( screenHeight - bottomMargin ) ;
2216- const currentTop = parseInt ( popup . style . top ) || 0 ;
2217- const newTop = currentTop - overflow ;
2218- popup . style . top = `${ newTop } px` ;
2237+ // 再次使用RAF确保布局稳定
2238+ requestAnimationFrame ( ( ) => {
2239+ const popupRect = popup . getBoundingClientRect ( ) ;
2240+ const screenWidth = window . innerWidth ;
2241+ const screenHeight = window . innerHeight ;
2242+ const rightMargin = 20 ; // 距离屏幕右侧的安全边距
2243+ const bottomMargin = 60 ; // 距离屏幕底部的安全边距(考虑系统任务栏,Windows任务栏约40-48px)
2244+
2245+ // 检查是否超出屏幕右侧
2246+ const popupRight = popupRect . right ;
2247+ if ( popupRight > screenWidth - rightMargin ) {
2248+ // 超出右边界,改为向左弹出
2249+ // 获取按钮的实际宽度来计算正确的偏移
2250+ const button = document . getElementById ( `live2d-btn-${ buttonId } ` ) ;
2251+ const buttonWidth = button ? button . offsetWidth : 48 ;
2252+ const gap = 8 ;
2253+
2254+ // 让弹出框完全移到按钮左侧,不遮挡按钮
2255+ popup . style . left = 'auto' ;
2256+ popup . style . right = '0' ;
2257+ popup . style . marginLeft = '0' ;
2258+ popup . style . marginRight = `${ buttonWidth + gap } px` ;
2259+ popup . style . transform = 'translateX(10px)' ; // 反向动画
22192260 }
22202261
2221- // 取消maxHeight限制,让内容完整显示
2222- popup . style . maxHeight = 'none' ;
2223- popup . style . overflowY = 'visible' ;
2224- }
2225-
2226- // 显示弹出框
2227- popup . style . visibility = 'visible' ;
2228- popup . style . opacity = '1' ;
2229- popup . style . transform = 'translateX(0)' ;
2230- } , 10 ) ;
2262+ // 检查是否超出屏幕底部(设置弹出框或其他较高的弹出框)
2263+ if ( buttonId === 'settings' || buttonId === 'agent' ) {
2264+ const popupBottom = popupRect . bottom ;
2265+ if ( popupBottom > screenHeight - bottomMargin ) {
2266+ // 计算需要向上移动的距离
2267+ const overflow = popupBottom - ( screenHeight - bottomMargin ) ;
2268+ const currentTop = parseInt ( popup . style . top ) || 0 ;
2269+ const newTop = currentTop - overflow ;
2270+ popup . style . top = `${ newTop } px` ;
2271+ }
2272+ }
2273+
2274+ // 显示弹出框
2275+ popup . style . visibility = 'visible' ;
2276+ popup . style . opacity = '1' ;
2277+ popup . style . transform = 'translateX(0)' ;
2278+ } ) ;
2279+ } ) ;
22312280
22322281 // 设置、agent、麦克风弹出框不自动隐藏,其他的1秒后隐藏
22332282 if ( buttonId !== 'settings' && buttonId !== 'agent' && buttonId !== 'mic' ) {
0 commit comments