🐞 fix: 修复歌词窗口拖动变大问题和多屏幕支持,优化字体样式

fixed: #77
This commit is contained in:
alger
2025-03-11 23:28:04 +08:00
parent e5e45148c3
commit 6a8813531f
2 changed files with 148 additions and 33 deletions

View File

@@ -5,6 +5,12 @@ import path, { join } from 'path';
const store = new Store();
let lyricWindow: BrowserWindow | null = null;
// 跟踪拖动状态
let isDragging = false;
// 添加窗口大小变化防护
let originalSize = { width: 0, height: 0 };
const createWin = () => {
console.log('Creating lyric window');
@@ -15,32 +21,73 @@ const createWin = () => {
y?: number;
width?: number;
height?: number;
displayId?: number;
}) || {};
const { x, y, width, height } = windowBounds;
// 获取屏幕尺寸
const { width: screenWidth, height: screenHeight } = screen.getPrimaryDisplay().workAreaSize;
const { x, y, width, height, displayId } = windowBounds;
// 验证保存的位置是否有效
const validPosition =
x !== undefined && y !== undefined && x >= 0 && y >= 0 && x < screenWidth && y < screenHeight;
// 获取所有屏幕的信息
const displays = screen.getAllDisplays();
let isValidPosition = false;
let targetDisplay = displays[0]; // 默认使用主显示器
// 如果有显示器ID尝试按ID匹配
if (displayId) {
const matchedDisplay = displays.find((d) => d.id === displayId);
if (matchedDisplay) {
targetDisplay = matchedDisplay;
console.log('Found matching display by ID:', displayId);
}
}
// 验证位置是否在任何显示器的范围内
if (x !== undefined && y !== undefined) {
for (const display of displays) {
const { bounds } = display;
if (
x >= bounds.x - 50 && // 允许一点偏移,避免卡在边缘
x < bounds.x + bounds.width + 50 &&
y >= bounds.y - 50 &&
y < bounds.y + bounds.height + 50
) {
isValidPosition = true;
targetDisplay = display;
break;
}
}
}
// 确保宽高合理
const defaultWidth = 800;
const defaultHeight = 200;
const validWidth = width && width > 0 ? width : defaultWidth;
const validHeight = height && height > 0 ? height : defaultHeight;
const maxWidth = 1600; // 设置最大宽度限制
const maxHeight = 800; // 设置最大高度限制
const validWidth = width && width > 0 && width <= maxWidth ? width : defaultWidth;
const validHeight = height && height > 0 && height <= maxHeight ? height : defaultHeight;
// 确定窗口位置
let windowX = isValidPosition ? x : undefined;
let windowY = isValidPosition ? y : undefined;
// 如果位置无效,默认在当前显示器中居中
if (windowX === undefined || windowY === undefined) {
windowX = targetDisplay.bounds.x + (targetDisplay.bounds.width - validWidth) / 2;
windowY = targetDisplay.bounds.y + (targetDisplay.bounds.height - validHeight) / 2;
}
lyricWindow = new BrowserWindow({
width: validWidth,
height: validHeight,
x: validPosition ? x : undefined,
y: validPosition ? y : undefined,
x: windowX,
y: windowY,
frame: false,
show: false,
transparent: true,
hasShadow: false,
alwaysOnTop: true,
resizable: true,
// 添加跨屏幕支持选项
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
sandbox: false,
@@ -58,6 +105,9 @@ const createWin = () => {
// 监听窗口大小变化事件,保存新的尺寸
lyricWindow.on('resize', () => {
// 如果正在拖动,忽略大小调整事件
if (isDragging) return;
if (lyricWindow && !lyricWindow.isDestroyed()) {
const [width, height] = lyricWindow.getSize();
const [x, y] = lyricWindow.getPosition();
@@ -154,28 +204,75 @@ export const loadLyricWindow = (ipcMain: IpcMain, mainWin: BrowserWindow): void
}
});
// 开始拖动时设置标志
ipcMain.on('lyric-drag-start', () => {
isDragging = true;
if (lyricWindow && !lyricWindow.isDestroyed()) {
// 记录原始窗口大小
const [width, height] = lyricWindow.getSize();
originalSize = { width, height };
// 在拖动时暂时禁用大小调整
lyricWindow.setResizable(false);
}
});
// 结束拖动时清除标志
ipcMain.on('lyric-drag-end', () => {
isDragging = false;
if (lyricWindow && !lyricWindow.isDestroyed()) {
// 确保窗口大小恢复原样
lyricWindow.setSize(originalSize.width, originalSize.height);
// 拖动结束后恢复可调整大小
lyricWindow.setResizable(true);
}
});
// 处理拖动移动
ipcMain.on('lyric-drag-move', (_, { deltaX, deltaY }) => {
if (!lyricWindow || lyricWindow.isDestroyed()) return;
if (!lyricWindow || lyricWindow.isDestroyed() || !isDragging) return;
const [currentX, currentY] = lyricWindow.getPosition();
const { width: screenWidth, height: screenHeight } = screen.getPrimaryDisplay().workAreaSize;
const [windowWidth, windowHeight] = lyricWindow.getSize();
// 计算新位置,确保窗口不会移出屏幕
const newX = Math.max(0, Math.min(currentX + deltaX, screenWidth - windowWidth));
const newY = Math.max(0, Math.min(currentY + deltaY, screenHeight - windowHeight));
// 使用记录的原始大小,而不是当前大小
const windowWidth = originalSize.width;
const windowHeight = originalSize.height;
lyricWindow.setPosition(newX, newY);
// 计算新位置
const newX = currentX + deltaX;
const newY = currentY + deltaY;
// 保存新位置但只保存位置信息不使用getBounds()避免在Windows下引起尺寸变化
const bounds = {
x: newX,
y: newY,
width: windowWidth, // 使用当前保存的宽度
height: windowHeight // 使用当前保存的高度
};
store.set('lyricWindowBounds', bounds);
try {
// 获取当前鼠标所在的显示器
const mousePoint = screen.getCursorScreenPoint();
const currentDisplay = screen.getDisplayNearestPoint(mousePoint);
// 拖动期间使用setBounds确保大小不变使用false避免动画卡顿
lyricWindow.setBounds(
{
x: newX,
y: newY,
width: windowWidth,
height: windowHeight
},
false
);
// 更新存储的位置
const windowBounds = {
x: newX,
y: newY,
width: windowWidth,
height: windowHeight,
displayId: currentDisplay.id // 记录当前显示器ID有助于多屏幕处理
};
store.set('lyricWindowBounds', windowBounds);
} catch (error) {
console.error('Error during window drag:', error);
// 出错时尝试使用更简单的方法
lyricWindow.setPosition(newX, newY);
}
});
// 添加鼠标穿透事件处理

View File

@@ -494,6 +494,8 @@ watch(
// 添加拖动相关变量
const isDragging = ref(false);
const startPosition = ref({ x: 0, y: 0 });
const lastMoveTime = ref(0);
const moveThrottleMs = 10; // 限制拖动事件发送频率,提高性能
// 处理鼠标按下事件
const handleMouseDown = (e: MouseEvent) => {
@@ -501,7 +503,8 @@ const handleMouseDown = (e: MouseEvent) => {
if (
lyricSetting.value.isLock ||
(e.target as HTMLElement).closest('.control-buttons') ||
(e.target as HTMLElement).closest('.font-size-controls')
(e.target as HTMLElement).closest('.font-size-controls') ||
(e.target as HTMLElement).closest('.play-controls')
) {
return;
}
@@ -511,23 +514,38 @@ const handleMouseDown = (e: MouseEvent) => {
isDragging.value = true;
startPosition.value = { x: e.screenX, y: e.screenY };
lastMoveTime.value = performance.now();
// 发送拖动开始信号到主进程
windowData.electron.ipcRenderer.send('lyric-drag-start');
// 添加全局鼠标事件监听
const handleMouseMove = (e: MouseEvent) => {
if (!isDragging.value) return;
// 时间节流,避免过于频繁的更新
const now = performance.now();
if (now - lastMoveTime.value < moveThrottleMs) return;
lastMoveTime.value = now;
const deltaX = e.screenX - startPosition.value.x;
const deltaY = e.screenY - startPosition.value.y;
// 发送移动事件到主进程
windowData.electron.ipcRenderer.send('lyric-drag-move', { deltaX, deltaY });
startPosition.value = { x: e.screenX, y: e.screenY };
// 只有在实际移动时才发送事件
if (Math.abs(deltaX) > 0 || Math.abs(deltaY) > 0) {
// 发送移动事件到主进程
windowData.electron.ipcRenderer.send('lyric-drag-move', { deltaX, deltaY });
startPosition.value = { x: e.screenX, y: e.screenY };
}
};
const handleMouseUp = () => {
if (!isDragging.value) return;
isDragging.value = false;
// 发送拖动结束信号到主进程
windowData.electron.ipcRenderer.send('lyric-drag-end');
// 移除事件监听
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
@@ -573,7 +591,7 @@ const handleNext = () => {
};
</script>
<style>
<style scoped>
body {
background-color: transparent !important;
}
@@ -589,9 +607,10 @@ body {
user-select: none;
transition: background-color 0.2s ease;
cursor: default;
border-radius: 14px;
&:hover {
background: rgba(0, 0, 0, 0.5);
background: rgba(44, 44, 44, 0.466);
.control-bar {
&-show {
opacity: 1;
@@ -750,16 +769,15 @@ body {
color: var(--text-color);
white-space: pre-wrap;
word-break: break-all;
text-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
transition: all 0.2s ease;
line-height: 1.4;
-webkit-text-stroke: 0.5px #0000008a;
}
.lyric-translation {
color: var(--text-secondary);
white-space: pre-wrap;
word-break: break-all;
text-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
transition: font-size 0.2s ease;
line-height: 1.4; // 添加行高比例
}