feat(字幕): 添加字幕样式设置面板及拖拽缩放功能
添加字幕样式设置浮动面板,包含宽度、高度、字体大小等配置项 实现字幕容器的拖拽移动和缩放功能 添加字幕容器自动检测和初始化逻辑
This commit is contained in:
@@ -22,6 +22,9 @@
|
|||||||
<button id="toggleConfigBtn" class="toolbar-btn" title="连接配置">
|
<button id="toggleConfigBtn" class="toolbar-btn" title="连接配置">
|
||||||
⚙️
|
⚙️
|
||||||
</button>
|
</button>
|
||||||
|
<button id="toggleCaptionBtn" class="toolbar-btn" title="字幕样式">
|
||||||
|
📝
|
||||||
|
</button>
|
||||||
<button id="toggleLogBtn" class="toolbar-btn" title="事件日志">
|
<button id="toggleLogBtn" class="toolbar-btn" title="事件日志">
|
||||||
📜
|
📜
|
||||||
</button>
|
</button>
|
||||||
@@ -68,6 +71,37 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 浮动面板:字幕样式 -->
|
||||||
|
<div id="captionPanel" class="floating-panel caption-panel">
|
||||||
|
<div class="panel-header">
|
||||||
|
<h2>📝 字幕样式设置</h2>
|
||||||
|
<button class="panel-close" data-panel="captionPanel">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="panel-content">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="captionWidth">宽度 (px)</label>
|
||||||
|
<input type="number" id="captionWidth" value="400" min="200" max="800">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="captionHeight">高度 (px)</label>
|
||||||
|
<input type="number" id="captionHeight" value="200" min="100" max="600">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="captionFontSize">字体大小 (px)</label>
|
||||||
|
<input type="number" id="captionFontSize" value="16" min="12" max="32">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="captionRight">距右侧距离 (px)</label>
|
||||||
|
<input type="number" id="captionRight" value="20" min="0" max="100">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="captionBottom">距底部距离 (px)</label>
|
||||||
|
<input type="number" id="captionBottom" value="20" min="0" max="200">
|
||||||
|
</div>
|
||||||
|
<button id="applyCaptionStyleBtn" class="btn btn-primary" style="width: 100%; margin-top: 16px;">应用样式</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 浮动面板:事件日志 -->
|
<!-- 浮动面板:事件日志 -->
|
||||||
<div id="logPanel" class="floating-panel log-panel">
|
<div id="logPanel" class="floating-panel log-panel">
|
||||||
<div class="panel-header">
|
<div class="panel-header">
|
||||||
|
|||||||
@@ -56,7 +56,15 @@ const elements = {
|
|||||||
clearLogBtn: document.getElementById('clearLogBtn'),
|
clearLogBtn: document.getElementById('clearLogBtn'),
|
||||||
fullscreenBtn: document.getElementById('fullscreenBtn'),
|
fullscreenBtn: document.getElementById('fullscreenBtn'),
|
||||||
toggleConfigBtn: document.getElementById('toggleConfigBtn'),
|
toggleConfigBtn: document.getElementById('toggleConfigBtn'),
|
||||||
|
toggleCaptionBtn: document.getElementById('toggleCaptionBtn'),
|
||||||
toggleLogBtn: document.getElementById('toggleLogBtn'),
|
toggleLogBtn: document.getElementById('toggleLogBtn'),
|
||||||
|
applyCaptionStyleBtn: document.getElementById('applyCaptionStyleBtn'),
|
||||||
|
|
||||||
|
captionWidth: document.getElementById('captionWidth'),
|
||||||
|
captionHeight: document.getElementById('captionHeight'),
|
||||||
|
captionFontSize: document.getElementById('captionFontSize'),
|
||||||
|
captionRight: document.getElementById('captionRight'),
|
||||||
|
captionBottom: document.getElementById('captionBottom'),
|
||||||
|
|
||||||
logLevel: document.getElementById('logLevel'),
|
logLevel: document.getElementById('logLevel'),
|
||||||
|
|
||||||
@@ -73,6 +81,7 @@ const fullscreenState = {
|
|||||||
|
|
||||||
const panelState = {
|
const panelState = {
|
||||||
configPanel: false,
|
configPanel: false,
|
||||||
|
captionPanel: false,
|
||||||
logPanel: false
|
logPanel: false
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -200,6 +209,7 @@ function togglePanel(panelId) {
|
|||||||
|
|
||||||
const btnMap = {
|
const btnMap = {
|
||||||
configPanel: 'toggleConfigBtn',
|
configPanel: 'toggleConfigBtn',
|
||||||
|
captionPanel: 'toggleCaptionBtn',
|
||||||
logPanel: 'toggleLogBtn'
|
logPanel: 'toggleLogBtn'
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -338,6 +348,142 @@ function clearLogs() {
|
|||||||
logger.info('日志已清空');
|
logger.info('日志已清空');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyCaptionStyle() {
|
||||||
|
const width = elements.captionWidth.value;
|
||||||
|
const height = elements.captionHeight.value;
|
||||||
|
const fontSize = elements.captionFontSize.value;
|
||||||
|
const right = elements.captionRight.value;
|
||||||
|
const bottom = elements.captionBottom.value;
|
||||||
|
|
||||||
|
const captionContainer = document.querySelector('.caption-container-hub');
|
||||||
|
if (!captionContainer) {
|
||||||
|
logger.warn('字幕容器未找到');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
captionContainer.style.position = 'fixed';
|
||||||
|
captionContainer.style.width = `${width}px`;
|
||||||
|
captionContainer.style.height = `${height}px`;
|
||||||
|
captionContainer.style.maxHeight = `${height}px`;
|
||||||
|
captionContainer.style.right = `${right}px`;
|
||||||
|
captionContainer.style.bottom = `${bottom}px`;
|
||||||
|
captionContainer.style.fontSize = `${fontSize}px`;
|
||||||
|
captionContainer.style.borderRadius = '8px';
|
||||||
|
captionContainer.style.padding = '10px';
|
||||||
|
captionContainer.style.overflowY = 'auto';
|
||||||
|
captionContainer.style.cursor = 'move';
|
||||||
|
captionContainer.style.userSelect = 'none';
|
||||||
|
captionContainer.style.zIndex = '9999';
|
||||||
|
|
||||||
|
const textElements = captionContainer.querySelectorAll('*');
|
||||||
|
textElements.forEach(el => {
|
||||||
|
el.style.fontSize = `${fontSize}px`;
|
||||||
|
});
|
||||||
|
|
||||||
|
initCaptionDragAndResize(captionContainer);
|
||||||
|
|
||||||
|
logger.info('字幕容器样式已应用', { width, height, fontSize, right, bottom });
|
||||||
|
}
|
||||||
|
|
||||||
|
function initCaptionDragAndResize(container) {
|
||||||
|
container.dataset.dragInitialized = 'true';
|
||||||
|
|
||||||
|
let isDragging = false;
|
||||||
|
let isResizing = false;
|
||||||
|
let startX, startY, startRight, startBottom, startWidth, startHeight;
|
||||||
|
|
||||||
|
let resizeHandle = container.querySelector('.caption-resize-handle');
|
||||||
|
if (!resizeHandle) {
|
||||||
|
resizeHandle = document.createElement('div');
|
||||||
|
resizeHandle.className = 'caption-resize-handle';
|
||||||
|
resizeHandle.style.position = 'absolute';
|
||||||
|
resizeHandle.style.right = '0';
|
||||||
|
resizeHandle.style.bottom = '0';
|
||||||
|
resizeHandle.style.width = '20px';
|
||||||
|
resizeHandle.style.height = '20px';
|
||||||
|
resizeHandle.style.cursor = 'nwse-resize';
|
||||||
|
resizeHandle.style.background = 'linear-gradient(135deg, transparent 50%, rgba(255,255,255,0.5) 50%)';
|
||||||
|
resizeHandle.style.borderRadius = '0 0 8px 0';
|
||||||
|
resizeHandle.style.zIndex = '10000';
|
||||||
|
container.appendChild(resizeHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
container.onmousedown = (e) => {
|
||||||
|
if (e.target === resizeHandle || resizeHandle.contains(e.target)) return;
|
||||||
|
isDragging = true;
|
||||||
|
startX = e.clientX;
|
||||||
|
startY = e.clientY;
|
||||||
|
const rect = container.getBoundingClientRect();
|
||||||
|
startRight = window.innerWidth - rect.right;
|
||||||
|
startBottom = window.innerHeight - rect.bottom;
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
resizeHandle.onmousedown = (e) => {
|
||||||
|
isResizing = true;
|
||||||
|
startX = e.clientX;
|
||||||
|
startY = e.clientY;
|
||||||
|
startWidth = container.offsetWidth;
|
||||||
|
startHeight = container.offsetHeight;
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
document.onmousemove = (e) => {
|
||||||
|
if (!isDragging && !isResizing) return;
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (isDragging) {
|
||||||
|
const dx = e.clientX - startX;
|
||||||
|
const dy = e.clientY - startY;
|
||||||
|
container.style.right = `${startRight - dx}px`;
|
||||||
|
container.style.bottom = `${startBottom + dy}px`;
|
||||||
|
}
|
||||||
|
if (isResizing) {
|
||||||
|
const dx = e.clientX - startX;
|
||||||
|
const dy = e.clientY - startY;
|
||||||
|
const newWidth = Math.max(200, startWidth + dx);
|
||||||
|
const newHeight = Math.max(100, startHeight + dy);
|
||||||
|
container.style.width = `${newWidth}px`;
|
||||||
|
container.style.height = `${newHeight}px`;
|
||||||
|
container.style.maxHeight = `${newHeight}px`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.onmouseup = () => {
|
||||||
|
isDragging = false;
|
||||||
|
isResizing = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
logger.info('字幕容器拖拽和缩放已启用');
|
||||||
|
}
|
||||||
|
|
||||||
|
function watchCaptionContainer() {
|
||||||
|
const observer = new MutationObserver((mutations) => {
|
||||||
|
for (const mutation of mutations) {
|
||||||
|
if (mutation.type === 'childList') {
|
||||||
|
const captionContainer = document.querySelector('.caption-container-hub');
|
||||||
|
if (captionContainer && !captionContainer.dataset.dragInitialized) {
|
||||||
|
logger.info('检测到字幕容器,自动启用拖拽功能');
|
||||||
|
captionContainer.style.position = 'fixed';
|
||||||
|
captionContainer.style.cursor = 'move';
|
||||||
|
captionContainer.style.userSelect = 'none';
|
||||||
|
captionContainer.style.zIndex = '9999';
|
||||||
|
initCaptionDragAndResize(captionContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info('已启动字幕容器监听');
|
||||||
|
}
|
||||||
|
|
||||||
function calculateScale() {
|
function calculateScale() {
|
||||||
const container = elements.digitalHumanBox;
|
const container = elements.digitalHumanBox;
|
||||||
const sdkContainer = container.querySelector('.digital-intelligent-interaction');
|
const sdkContainer = container.querySelector('.digital-intelligent-interaction');
|
||||||
@@ -392,6 +538,7 @@ function bindEvents() {
|
|||||||
elements.initBtn.addEventListener('click', initializeSDK);
|
elements.initBtn.addEventListener('click', initializeSDK);
|
||||||
elements.destroyBtn.addEventListener('click', destroySDK);
|
elements.destroyBtn.addEventListener('click', destroySDK);
|
||||||
elements.getOnceCodeBtn.addEventListener('click', handleGetOnceCode);
|
elements.getOnceCodeBtn.addEventListener('click', handleGetOnceCode);
|
||||||
|
elements.applyCaptionStyleBtn.addEventListener('click', applyCaptionStyle);
|
||||||
|
|
||||||
elements.clearLogBtn.addEventListener('click', clearLogs);
|
elements.clearLogBtn.addEventListener('click', clearLogs);
|
||||||
elements.logLevel.addEventListener('change', () => {
|
elements.logLevel.addEventListener('change', () => {
|
||||||
@@ -402,6 +549,7 @@ function bindEvents() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
elements.toggleConfigBtn.addEventListener('click', () => togglePanel('configPanel'));
|
elements.toggleConfigBtn.addEventListener('click', () => togglePanel('configPanel'));
|
||||||
|
elements.toggleCaptionBtn.addEventListener('click', () => togglePanel('captionPanel'));
|
||||||
elements.toggleLogBtn.addEventListener('click', () => togglePanel('logPanel'));
|
elements.toggleLogBtn.addEventListener('click', () => togglePanel('logPanel'));
|
||||||
elements.fullscreenBtn.addEventListener('click', toggleFullscreen);
|
elements.fullscreenBtn.addEventListener('click', toggleFullscreen);
|
||||||
|
|
||||||
@@ -434,6 +582,7 @@ function init() {
|
|||||||
bindEvents();
|
bindEvents();
|
||||||
updateUIState();
|
updateUIState();
|
||||||
initScaling();
|
initScaling();
|
||||||
|
watchCaptionContainer();
|
||||||
}
|
}
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
|||||||
@@ -333,6 +333,13 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ===== 对话控制面板 ===== */
|
/* ===== 对话控制面板 ===== */
|
||||||
|
/* ===== 字幕样式面板 ===== */
|
||||||
|
.caption-panel {
|
||||||
|
bottom: calc(var(--spacing-xl) + 60px);
|
||||||
|
right: var(--spacing-xl);
|
||||||
|
width: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ===== 日志面板 ===== */
|
/* ===== 日志面板 ===== */
|
||||||
.log-panel {
|
.log-panel {
|
||||||
bottom: calc(var(--spacing-xl) + 60px);
|
bottom: calc(var(--spacing-xl) + 60px);
|
||||||
|
|||||||
Reference in New Issue
Block a user