comic-bubbles
Version:
Animated comic bubbles - what else?
377 lines (315 loc) • 11.8 kB
JavaScript
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
}