Feature: Fabrica de Quadrinhos profissional com geracao paralela, modo apresentacao fullscreen, storyboard interativo, exportacao ZIP e adaptacao mobile
This commit is contained in:
+392
-55
@@ -4442,6 +4442,8 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
||||
comicsCharListContainer.appendChild(row);
|
||||
};
|
||||
|
||||
const btnImportTurmaChars = document.getElementById('btnImportTurmaChars');
|
||||
|
||||
if (btnAddHumanChar) {
|
||||
btnAddHumanChar.addEventListener('click', () => {
|
||||
addCharacterRow('', '', false);
|
||||
@@ -4454,6 +4456,37 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
||||
});
|
||||
}
|
||||
|
||||
if (btnImportTurmaChars) {
|
||||
btnImportTurmaChars.addEventListener('click', async () => {
|
||||
try {
|
||||
btnImportTurmaChars.disabled = true;
|
||||
btnImportTurmaChars.textContent = '⏳ Carregando...';
|
||||
const res = await fetch('/api/alunos');
|
||||
if (!res.ok) throw new Error('Falha ao obter lista de alunos');
|
||||
const alunos = await res.json();
|
||||
if (!alunos || alunos.length === 0) {
|
||||
await showCustomAlert('Nenhum Aluno', 'Cadastre alunos na seção "Minha Turma" para importá-los automaticamente.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Adiciona os alunos como personagens (até 4 para manter a história focada)
|
||||
const selecionados = alunos.slice(0, 4);
|
||||
comicsCharListContainer.innerHTML = '';
|
||||
selecionados.forEach(aluno => {
|
||||
const generoProvavel = aluno.nome.trim().endsWith('a') ? 'Menina' : 'Menino';
|
||||
addCharacterRow(generoProvavel, aluno.nome.trim(), false);
|
||||
});
|
||||
await showCustomAlert('Turma Importada!', `${selecionados.length} alunos foram adicionados como personagens da história.`);
|
||||
} catch (err) {
|
||||
console.error('Erro ao importar alunos:', err);
|
||||
await showCustomAlert('Erro', 'Não foi possível carregar a lista de alunos: ' + err.message);
|
||||
} finally {
|
||||
btnImportTurmaChars.disabled = false;
|
||||
btnImportTurmaChars.textContent = '+ 🏫 Importar da Turma';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Exibir/ocultar cenário personalizado
|
||||
if (comicsCenarioSelect) {
|
||||
comicsCenarioSelect.addEventListener('change', () => {
|
||||
@@ -4945,18 +4978,14 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
||||
currentComicsCharDescEnglish = scriptData.character_description;
|
||||
}
|
||||
|
||||
// 2. Gerar cada quadrinho sequencialmente
|
||||
// 2. Gerar quadrinhos em PARALELO com tracking de progresso em tempo real
|
||||
if (genMode !== 'extend') {
|
||||
generatedPanelsData = [];
|
||||
}
|
||||
const totalPanels = scriptData.panels.length;
|
||||
let completedCount = 0;
|
||||
|
||||
for (let i = 0; i < totalPanels; i++) {
|
||||
const panel = scriptData.panels[i];
|
||||
const progressPercent = 15 + Math.floor((i / totalPanels) * 80);
|
||||
comicsLoaderProgress.style.width = `${progressPercent}%`;
|
||||
comicsLoaderText.textContent = `Ilustrando quadrinho ${i + 1} de ${totalPanels}...`;
|
||||
|
||||
const panelPromises = scriptData.panels.map(async (panel, idx) => {
|
||||
// Injetamos a proporção e estilo de forma reforçada no prompt
|
||||
const fullPrompt = `${panel.image_prompt}, high resolution children book illustration, cute Pixar style, ${selectedComicsRatio === '16:9' ? '16:9 aspect ratio' : '4:3 aspect ratio'}`;
|
||||
|
||||
@@ -4968,65 +4997,43 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
||||
|
||||
if (!frameResp.ok) {
|
||||
const err = await frameResp.json();
|
||||
throw new Error(`Erro no quadrinho ${i + 1}: ${err.error}`);
|
||||
throw new Error(`Erro no quadrinho ${panel.panel_number}: ${err.error}`);
|
||||
}
|
||||
|
||||
const frameData = await frameResp.json();
|
||||
generatedPanelsData.push({
|
||||
completedCount++;
|
||||
const progressPercent = 15 + Math.floor((completedCount / totalPanels) * 80);
|
||||
if (comicsLoaderProgress) comicsLoaderProgress.style.width = `${progressPercent}%`;
|
||||
if (comicsLoaderText) comicsLoaderText.textContent = `⚡ Ilustrando em paralelo: ${completedCount}/${totalPanels} quadros concluídos...`;
|
||||
|
||||
return {
|
||||
panel_number: panel.panel_number,
|
||||
imageUrl: frameData.imageUrl,
|
||||
dialogue: panel.dialogue || '',
|
||||
image_prompt: panel.image_prompt || ''
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
const newPanels = await Promise.all(panelPromises);
|
||||
|
||||
// Ordena por panel_number para garantir a sequência correta
|
||||
newPanels.sort((a, b) => a.panel_number - b.panel_number);
|
||||
|
||||
if (genMode === 'extend') {
|
||||
generatedPanelsData = generatedPanelsData.concat(newPanels);
|
||||
} else {
|
||||
generatedPanelsData = newPanels;
|
||||
}
|
||||
|
||||
// Renderizar quadrinhos na tela (todos, incluindo os anteriores caso tenha sido extensão)
|
||||
// Atualizar cabeçalho e metadados
|
||||
comicsResultTitle.textContent = scriptData.title || comicsResultTitle.textContent || 'Fábrica de Quadrinhos Pedagog';
|
||||
comicsGridContainer.innerHTML = '';
|
||||
const metaPanels = document.getElementById('comicsMetaPanels');
|
||||
if (metaPanels) metaPanels.textContent = `🖼️ ${generatedPanelsData.length} Quadros`;
|
||||
const metaRatio = document.getElementById('comicsMetaRatio');
|
||||
if (metaRatio) metaRatio.textContent = `📐 ${selectedComicsRatio} ${selectedComicsRatio === '16:9' ? 'HD' : 'Retrô'}`;
|
||||
|
||||
generatedPanelsData.forEach((panel, index) => {
|
||||
const panelCard = document.createElement('div');
|
||||
panelCard.className = 'comic-panel-card';
|
||||
|
||||
const imgContainer = document.createElement('div');
|
||||
imgContainer.className = `comic-panel-image-container ratio-${selectedComicsRatio.replace(':', '-')}`;
|
||||
|
||||
const img = document.createElement('img');
|
||||
img.className = 'comic-panel-image';
|
||||
img.src = panel.imageUrl || panel.image_url;
|
||||
img.alt = `Quadro ${panel.panel_number}`;
|
||||
imgContainer.appendChild(img);
|
||||
|
||||
// Selo do número
|
||||
const badge = document.createElement('div');
|
||||
badge.className = 'comic-panel-number-badge';
|
||||
badge.textContent = panel.panel_number;
|
||||
imgContainer.appendChild(badge);
|
||||
|
||||
panelCard.appendChild(imgContainer);
|
||||
|
||||
const caption = document.createElement('div');
|
||||
caption.className = 'comic-caption-text';
|
||||
caption.style.cssText = 'padding: 10px 14px; background: var(--bg-primary); border-top: 1px solid var(--border-light);';
|
||||
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.className = 'comic-caption-input';
|
||||
textarea.value = panel.dialogue || '';
|
||||
textarea.placeholder = 'Digite a fala ou legenda do quadrinho...';
|
||||
textarea.style.cssText = 'width: 100%; border: 1px solid var(--border-light); background: var(--bg-secondary); color: var(--text-primary); border-radius: 6px; padding: 8px; font-family: inherit; font-size: 0.85rem; resize: vertical; box-sizing: border-box; min-height: 50px;';
|
||||
|
||||
textarea.addEventListener('input', (e) => {
|
||||
panel.dialogue = e.target.value;
|
||||
if (generatedPanelsData[index]) {
|
||||
generatedPanelsData[index].dialogue = e.target.value;
|
||||
}
|
||||
});
|
||||
|
||||
caption.appendChild(textarea);
|
||||
panelCard.appendChild(caption);
|
||||
|
||||
comicsGridContainer.appendChild(panelCard);
|
||||
});
|
||||
// Renderizar Storyboard Interativo
|
||||
renderComicsStoryboard();
|
||||
|
||||
// Se for uma nova história derivada de um projeto antigo, limpamos o ID para virar um novo projeto independente ao salvar
|
||||
if (genMode === 'new_story' && currentComicsProjectId) {
|
||||
@@ -5053,6 +5060,336 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
||||
});
|
||||
}
|
||||
|
||||
// --- RENDERIZADOR DO STORYBOARD PROFISSIONAL DOS QUADRINHOS ---
|
||||
const renderComicsStoryboard = () => {
|
||||
if (!comicsGridContainer) return;
|
||||
comicsGridContainer.innerHTML = '';
|
||||
|
||||
generatedPanelsData.forEach((panel, index) => {
|
||||
const panelCard = document.createElement('div');
|
||||
panelCard.className = 'comic-panel-card';
|
||||
panelCard.dataset.index = index;
|
||||
|
||||
const imgContainer = document.createElement('div');
|
||||
imgContainer.className = `comic-panel-image-container ratio-${selectedComicsRatio.replace(':', '-')}`;
|
||||
|
||||
const img = document.createElement('img');
|
||||
img.className = 'comic-panel-image';
|
||||
img.src = panel.imageUrl || panel.image_url;
|
||||
img.alt = `Quadro ${panel.panel_number}`;
|
||||
img.loading = 'lazy';
|
||||
imgContainer.appendChild(img);
|
||||
|
||||
// Selo de número do quadrinho
|
||||
const badge = document.createElement('div');
|
||||
badge.className = 'comic-panel-number-badge';
|
||||
badge.textContent = panel.panel_number;
|
||||
imgContainer.appendChild(badge);
|
||||
|
||||
// Barra de Ações Rápidas por Quadro (Mobile & Desktop)
|
||||
const actionsOverlay = document.createElement('div');
|
||||
actionsOverlay.className = 'comic-panel-overlay-actions';
|
||||
|
||||
// Botão 🔍 Zoom
|
||||
const btnZoom = document.createElement('button');
|
||||
btnZoom.type = 'button';
|
||||
btnZoom.className = 'btn-comic-action';
|
||||
btnZoom.title = 'Ampliar imagem';
|
||||
btnZoom.innerHTML = '🔍';
|
||||
btnZoom.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
openComicsPresentation(index);
|
||||
});
|
||||
actionsOverlay.appendChild(btnZoom);
|
||||
|
||||
// Botão 🔄 Regenerar Imagem deste Quadro
|
||||
const btnRegen = document.createElement('button');
|
||||
btnRegen.type = 'button';
|
||||
btnRegen.className = 'btn-comic-action';
|
||||
btnRegen.title = 'Regenerar apenas este quadrinho';
|
||||
btnRegen.innerHTML = '🔄';
|
||||
btnRegen.addEventListener('click', async (e) => {
|
||||
e.stopPropagation();
|
||||
await regenerateSinglePanel(index, panelCard, img, btnRegen);
|
||||
});
|
||||
actionsOverlay.appendChild(btnRegen);
|
||||
|
||||
// Botão ✏️ Ver/Editar Prompt
|
||||
const btnPromptToggle = document.createElement('button');
|
||||
btnPromptToggle.type = 'button';
|
||||
btnPromptToggle.className = 'btn-comic-action';
|
||||
btnPromptToggle.title = 'Editar prompt visual';
|
||||
btnPromptToggle.innerHTML = '✏️';
|
||||
actionsOverlay.appendChild(btnPromptToggle);
|
||||
|
||||
// Botão 💾 Baixar Imagem Individual
|
||||
const btnDownloadImg = document.createElement('button');
|
||||
btnDownloadImg.type = 'button';
|
||||
btnDownloadImg.className = 'btn-comic-action';
|
||||
btnDownloadImg.title = 'Baixar esta imagem PNG';
|
||||
btnDownloadImg.innerHTML = '💾';
|
||||
btnDownloadImg.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const a = document.createElement('a');
|
||||
a.href = panel.imageUrl || panel.image_url;
|
||||
a.download = `quadrinho_${panel.panel_number}.png`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
});
|
||||
actionsOverlay.appendChild(btnDownloadImg);
|
||||
|
||||
imgContainer.appendChild(actionsOverlay);
|
||||
panelCard.appendChild(imgContainer);
|
||||
|
||||
// Área de Legenda / Diálogo
|
||||
const caption = document.createElement('div');
|
||||
caption.className = 'comic-caption-text';
|
||||
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.className = 'comic-caption-input';
|
||||
textarea.value = panel.dialogue || '';
|
||||
textarea.placeholder = 'Digite a fala ou legenda do quadrinho...';
|
||||
textarea.style.cssText = 'width: 100%; border: 1px solid var(--border-light); background: var(--bg-secondary); color: var(--text-primary); border-radius: 6px; padding: 8px; font-family: inherit; font-size: 0.85rem; resize: vertical; box-sizing: border-box; min-height: 48px;';
|
||||
|
||||
textarea.addEventListener('input', (e) => {
|
||||
panel.dialogue = e.target.value;
|
||||
if (generatedPanelsData[index]) {
|
||||
generatedPanelsData[index].dialogue = e.target.value;
|
||||
}
|
||||
});
|
||||
caption.appendChild(textarea);
|
||||
|
||||
// Mini-Editor de Prompt Visual (Oculto por padrão, ativado no botão ✏️)
|
||||
const promptBox = document.createElement('div');
|
||||
promptBox.className = 'comic-prompt-details';
|
||||
const promptInput = document.createElement('textarea');
|
||||
promptInput.style.cssText = 'width: 100%; font-size: 0.72rem; padding: 4px; background: var(--bg-primary); color: var(--text-primary); border: 1px solid var(--border-light); border-radius: 4px; resize: vertical; min-height: 40px; box-sizing: border-box;';
|
||||
promptInput.value = panel.image_prompt || '';
|
||||
promptInput.addEventListener('input', (e) => {
|
||||
panel.image_prompt = e.target.value;
|
||||
});
|
||||
promptBox.appendChild(document.createTextNode('Prompt Visual: '));
|
||||
promptBox.appendChild(promptInput);
|
||||
caption.appendChild(promptBox);
|
||||
|
||||
btnPromptToggle.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
promptBox.style.display = promptBox.style.display === 'block' ? 'none' : 'block';
|
||||
});
|
||||
|
||||
panelCard.appendChild(caption);
|
||||
comicsGridContainer.appendChild(panelCard);
|
||||
});
|
||||
};
|
||||
|
||||
// Regenerar um único painel
|
||||
const regenerateSinglePanel = async (index, panelCard, imgElement, btnRegen) => {
|
||||
const panel = generatedPanelsData[index];
|
||||
if (!panel) return;
|
||||
|
||||
try {
|
||||
btnRegen.disabled = true;
|
||||
btnRegen.textContent = '⏳';
|
||||
imgElement.style.opacity = '0.4';
|
||||
|
||||
const promptToUse = panel.image_prompt || 'cute children illustration';
|
||||
const fullPrompt = `${promptToUse}, high resolution children book illustration, cute Pixar style, ${selectedComicsRatio === '16:9' ? '16:9 aspect ratio' : '4:3 aspect ratio'}`;
|
||||
|
||||
const resp = await fetch('/api/comics/generate-frame', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ prompt: fullPrompt })
|
||||
});
|
||||
|
||||
if (!resp.ok) {
|
||||
const err = await resp.json();
|
||||
throw new Error(err.error || 'Erro na regeneração');
|
||||
}
|
||||
|
||||
const data = await resp.json();
|
||||
panel.imageUrl = data.imageUrl;
|
||||
imgElement.src = data.imageUrl + '?v=' + Date.now();
|
||||
imgElement.style.opacity = '1';
|
||||
} catch (err) {
|
||||
console.error('Erro ao regenerar painel:', err);
|
||||
await showCustomAlert('Erro', 'Não foi possível regenerar o quadrinho: ' + err.message);
|
||||
imgElement.style.opacity = '1';
|
||||
} finally {
|
||||
btnRegen.disabled = false;
|
||||
btnRegen.innerHTML = '🔄';
|
||||
}
|
||||
};
|
||||
|
||||
// --- EXPORTAR TODAS AS IMAGENS EM ZIP ---
|
||||
const btnExportComicsZIP = document.getElementById('btnExportComicsZIP');
|
||||
if (btnExportComicsZIP) {
|
||||
btnExportComicsZIP.addEventListener('click', async () => {
|
||||
if (!generatedPanelsData || generatedPanelsData.length === 0) {
|
||||
await showCustomAlert('Aviso', 'Nenhum quadrinho gerado para exportar.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
btnExportComicsZIP.disabled = true;
|
||||
btnExportComicsZIP.textContent = '⏳ Criando ZIP...';
|
||||
|
||||
if (typeof JSZip === 'undefined') {
|
||||
throw new Error('Biblioteca JSZip não carregada.');
|
||||
}
|
||||
|
||||
const zip = new JSZip();
|
||||
const folder = zip.folder('quadrinhos');
|
||||
const title = (comicsResultTitle?.textContent || 'Historia_Quadrinhos').replace(/[^a-zA-Z0-9_-]/g, '_');
|
||||
|
||||
let roteiroTxt = `HISTÓRIA EM QUADRINHOS: ${comicsResultTitle?.textContent || 'Fábrica de Quadrinhos'}\n`;
|
||||
roteiroTxt += `Criado com PedagogIA em ${new Date().toLocaleDateString('pt-BR')}\n\n`;
|
||||
|
||||
for (let i = 0; i < generatedPanelsData.length; i++) {
|
||||
const panel = generatedPanelsData[i];
|
||||
const imgUrl = panel.imageUrl || panel.image_url;
|
||||
roteiroTxt += `[QUADRO ${panel.panel_number}]\nFala/Legenda: ${panel.dialogue || '(Sem fala)'}\nPrompt Visual: ${panel.image_prompt || ''}\n\n`;
|
||||
|
||||
try {
|
||||
const resp = await fetch(imgUrl);
|
||||
const blob = await resp.blob();
|
||||
folder.file(`quadrinho_${panel.panel_number}.jpg`, blob);
|
||||
} catch (fetchErr) {
|
||||
console.warn(`Erro ao incluir imagem ${panel.panel_number} no ZIP:`, fetchErr);
|
||||
}
|
||||
}
|
||||
|
||||
folder.file('roteiro.txt', roteiroTxt);
|
||||
|
||||
const content = await zip.generateAsync({ type: 'blob' });
|
||||
const downloadUrl = URL.createObjectURL(content);
|
||||
const a = document.createElement('a');
|
||||
a.href = downloadUrl;
|
||||
a.download = `${title}_pedagog.zip`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
URL.revokeObjectURL(downloadUrl);
|
||||
|
||||
} catch (err) {
|
||||
console.error('Erro ao gerar ZIP:', err);
|
||||
await showCustomAlert('Erro', 'Não foi possível gerar o ZIP: ' + err.message);
|
||||
} finally {
|
||||
btnExportComicsZIP.disabled = false;
|
||||
btnExportComicsZIP.innerHTML = '<span>📦 Baixar Todas as Imagens (ZIP)</span>';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --- MODO APRESENTAÇÃO EM TELA CHEIA (LEITURA INFANTIL / MOBILE) ---
|
||||
const comicsPresModal = document.getElementById('comicsPresentationModal');
|
||||
const comicsPresTitle = document.getElementById('comicsPresTitle');
|
||||
const comicsPresImage = document.getElementById('comicsPresImage');
|
||||
const comicsPresCaption = document.getElementById('comicsPresCaption');
|
||||
const comicsPresCounter = document.getElementById('comicsPresCounter');
|
||||
const btnPresPrev = document.getElementById('btnPresPrev');
|
||||
const btnPresNext = document.getElementById('btnPresNext');
|
||||
const btnCloseComicsPresModal = document.getElementById('btnCloseComicsPresModal');
|
||||
const btnComicsPresentationMode = document.getElementById('btnComicsPresentationMode');
|
||||
|
||||
let presCurrentIndex = 0;
|
||||
|
||||
const openComicsPresentation = (index = 0) => {
|
||||
if (!generatedPanelsData || generatedPanelsData.length === 0) return;
|
||||
presCurrentIndex = Math.max(0, Math.min(index, generatedPanelsData.length - 1));
|
||||
if (comicsPresModal) comicsPresModal.style.display = 'flex';
|
||||
updateComicsPresentationView();
|
||||
};
|
||||
|
||||
const closeComicsPresentation = () => {
|
||||
if (comicsPresModal) comicsPresModal.style.display = 'none';
|
||||
};
|
||||
|
||||
const updateComicsPresentationView = () => {
|
||||
if (!generatedPanelsData || generatedPanelsData.length === 0) return;
|
||||
const panel = generatedPanelsData[presCurrentIndex];
|
||||
if (!panel) return;
|
||||
|
||||
if (comicsPresTitle) comicsPresTitle.textContent = comicsResultTitle?.textContent || 'Fábrica de Quadrinhos';
|
||||
if (comicsPresCounter) comicsPresCounter.textContent = `${presCurrentIndex + 1} / ${generatedPanelsData.length}`;
|
||||
if (comicsPresImage) comicsPresImage.src = panel.imageUrl || panel.image_url;
|
||||
if (comicsPresCaption) comicsPresCaption.textContent = panel.dialogue || '(História silenciosa)';
|
||||
|
||||
if (btnPresPrev) btnPresPrev.disabled = presCurrentIndex === 0;
|
||||
if (btnPresNext) btnPresNext.disabled = presCurrentIndex === generatedPanelsData.length - 1;
|
||||
};
|
||||
|
||||
if (btnComicsPresentationMode) {
|
||||
btnComicsPresentationMode.addEventListener('click', () => {
|
||||
openComicsPresentation(0);
|
||||
});
|
||||
}
|
||||
|
||||
if (btnPresPrev) {
|
||||
btnPresPrev.addEventListener('click', () => {
|
||||
if (presCurrentIndex > 0) {
|
||||
presCurrentIndex--;
|
||||
updateComicsPresentationView();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (btnPresNext) {
|
||||
btnPresNext.addEventListener('click', () => {
|
||||
if (presCurrentIndex < generatedPanelsData.length - 1) {
|
||||
presCurrentIndex++;
|
||||
updateComicsPresentationView();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (btnCloseComicsPresModal) {
|
||||
btnCloseComicsPresModal.addEventListener('click', closeComicsPresentation);
|
||||
}
|
||||
|
||||
// Teclado (setas) para o modo apresentação
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if (comicsPresModal && comicsPresModal.style.display === 'flex') {
|
||||
if (e.key === 'ArrowRight' || e.key === 'Space') {
|
||||
if (presCurrentIndex < generatedPanelsData.length - 1) {
|
||||
presCurrentIndex++;
|
||||
updateComicsPresentationView();
|
||||
}
|
||||
} else if (e.key === 'ArrowLeft') {
|
||||
if (presCurrentIndex > 0) {
|
||||
presCurrentIndex--;
|
||||
updateComicsPresentationView();
|
||||
}
|
||||
} else if (e.key === 'Escape') {
|
||||
closeComicsPresentation();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Touch Swipe para smartphones no modo apresentação
|
||||
let touchStartX = 0;
|
||||
if (comicsPresModal) {
|
||||
comicsPresModal.addEventListener('touchstart', (e) => {
|
||||
touchStartX = e.changedTouches[0].screenX;
|
||||
}, { passive: true });
|
||||
|
||||
comicsPresModal.addEventListener('touchend', (e) => {
|
||||
const touchEndX = e.changedTouches[0].screenX;
|
||||
const diff = touchEndX - touchStartX;
|
||||
if (Math.abs(diff) > 50) {
|
||||
if (diff < 0 && presCurrentIndex < generatedPanelsData.length - 1) {
|
||||
// Swipe para a esquerda -> Próximo
|
||||
presCurrentIndex++;
|
||||
updateComicsPresentationView();
|
||||
} else if (diff > 0 && presCurrentIndex > 0) {
|
||||
// Swipe para a direita -> Anterior
|
||||
presCurrentIndex--;
|
||||
updateComicsPresentationView();
|
||||
}
|
||||
}
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
// Exportar PDF Retrato (1 por página)
|
||||
if (btnExportComicsPDFPortrait) {
|
||||
btnExportComicsPDFPortrait.addEventListener('click', async () => {
|
||||
|
||||
Reference in New Issue
Block a user