feat(字幕样式): 添加字幕样式保存与加载功能
实现字幕样式的本地存储功能,包括保存当前设置、加载已保存设置和应用默认设置。新增样式提示对话框让用户选择是否应用已保存样式,并完善透明背景色的处理逻辑。
This commit is contained in:
@@ -601,6 +601,125 @@ function hexToRgba(hex, alpha) {
|
||||
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
||||
}
|
||||
|
||||
const CAPTION_STYLE_KEY = 'captionStyleSettings';
|
||||
|
||||
function saveCaptionStyleToStorage() {
|
||||
const styleData = {
|
||||
containerBgColor: elements.containerBgColor.value,
|
||||
containerBgOpacity: elements.containerBgOpacity.value,
|
||||
containerBorderColor: elements.containerBorderColor.value,
|
||||
containerBorderRadius: elements.containerBorderRadius.value,
|
||||
containerPadding: elements.containerPadding.value,
|
||||
tipFontSize: elements.tipFontSize.value,
|
||||
tipFontWeight: elements.tipFontWeight.value,
|
||||
tipColor: elements.tipColor.value,
|
||||
tipLineHeight: elements.tipLineHeight.value,
|
||||
tipBgColor: elements.tipBgColor.value,
|
||||
tipBgOpacity: elements.tipBgOpacity.value,
|
||||
tipBorderRadius: elements.tipBorderRadius.value,
|
||||
tipPadding: elements.tipPadding.value,
|
||||
messageFontSize: elements.messageFontSize.value,
|
||||
messageFontWeight: elements.messageFontWeight.value,
|
||||
messageColor: elements.messageColor.value,
|
||||
messageLineHeight: elements.messageLineHeight.value,
|
||||
messageBgColor: elements.messageBgColor.value,
|
||||
messageBgOpacity: elements.messageBgOpacity.value,
|
||||
messageBorderRadius: elements.messageBorderRadius.value,
|
||||
messagePadding: elements.messagePadding.value,
|
||||
hotTitleFontSize: elements.hotTitleFontSize.value,
|
||||
hotTitleFontWeight: elements.hotTitleFontWeight.value,
|
||||
hotTitleColor: elements.hotTitleColor.value,
|
||||
hotTitleLineHeight: elements.hotTitleLineHeight.value,
|
||||
hotTitleBgColor: elements.hotTitleBgColor.value,
|
||||
hotTitleBgOpacity: elements.hotTitleBgOpacity.value,
|
||||
hotTitleBorderRadius: elements.hotTitleBorderRadius.value,
|
||||
hotTitlePadding: elements.hotTitlePadding.value,
|
||||
hotIssuesFontSize: elements.hotIssuesFontSize.value,
|
||||
hotIssuesFontWeight: elements.hotIssuesFontWeight.value,
|
||||
hotIssuesColor: elements.hotIssuesColor.value,
|
||||
hotIssuesLineHeight: elements.hotIssuesLineHeight.value,
|
||||
hotIssuesBgColor: elements.hotIssuesBgColor.value,
|
||||
hotIssuesBgOpacity: elements.hotIssuesBgOpacity.value,
|
||||
hotIssuesBorderRadius: elements.hotIssuesBorderRadius.value,
|
||||
hotIssuesPadding: elements.hotIssuesPadding.value
|
||||
};
|
||||
localStorage.setItem(CAPTION_STYLE_KEY, JSON.stringify(styleData));
|
||||
logger.info('字幕样式已保存');
|
||||
}
|
||||
|
||||
function loadCaptionStyleFromStorage() {
|
||||
const savedData = localStorage.getItem(CAPTION_STYLE_KEY);
|
||||
if (!savedData) return null;
|
||||
try {
|
||||
return JSON.parse(savedData);
|
||||
} catch (e) {
|
||||
logger.error('加载保存的字幕样式失败', e.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function hasSavedCaptionStyle() {
|
||||
return localStorage.getItem(CAPTION_STYLE_KEY) !== null;
|
||||
}
|
||||
|
||||
function applySavedCaptionStyle(savedData) {
|
||||
if (savedData.containerBgColor) elements.containerBgColor.value = savedData.containerBgColor;
|
||||
if (savedData.containerBgOpacity) elements.containerBgOpacity.value = savedData.containerBgOpacity;
|
||||
if (savedData.containerBorderColor) elements.containerBorderColor.value = savedData.containerBorderColor;
|
||||
if (savedData.containerBorderRadius) elements.containerBorderRadius.value = savedData.containerBorderRadius;
|
||||
if (savedData.containerPadding) elements.containerPadding.value = savedData.containerPadding;
|
||||
if (savedData.tipFontSize) elements.tipFontSize.value = savedData.tipFontSize;
|
||||
if (savedData.tipFontWeight) elements.tipFontWeight.value = savedData.tipFontWeight;
|
||||
if (savedData.tipColor) elements.tipColor.value = savedData.tipColor;
|
||||
if (savedData.tipLineHeight) elements.tipLineHeight.value = savedData.tipLineHeight;
|
||||
if (savedData.tipBgColor) elements.tipBgColor.value = savedData.tipBgColor;
|
||||
if (savedData.tipBgOpacity) elements.tipBgOpacity.value = savedData.tipBgOpacity;
|
||||
if (savedData.tipBorderRadius) elements.tipBorderRadius.value = savedData.tipBorderRadius;
|
||||
if (savedData.tipPadding) elements.tipPadding.value = savedData.tipPadding;
|
||||
if (savedData.messageFontSize) elements.messageFontSize.value = savedData.messageFontSize;
|
||||
if (savedData.messageFontWeight) elements.messageFontWeight.value = savedData.messageFontWeight;
|
||||
if (savedData.messageColor) elements.messageColor.value = savedData.messageColor;
|
||||
if (savedData.messageLineHeight) elements.messageLineHeight.value = savedData.messageLineHeight;
|
||||
if (savedData.messageBgColor) elements.messageBgColor.value = savedData.messageBgColor;
|
||||
if (savedData.messageBgOpacity) elements.messageBgOpacity.value = savedData.messageBgOpacity;
|
||||
if (savedData.messageBorderRadius) elements.messageBorderRadius.value = savedData.messageBorderRadius;
|
||||
if (savedData.messagePadding) elements.messagePadding.value = savedData.messagePadding;
|
||||
if (savedData.hotTitleFontSize) elements.hotTitleFontSize.value = savedData.hotTitleFontSize;
|
||||
if (savedData.hotTitleFontWeight) elements.hotTitleFontWeight.value = savedData.hotTitleFontWeight;
|
||||
if (savedData.hotTitleColor) elements.hotTitleColor.value = savedData.hotTitleColor;
|
||||
if (savedData.hotTitleLineHeight) elements.hotTitleLineHeight.value = savedData.hotTitleLineHeight;
|
||||
if (savedData.hotTitleBgColor) elements.hotTitleBgColor.value = savedData.hotTitleBgColor;
|
||||
if (savedData.hotTitleBgOpacity) elements.hotTitleBgOpacity.value = savedData.hotTitleBgOpacity;
|
||||
if (savedData.hotTitleBorderRadius) elements.hotTitleBorderRadius.value = savedData.hotTitleBorderRadius;
|
||||
if (savedData.hotTitlePadding) elements.hotTitlePadding.value = savedData.hotTitlePadding;
|
||||
if (savedData.hotIssuesFontSize) elements.hotIssuesFontSize.value = savedData.hotIssuesFontSize;
|
||||
if (savedData.hotIssuesFontWeight) elements.hotIssuesFontWeight.value = savedData.hotIssuesFontWeight;
|
||||
if (savedData.hotIssuesColor) elements.hotIssuesColor.value = savedData.hotIssuesColor;
|
||||
if (savedData.hotIssuesLineHeight) elements.hotIssuesLineHeight.value = savedData.hotIssuesLineHeight;
|
||||
if (savedData.hotIssuesBgColor) elements.hotIssuesBgColor.value = savedData.hotIssuesBgColor;
|
||||
if (savedData.hotIssuesBgOpacity) elements.hotIssuesBgOpacity.value = savedData.hotIssuesBgOpacity;
|
||||
if (savedData.hotIssuesBorderRadius) elements.hotIssuesBorderRadius.value = savedData.hotIssuesBorderRadius;
|
||||
if (savedData.hotIssuesPadding) elements.hotIssuesPadding.value = savedData.hotIssuesPadding;
|
||||
|
||||
document.querySelectorAll('#captionPanel input[type="range"]').forEach(slider => {
|
||||
const valueSpan = slider.nextElementSibling;
|
||||
if (valueSpan && valueSpan.classList.contains('range-value')) {
|
||||
valueSpan.textContent = `${slider.value}%`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showCaptionStylePrompt(callback) {
|
||||
const savedData = loadCaptionStyleFromStorage();
|
||||
if (!savedData) {
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmed = confirm('检测到已保存的字幕样式设置。\n\n点击"确定"应用保存的样式,\n点击"取消"使用全新默认设置。');
|
||||
callback(confirmed);
|
||||
}
|
||||
|
||||
function loadCaptionStyleFromContainer() {
|
||||
const captionContainer = document.querySelector('.caption-container-hub');
|
||||
if (!captionContainer) return;
|
||||
@@ -669,13 +788,18 @@ function loadCaptionStyleFromContainer() {
|
||||
}
|
||||
}
|
||||
|
||||
if (styles.backgroundColor && styles.backgroundColor !== 'transparent' && styles.backgroundColor !== 'rgba(0, 0, 0, 0)') {
|
||||
if (styles.backgroundColor) {
|
||||
const rgbaMatch = styles.backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+),?\s*([\d.]*)\)/);
|
||||
if (rgbaMatch) {
|
||||
section.elements.bgColor.value = rgbToHex(parseInt(rgbaMatch[1]), parseInt(rgbaMatch[2]), parseInt(rgbaMatch[3]));
|
||||
if (rgbaMatch[4]) {
|
||||
section.elements.bgOpacity.value = Math.round(parseFloat(rgbaMatch[4]) * 100);
|
||||
} else {
|
||||
section.elements.bgOpacity.value = 0;
|
||||
}
|
||||
} else if (styles.backgroundColor === 'transparent' || styles.backgroundColor === 'rgba(0, 0, 0, 0)') {
|
||||
section.elements.bgColor.value = '#000000';
|
||||
section.elements.bgOpacity.value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -831,6 +955,7 @@ function initCaptionDragAndResize(container) {
|
||||
|
||||
let captionStyleObserver = null;
|
||||
let captionStyleApplied = false;
|
||||
let captionStylePromptShown = false;
|
||||
|
||||
function watchCaptionContainer() {
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
@@ -947,6 +1072,7 @@ function bindEvents() {
|
||||
elements.getOnceCodeBtn.addEventListener('click', handleGetOnceCode);
|
||||
elements.applyCaptionStyleBtn.addEventListener('click', () => {
|
||||
applyCaptionStyle();
|
||||
saveCaptionStyleToStorage();
|
||||
captionStyleApplied = true;
|
||||
watchCaptionContentChanges();
|
||||
});
|
||||
@@ -958,6 +1084,7 @@ function bindEvents() {
|
||||
valueSpan.textContent = `${e.target.value}%`;
|
||||
}
|
||||
updateSingleStyle(e.target.id);
|
||||
saveCaptionStyleToStorage();
|
||||
captionStyleApplied = true;
|
||||
watchCaptionContentChanges();
|
||||
});
|
||||
@@ -966,11 +1093,13 @@ function bindEvents() {
|
||||
document.querySelectorAll('#captionPanel input[type="number"], #captionPanel input[type="color"], #captionPanel select').forEach(input => {
|
||||
input.addEventListener('input', (e) => {
|
||||
updateSingleStyle(e.target.id);
|
||||
saveCaptionStyleToStorage();
|
||||
captionStyleApplied = true;
|
||||
watchCaptionContentChanges();
|
||||
});
|
||||
input.addEventListener('change', (e) => {
|
||||
updateSingleStyle(e.target.id);
|
||||
saveCaptionStyleToStorage();
|
||||
captionStyleApplied = true;
|
||||
watchCaptionContentChanges();
|
||||
});
|
||||
@@ -987,7 +1116,28 @@ function bindEvents() {
|
||||
elements.toggleConfigBtn.addEventListener('click', () => togglePanel('configPanel'));
|
||||
elements.toggleCaptionBtn.addEventListener('click', () => {
|
||||
togglePanel('captionPanel');
|
||||
loadCaptionStyleFromContainer();
|
||||
if (!captionStylePromptShown && hasSavedCaptionStyle()) {
|
||||
captionStylePromptShown = true;
|
||||
showCaptionStylePrompt((applySaved) => {
|
||||
if (applySaved) {
|
||||
const savedData = loadCaptionStyleFromStorage();
|
||||
if (savedData) {
|
||||
applySavedCaptionStyle(savedData);
|
||||
applyCaptionStyle();
|
||||
captionStyleApplied = true;
|
||||
watchCaptionContentChanges();
|
||||
logger.info('已应用保存的字幕样式');
|
||||
}
|
||||
} else {
|
||||
loadCaptionStyleFromContainer();
|
||||
localStorage.removeItem(CAPTION_STYLE_KEY);
|
||||
captionStyleApplied = false;
|
||||
logger.info('已清除保存的字幕样式,使用全新默认设置');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
loadCaptionStyleFromContainer();
|
||||
}
|
||||
});
|
||||
elements.toggleLogBtn.addEventListener('click', () => togglePanel('logPanel'));
|
||||
elements.fullscreenBtn.addEventListener('click', toggleFullscreen);
|
||||
|
||||
Reference in New Issue
Block a user