UNPKG

comic-bubbles

Version:

Animated comic bubbles - what else?

377 lines (315 loc) 11.8 kB
import { getFullFontName, fontDescs, getCharDesc } from './fontMapper.mjs' import { incCharsDrawn, charsDrawn, counter, resetCharsDrawn } from './heartbeat.mjs' import { scenePrefs } from './scenePrefs.mjs' import { Char } from './objects/char.mjs' import { Word } from './objects/word.mjs' import { Row } from './objects/row.mjs' import { Text } from './objects/text.mjs' import { Bubble } from './objects/bubble.mjs' import { getScene } from './sceneCache.mjs' import { getResourceUrl } from './helperFunctions.mjs' export let phrasesJson = undefined export async function getRandomPhraseId() { let phrasesJson = await getPhrases() const randomPhrase = Math.floor(Math.random() * phrasesJson.phrases.length) const randomPhraseId = phrasesJson.phrases[randomPhrase].name return randomPhraseId } export async function getPhrases() { if (phrasesJson !== undefined) return phrasesJson try { const jsonPath = getResourceUrl('phrases JSON path', '/phrases.json') const response = await fetch(jsonPath) phrasesJson = await response.json() return phrasesJson } catch (error) { console.error(error) } } export async function injectBubbles(sceneId, phraseName) { resetCharsDrawn() let scene = getScene(sceneId) phrasesJson === undefined && (await getPhrases()) let phraseStr = getPhrase(phraseName).content const parser = new DOMParser() phraseStr = '<root>' + phraseStr + '</root>' const doc = parser.parseFromString(phraseStr, 'application/xml') let charStates = evalCharStatesArrForHtmlNodes(doc.firstChild, new Array()) let bubble = new Bubble() let text = new Text() let row = new Row() let word = new Word() for (let k = 0; k < charStates.length; k++) { let prevWordSpaceWidth = 0 let charState = charStates[k] if (charState.newParagraph || k == charStates.length - 1) { if (word.getCharCount() > 0) { row.addWord(word) text.addRow(row) } if (text.getRowCount() > 0) { bubble.body.setText(text) scene.bubCon.addBubble(bubble) } bubble = new Bubble() text = new Text() row = new Row() prevWordSpaceWidth = word.spaceWidth word = new Word() continue } if (charState.charIdx == 0 && charState.char != undefined) { if (word.getCharCount() > 0) { row.addWord(word) } prevWordSpaceWidth = word.spaceWidth word = new Word() } if (charState.char != undefined) { let b = scenePrefs.scene.bubCon.bubble let maxRowWidth = scene.bubCon.getWidth() - b.margin.left - b.margin.right - b.arrow.width maxRowWidth += -b.body.text.margin.left - b.body.text.margin.right - 10 if (row.getWidth() + word.bounds.width + prevWordSpaceWidth >= maxRowWidth) { if (row.getWordCount() > 0) { text.addRow(row) } row = new Row() } let char = charState.char let fontName = charState.fontName let bold = charState.bold let italic = charState.italic let charDesc = getCharDesc(char, fontName, bold, italic) let fontDesc = fontDescs.get(getFullFontName(fontName, bold, italic)) let charObj = new Char() charObj.name = charDesc.name charObj.margin.left = charDesc.margin.left charObj.margin.right = charDesc.margin.right charObj.margin.top = charDesc.margin.top charObj.margin.bottom = charDesc.margin.bottom charObj.bounds.x = charDesc.bounds.x charObj.bounds.y = charDesc.bounds.y charObj.bounds.width = charDesc.bounds.width charObj.bounds.height = charDesc.bounds.height charObj.font.name = fontName charObj.font.bold = fontDesc.bold charObj.font.italic = fontDesc.italic charObj.font.image = fontDesc.image charObj.font.fontHeight = fontDesc.fontHeight charObj.font.letterSpacing = fontDesc.letterSpacing charObj.font.lineSpacing = fontDesc.lineSpacing charObj.font.spaceWidth = fontDesc.spaceWidth word.addChar(charObj) incCharsDrawn() if (charsDrawn >= counter) { row.addWord(word) text.addRow(row) bubble.body.setText(text) scene.bubCon.addBubble(bubble) break } } } } // FIXME: DELETE THIS SHIT! export async function oldInjectBubbles(phraseName) { phrasesJson === undefined && (await getPhrases()) let phraseStr = getPhrase(phraseName).content const parser = new DOMParser() phraseStr = '<root>' + phraseStr + '</root>' const doc = parser.parseFromString(phraseStr, 'application/xml') let charStates = evalCharStatesArrForHtmlNodes(doc.firstChild, new Array()) let paragraphs = await getParagraphs() let maxRowWidth = await calcMaxRowWidth() for (let i = 0; i < paragraphs.length; i++) { let bubble = new Bubble() let text = new Text() let words = paragraphs[i] let row = new Row() for (let j = 0; j < words.length; j++) { let fullWord = words[j] let word = new Word() for (let k = 0; k < fullWord.getCharCount(); k++) { word.addChar(fullWord.char(k)) incCharsDrawn() if (charsDrawn >= counter) break } let prevWordSpaceWidth = j > 0 ? words[j - 1].spaceWidth : 0 if (row.getWidth() + word.bounds.width + prevWordSpaceWidth < maxRowWidth) { row.addWord(word) } else { text.addRow(row) row = new Row() row.addWord(word) } if (charsDrawn >= counter) break } if (row.getWordCount() > 0) { text.addRow(row) } bubble.body.setText(text) scene.bubCon.addBubble(bubble) if (charsDrawn >= counter) break } } class CharState { constructor() { this.char = undefined this.paragraphIdx = -1 this.newParagraph = false this.wordIdx = 0 this.charIdx = -1 this.fontName = 'font-6' this.bold = false this.italic = false } } function evalCharStatesArrForHtmlNodes(node, charStates) { if (charStates == undefined || charStates == null) charStates = new Array() let lastState = new CharState() if (charStates && charStates.length > 0) lastState = charStates[charStates.length - 1] if (node.tagName == 'root' || node.tagName == 'font-6') { if (node.childNodes.length > 0) { for (let j = 0; j < node.childNodes.length; j++) { charStates = evalCharStatesArrForHtmlNodes(node.childNodes[j], charStates) } } } else if (node.tagName == 'p') { let stateStart = new CharState() stateStart.paragraphIdx = lastState.paragraphIdx + 1 stateStart.newParagraph = true stateStart.wordIdx = 0 stateStart.charIdx = -1 stateStart.fontName = lastState.fontName stateStart.bold = lastState.bold stateStart.italic = lastState.italic charStates.push(stateStart) if (node.childNodes.length > 0) { for (let j = 0; j < node.childNodes.length; j++) { charStates = evalCharStatesArrForHtmlNodes(node.childNodes[j], charStates) } } } else if (node.tagName == 'i') { let stateStart = new CharState() stateStart.paragraphIdx = lastState.paragraphIdx stateStart.wordIdx = lastState.wordIdx stateStart.charIdx = lastState.charIdx stateStart.fontName = lastState.fontName stateStart.bold = lastState.bold stateStart.italic = true charStates.push(stateStart) if (node.childNodes.length > 0) { for (let j = 0; j < node.childNodes.length; j++) { charStates = evalCharStatesArrForHtmlNodes(node.childNodes[j], charStates) } } lastState = charStates[charStates.length - 1] let endStart = new CharState() endStart.paragraphIdx = lastState.paragraphIdx endStart.wordIdx = lastState.wordIdx endStart.charIdx = lastState.charIdx endStart.fontName = lastState.fontName endStart.bold = lastState.bold endStart.italic = false charStates.push(endStart) } else if (node.tagName == 'b') { let stateStart = new CharState() stateStart.paragraphIdx = lastState.paragraphIdx stateStart.wordIdx = lastState.wordIdx stateStart.charIdx = lastState.charIdx stateStart.fontName = lastState.fontName stateStart.bold = true stateStart.italic = lastState.italic charStates.push(stateStart) if (node.childNodes.length > 0) { for (let j = 0; j < node.childNodes.length; j++) { charStates = evalCharStatesArrForHtmlNodes(node.childNodes[j], charStates) } } lastState = charStates[charStates.length - 1] let endStart = new CharState() endStart.paragraphIdx = lastState.paragraphIdx endStart.wordIdx = lastState.wordIdx endStart.charIdx = lastState.charIdx endStart.fontName = lastState.fontName endStart.bold = false endStart.italic = lastState.italic charStates.push(endStart) } else if (node.nodeName == '#text') { for (let i = 0; i < node.data.length; i++) { lastState = charStates[charStates.length - 1] let char = node.data[i] let state = new CharState() state.paragraphIdx = lastState.paragraphIdx state.fontName = lastState.fontName state.bold = lastState.bold state.italic = lastState.italic if (char != ' ') { state.wordIdx = lastState.wordIdx state.char = char state.charIdx = lastState.charIdx + 1 } else { state.wordIdx = lastState.wordIdx + 1 state.charIdx = -1 } charStates.push(state) } } return charStates } // FIXME: DELETE THIS SHIT! export async function getParagraphs() { phrasesJson === undefined && (await getPhrases()) let phraseStr = getPhrase('ahnung').content const parser = new DOMParser() phraseStr = '<root>' + phraseStr + '</root>' const doc = parser.parseFromString(phraseStr, 'application/xml') // Get all the <p> elements in the document const paragraphs = doc.getElementsByTagName('p') let paragraphArr = new Array() // Loop through each <p> element and tokenize its substrings for (let i = 0; i < paragraphs.length; i++) { const paragraph = paragraphs[i] const text = paragraph.textContent.trim() const tokens = text.split(/\s+/) const fontName = 'font-6' const bold = false const italic = false let wordObjArr = new Array() for (let j = 0; j < tokens.length; j++) { const wordRead = tokens[j] let wordObj = new Word() for (let k = 0; k < wordRead.length; k++) { const charRead = wordRead[k] let charDesc = getCharDesc(charRead, fontName, bold, italic) let fontDesc = fontDescs.get(getFullFontName(fontName, bold, italic)) let charObj = new Char() charObj.name = charDesc.name charObj.margin.left = charDesc.margin.left charObj.margin.right = charDesc.margin.right charObj.margin.top = charDesc.margin.top charObj.margin.bottom = charDesc.margin.bottom charObj.bounds.x = charDesc.bounds.x charObj.bounds.y = charDesc.bounds.y charObj.bounds.width = charDesc.bounds.width charObj.bounds.height = charDesc.bounds.height charObj.font.name = fontName charObj.font.bold = fontDesc.bold charObj.font.italic = fontDesc.italic charObj.font.image = fontDesc.image charObj.font.fontHeight = fontDesc.fontHeight charObj.font.letterSpacing = fontDesc.letterSpacing charObj.font.lineSpacing = fontDesc.lineSpacing charObj.font.spaceWidth = fontDesc.spaceWidth wordObj.addChar(charObj) } wordObjArr.push(wordObj) } paragraphArr.push(wordObjArr) } return paragraphArr } export function getPhrase(phraseName) { const phrase = phrasesJson.phrases.find((element) => element.name === phraseName) return phrase }