diff --git a/src/components/BlablaInput.astro b/src/components/BlablaInput.astro
index 69ce58a..b7187f4 100644
--- a/src/components/BlablaInput.astro
+++ b/src/components/BlablaInput.astro
@@ -238,6 +238,24 @@
+
+ Export
+ |
+
+
+
+ |
+
+
+
+
+ |
+
+
Delete / Reset
diff --git a/src/pages/update-log.md b/src/pages/update-log.md
index d4cc19f..f12abd5 100644
--- a/src/pages/update-log.md
+++ b/src/pages/update-log.md
@@ -9,6 +9,17 @@ embeddesc: the update log
---
+## video exporting `Dec 21, 2023`
+
+**blabla gen**
+- you can now export it as a video with animations like the game
+- choices are now also included when exporting to JSON
+
+
+
+---
+
+
## attachments `Dec 19, 2023`
**blabla gen**
diff --git a/src/scripts/blabla.js b/src/scripts/blabla.js
index 8851fc8..c30265d 100644
--- a/src/scripts/blabla.js
+++ b/src/scripts/blabla.js
@@ -1,6 +1,8 @@
import { draw9slice } from "./util.js";
import fuzzysort from "fuzzysort";
import { wifiOffI, wifiOnI } from "./util.js";
+import * as HME from "h264-mp4-encoder";
+import { Buffer } from "buffer";
const canvas = document.getElementById("blabla-canvas");
const ctx = canvas.getContext("2d", { willReadFrequently: true });
@@ -254,6 +256,29 @@ let loaded = {};
// loaded attachments
let loadedAttachments = {}
+/*
+0.3 seconds
+75 pixel offset
+sine out
+*/
+
+let xOffset = 0.0; // 75 to 0
+let yOffset = 0.0; // 10 to 0
+let alphaMult = 1.0;
+let messageMaxFrames = 0; // (0.3 seconds + (0.05 * amount of letters in message)) * 30
+let curMessageFrames = 0;
+let exporting = false;
+
+const easeOutSine = x => Math.sin((x * Math.PI) / 2);
+
+function resetAnimatables() {
+ xOffset = 0;
+ yOffset = 0;
+ alphaMult = 1;
+ messageMaxFrames = 0;
+ curMessageFrames = 0;
+}
+
function generateBlabla() {
ctx.clearRect(0, 0, canvas.width, canvas.height)
canvas.width = 540;
@@ -279,6 +304,16 @@ function generateBlabla() {
switchedSpeakers = true;
}
+ if (exporting && messageBeingAnimated == i) {
+ xOffset = 75 * (1 - easeOutSine(Math.min(curMessageFrames, 10) / 10));
+ yOffset = 10 * (1 - easeOutSine(Math.min(curMessageFrames, 10) / 10));
+ alphaMult = easeOutSine(Math.min(curMessageFrames, 10) / 10);
+ } else {
+ xOffset = 0;
+ yOffset = 0;
+ alphaMult = 1;
+ }
+
if (chats[i + 1] != null) {
if (curSpeaker != chats[i + 1].name.toLowerCase()) {
switch (chats[i + 1].name.toLowerCase()) {
@@ -326,10 +361,10 @@ function generateBlabla() {
scale = 350 / attachment.width;
}
- ctx.globalAlpha = 0.35;
- draw9slice(ctx, r_sbub, [slicex, 35, 2, 2], canvas.width - 39 - attachment.width * scale - 12, cury - 13, (attachment.width * scale) + margins + 4, (attachment.height * scale) + margins)
- ctx.globalAlpha = 1;
- draw9slice(ctx, r_bub, [slicex, 35, 2, 2], canvas.width - 39 - attachment.width * scale - 12, cury - 13, (attachment.width * scale) + margins + 4, (attachment.height * scale) + margins, item.color)
+ ctx.globalAlpha = 0.35 * alphaMult;
+ draw9slice(ctx, r_sbub, [slicex, 35, 2, 2], canvas.width - 39 + xOffset - attachment.width * scale - 12, cury - 13, (attachment.width * scale) + margins + 4, (attachment.height * scale) + margins)
+ ctx.globalAlpha = 1 * alphaMult;
+ draw9slice(ctx, r_bub, [slicex, 35, 2, 2], canvas.width - 39 + xOffset - attachment.width * scale - 12, cury - 13, (attachment.width * scale) + margins + 4, (attachment.height * scale) + margins, item.color)
attachmentCtx.globalCompositeOperation = 'source-over';
attachmentCtx.clearRect(0, 0, attachmentCanvas.width, attachmentCanvas.height);
@@ -339,7 +374,7 @@ function generateBlabla() {
attachmentCtx.globalCompositeOperation = 'source-in';
attachmentCtx.drawImage(attachment, 0, 0, attachmentCanvas.width, attachmentCanvas.height)
- ctx.drawImage(attachmentCanvas, canvas.width - 32 - attachment.width * scale - 1, cury + 5);
+ ctx.drawImage(attachmentCanvas, canvas.width - 32 + xOffset - attachment.width * scale - 1, cury + 5);
height = attachment.height * scale + margins;
} else {
@@ -347,12 +382,12 @@ function generateBlabla() {
innerBubbleWidth = width + 22 * 2 > 420 ? 420 : width + 22 * 2; // the bubble w/o shadow
textWidth = innerBubbleWidth - 22 * 2; // could either be 418 - 22 * 2 or width - 22 * 2
- ctx.globalAlpha = 0.35;
- draw9slice(ctx, r_sbub, [slicex, 35, 2, 2], canvas.width - 39 - innerBubbleWidth, cury - 13, innerBubbleWidth + 30, height)
- ctx.globalAlpha = 1;
- draw9slice(ctx, r_bub, [slicex, 35, 2, 2], canvas.width - 39 - innerBubbleWidth, cury - 13, innerBubbleWidth + 30, height, item.color)
+ ctx.globalAlpha = 0.35 * alphaMult;
+ draw9slice(ctx, r_sbub, [slicex, 35, 2, 2], canvas.width - 39 + xOffset - innerBubbleWidth, cury - 13, innerBubbleWidth + 30, height)
+ ctx.globalAlpha = 1 * alphaMult;
+ draw9slice(ctx, r_bub, [slicex, 35, 2, 2], canvas.width - 39 + xOffset - innerBubbleWidth, cury - 13, innerBubbleWidth + 30, height, item.color)
- ctx.fillText('', canvas.width - 20 - innerBubbleWidth + 14, cury + 20, textWidth);
+ ctx.fillText('', canvas.width - 20 + xOffset - innerBubbleWidth + 14, cury + 20, textWidth);
let attachmentImg = new Image();
attachmentImg.crossOrigin = "anonymous";
@@ -369,13 +404,13 @@ function generateBlabla() {
}
} else {
ctx.fillStyle = "#ffffff"
- ctx.globalAlpha = 0.35;
- draw9slice(ctx, r_sbub, [slicex, 35, 2, 2], canvas.width - 39 - innerBubbleWidth, cury - 13, innerBubbleWidth + 28, height)
- ctx.globalAlpha = 1;
- draw9slice(ctx, r_bub, [slicex, 35, 2, 2], canvas.width - 39 - innerBubbleWidth, cury - 13, innerBubbleWidth + 28, height, item.color)
+ ctx.globalAlpha = 0.35 * alphaMult;
+ draw9slice(ctx, r_sbub, [slicex, 35, 2, 2], canvas.width - 39 + xOffset - innerBubbleWidth, cury - 13, innerBubbleWidth + 28, height)
+ ctx.globalAlpha = 1 * alphaMult;
+ draw9slice(ctx, r_bub, [slicex, 35, 2, 2], canvas.width - 39 + xOffset - innerBubbleWidth, cury - 13, innerBubbleWidth + 28, height, item.color)
for (let j = 0; j < lines.length; j++) {
- ctx.fillText(lines[j].trim(), canvas.width - 20 - innerBubbleWidth + 14, cury + 20 + ((31) * j), textWidth);
+ ctx.fillText(lines[j].trim(), canvas.width - 20 + xOffset - innerBubbleWidth + 14, cury + 20 + ((31) * j), textWidth);
// ctx.fillStyle = "#ff0000";
// ctx.fillRect(canvas.width - 20 - innerBubbleWidth + 14, cury + 20 + ((31) * j), textWidth, 10);
@@ -387,13 +422,13 @@ function generateBlabla() {
case 'system':
ctx.fillStyle = "#dcdcdc"
ctx.textAlign = 'center';
- ctx.globalAlpha = 1;
+ ctx.globalAlpha = 1 * alphaMult;
let shitwidth = width + 32 * 2 > 500 ? 500 : width + 32 * 2;
- draw9slice(ctx, sybub, [20, 22, 2, 2], (canvas.width - shitwidth) / 2, cury, shitwidth, lines.length == 1 ? 55 : 28 + ((29) * (lines.length)), ' #989898')
+ draw9slice(ctx, sybub, [20, 22, 2, 2], (canvas.width - shitwidth) / 2, cury + yOffset, shitwidth, lines.length == 1 ? 55 : 28 + ((29) * (lines.length)), ' #989898')
for (let j = 0; j < lines.length; j++) {
- ctx.fillText(lines[j].trim(), canvas.width / 2, cury + 19 + ((29) * j), canvas.width);
+ ctx.fillText(lines[j].trim(), canvas.width / 2, cury + 19 + ((29) * j) + yOffset, canvas.width);
}
ctx.textAlign = 'left';
@@ -435,10 +470,10 @@ function generateBlabla() {
scale = 350 / attachment.width;
}
- ctx.globalAlpha = 0.35;
- draw9slice(ctx, sbub, [slicex, 35, 2, 2], curx - 23, cury - 13, (attachment.width * scale) + margins + 4, (attachment.height * scale) + margins)
- ctx.globalAlpha = 1;
- draw9slice(ctx, bub, [slicex, 35, 2, 2], curx - 23, cury - 13, (attachment.width * scale) + margins + 4, (attachment.height * scale) + margins, item.color)
+ ctx.globalAlpha = 0.35 * alphaMult;
+ draw9slice(ctx, sbub, [slicex, 35, 2, 2], curx - 23 - xOffset, cury - 13, (attachment.width * scale) + margins + 4, (attachment.height * scale) + margins)
+ ctx.globalAlpha = 1 * alphaMult;
+ draw9slice(ctx, bub, [slicex, 35, 2, 2], curx - 23 - xOffset, cury - 13, (attachment.width * scale) + margins + 4, (attachment.height * scale) + margins, item.color)
attachmentCtx.globalCompositeOperation = 'source-over';
attachmentCtx.clearRect(0, 0, attachmentCanvas.width, attachmentCanvas.height);
@@ -448,7 +483,7 @@ function generateBlabla() {
attachmentCtx.globalCompositeOperation = 'source-in';
attachmentCtx.drawImage(attachment, 0, 0, attachmentCanvas.width, attachmentCanvas.height)
- ctx.drawImage(attachmentCanvas, curx - 1, cury + 5);
+ ctx.drawImage(attachmentCanvas, curx - 1 - xOffset, cury + 5);
height = attachment.height * scale + margins;
} else {
@@ -456,12 +491,12 @@ function generateBlabla() {
innerBubbleWidth = width + 22 * 2 > 420 ? 420 : width + 22 * 2; // the bubble w/o shadow
textWidth = innerBubbleWidth - 22 * 2; // could either be 418 - 22 * 2 or width - 22 * 2
- ctx.globalAlpha = 0.35;
- draw9slice(ctx, sbub, [slicex, 35, 2, 2], curx - 23, cury - 13, innerBubbleWidth + 30, height)
- ctx.globalAlpha = 1;
- draw9slice(ctx, bub, [slicex, 35, 2, 2], curx - 23, cury - 13, innerBubbleWidth + 30, height, item.color)
+ ctx.globalAlpha = 0.35 * alphaMult;
+ draw9slice(ctx, sbub, [slicex, 35, 2, 2], curx - 23 - xOffset, cury - 13, innerBubbleWidth + 30, height)
+ ctx.globalAlpha = 1 * alphaMult;
+ draw9slice(ctx, bub, [slicex, 35, 2, 2], curx - 23 - xOffset, cury - 13, innerBubbleWidth + 30, height, item.color)
- ctx.fillText('', curx + 16, cury + 20, textWidth);
+ ctx.fillText('', curx + 16 - xOffset, cury + 20, textWidth);
let attachmentImg = new Image();
attachmentImg.crossOrigin = "anonymous";
@@ -477,13 +512,13 @@ function generateBlabla() {
}
}
} else {
- ctx.globalAlpha = 0.35;
- draw9slice(ctx, sbub, [slicex, 35, 2, 2], curx - 23, cury - 13, innerBubbleWidth + 30, height)
- ctx.globalAlpha = 1;
- draw9slice(ctx, bub, [slicex, 35, 2, 2], curx - 23, cury - 13, innerBubbleWidth + 30, height, item.color)
+ ctx.globalAlpha = 0.35 * alphaMult;
+ draw9slice(ctx, sbub, [slicex, 35, 2, 2], curx - 23 - xOffset, cury - 13, innerBubbleWidth + 30, height)
+ ctx.globalAlpha = 1 * alphaMult;
+ draw9slice(ctx, bub, [slicex, 35, 2, 2], curx - 23 - xOffset, cury - 13, innerBubbleWidth + 30, height, item.color)
for (let j = 0; j < lines.length; j++) {
- ctx.fillText(lines[j].trim(), curx + 16, cury + 20 + ((31) * j), textWidth);
+ ctx.fillText(lines[j].trim(), curx + 16 - xOffset, cury + 20 + ((31) * j), textWidth);
// ctx.fillStyle = "#ff0000";
// ctx.fillRect(curx + 16, cury + 20 + ((31) * j), textWidth, 10);
@@ -496,7 +531,7 @@ function generateBlabla() {
ctx.textBaseline = "bottom";
ctx.textAlign = "left";
- ctx.fillText(item.name, curx + 9, cury - 5);
+ ctx.fillText(item.name, curx + 9 - xOffset, cury - 5);
if (loaded[item.image] != null) {
let pfpImg = loaded[item.image];
@@ -513,7 +548,7 @@ function generateBlabla() {
let diff = top.height - pfpy + 19;
let cond = (diff > 0 ? diff : 0);
- ctx.drawImage(pfpCanvas, 0, diff > 0 ? diff : 0, 74, 74 - cond, 107 - 74 - 19, pfpy - 19 + cond, 74, 74 - cond)
+ ctx.drawImage(pfpCanvas, 0, diff > 0 ? diff : 0, 74, 74 - cond, 107 - 74 - 19 - xOffset, pfpy - 19 + cond, 74, 74 - cond)
} else {
let pfpImg = new Image();
pfpImg.crossOrigin = "anonymous";
@@ -534,12 +569,34 @@ function generateBlabla() {
}
cury += height - 26 + gap;
+
+ if (exporting) {
+ if (cury > canvas.height) {
+ ypos -= Math.abs(canvas.height - cury) - gap + 24;
+ }
+ }
}
// draw choices
// someone please help me im fucking dying
if (document.getElementById("choices").value.trim().length > 0) {
+ if (exporting) {
+ if (messageBeingAnimated == chats.length) {
+ xOffset = 75 * (1 - easeOutSine(Math.min(curMessageFrames, 10) / 10));
+ yOffset = 10 * (1 - easeOutSine(Math.min(curMessageFrames, 10) / 10));
+ alphaMult = easeOutSine(Math.min(curMessageFrames, 10) / 10);
+ } else {
+ xOffset = 75;
+ yOffset = 10;
+ alphaMult = 0;
+ }
+ } else {
+ xOffset = 0;
+ yOffset = 0;
+ alphaMult = 1;
+ }
+
let shit = document.getElementById("choices").value.trim().split('\n');
let maxWidth = 0;
let bubbleHeight = 0;
@@ -569,12 +626,12 @@ function generateBlabla() {
bubbleHeight += lines.length * 31 + 22;
}
- const bx = canvas.width - 24 - (maxWidth + 30 + 22 * 2) + 13;
+ const bx = canvas.width - 24 - (maxWidth + 30 + 22 * 2) + 13 + xOffset;
const by = cury - 13;
- ctx.globalAlpha = 0.35;
+ ctx.globalAlpha = 0.35 * alphaMult;
draw9slice(ctx, r_sbub, [37, 35, 2, 2], bx, by, maxWidth + 30 + 22 * 2, bubbleHeight + 7 * (choices.length - 1) + 13 * 4)
- ctx.globalAlpha = 1;
+ ctx.globalAlpha = 1 * alphaMult;
draw9slice(ctx, r_bub, [37, 35, 2, 2], bx, by, maxWidth + 30 + 22 * 2, bubbleHeight + 7 * (choices.length - 1) + 13 * 4, document.getElementById("com-color").value)
for (let i = 0; i < choices.length; i++) {
@@ -582,9 +639,9 @@ function generateBlabla() {
let ibx = bx + ((maxWidth + 30 + 22 * 2) - fuck.boxwidth) / 2;
let iby = innerbubcury + 26;
- ctx.globalAlpha = 0.5;
+ ctx.globalAlpha = 0.5 * alphaMult;
draw9slice(ctx, s_sybub, [26, 28, 2, 2], ibx - 6, iby - 2, fuck.boxwidth + 12, fuck.boxheight + 12);
- ctx.globalAlpha = 1;
+ ctx.globalAlpha = 1 * alphaMult;
draw9slice(ctx, sybub, [20, 22, 2, 2], ibx, iby + 4, fuck.boxwidth, fuck.boxheight, ' #ffffff');
for (let j = 0; j < fuck.lines.length; j++) {
@@ -593,8 +650,17 @@ function generateBlabla() {
innerbubcury += fuck.boxheight + 7;
}
+
+ if (exporting && messageBeingAnimated == chats.length) {
+ let add = (bubbleHeight + 7 * (choices.length - 1) + 13 * 2);
+ if (cury + add > canvas.height) {
+ ypos -= Math.abs(canvas.height - cury - add) + 24;
+ }
+ }
}
+ ctx.globalAlpha = 1;
+
ctx.drawImage(top, 0, 0);
ctx.globalAlpha = 0.4;
ctx.drawImage(shadow, 0, top.height);
@@ -629,17 +695,27 @@ function generateBlabla() {
for (let i = 0; i < chats.length; i++) {
let item = chats[i];
- ctx.drawImage(blabla_chatbox, curx - 13, cury - 13);
- ctx.drawImage(nikkelogo_c, curx + 125, cury + 20);
+ if (exporting && messageBeingAnimated == i) {
+ yOffset = 10 * (1 - easeOutSine(Math.min(curMessageFrames, 10) / 10));
+ alphaMult = easeOutSine(Math.min(curMessageFrames, 10) / 10);
+ } else {
+ yOffset = 0;
+ alphaMult = 1;
+ }
+
+ ctx.globalAlpha = alphaMult;
+
+ ctx.drawImage(blabla_chatbox, curx - 13, cury - 13 + yOffset);
+ ctx.drawImage(nikkelogo_c, curx + 125, cury + 20 + yOffset);
ctx.font = "18px PEB";
- ctx.fillText(item.name, curx + 139, cury + 19);
+ ctx.fillText(item.name, curx + 139, cury + 19 + yOffset);
ctx.font = "18px PR";
- ctx.fillText(item.message, curx + 123, cury + 50);
+ ctx.fillText(item.message, curx + 123, cury + 50 + yOffset);
if (loaded[item.image] != null) {
let pfpImg = loaded[item.image];
- let pfpy = cury;
+ let pfpy = cury + yOffset;
chatterCtx.drawImage(chatter_mask, 0, 0);
chatterCtx.globalCompositeOperation = 'source-in';
@@ -671,6 +747,8 @@ function generateBlabla() {
cury += 90;
}
+ ctx.globalAlpha = 1;
+
ctx.drawImage(top2, 0, 0);
ctx.font = "16px PEB";
@@ -977,7 +1055,10 @@ document.getElementById("char-img-edit").onchange = (e) => {
}
}
-document.getElementById("blabla-canvas").onclick = () => {
+document.getElementById("blabla-canvas").onclick = downloadPng;
+document.getElementById("export-png").onclick = downloadPng;
+
+function downloadPng() {
if (dragOn) return;
var link = document.createElement('a');
@@ -1076,6 +1157,10 @@ document.getElementById("chat-json-up").onchange = (e) => {
document.getElementById("ypos").value = ypos;
document.getElementById('chattime').value = json.chattime;
+ if (json.choices != null) {
+ document.getElementById("choices").value = json.choices.trim();
+ }
+
generateBlabla();
};
if (fileList.length > 0) {
@@ -1084,6 +1169,8 @@ document.getElementById("chat-json-up").onchange = (e) => {
};
document.getElementById("export").onclick = exportChat;
+document.getElementById("export-mp4").onclick = downloadVideo;
+
document.getElementById("com-color").onchange = () => {
generateBlabla();
}
@@ -1134,7 +1221,8 @@ function exportChat() {
let item = {
"chats": chats,
"chatname": chatname,
- "chattime": document.getElementById('chattime').value
+ "chattime": document.getElementById('chattime').value,
+ "choices": document.getElementById("choices").value.trim()
}
const link = document.createElement("a");
const file = new Blob([JSON.stringify(item, null, "\t")], { type: 'application/json' });
@@ -1145,6 +1233,88 @@ function exportChat() {
URL.revokeObjectURL(link.href);
}
+let messageBeingAnimated = 0;
+
+function downloadVideo() {
+ let defaultYpos = ypos;
+ ypos = 0;
+ generateBlabla();
+
+ if (exporting) return;
+ exporting = true;
+
+ HME.createH264MP4Encoder().then((encoder) => {
+
+ encoder.width = canvas.width % 2 == 0 ? canvas.width : canvas.width + 1;
+ encoder.height = canvas.height % 2 == 0 ? canvas.height : canvas.height + 1;
+ encoder.quantizationParameter = 15;
+ encoder.frameRate = 30;
+ encoder.initialize();
+
+ let maxChats = chats.length;
+ let copy = chats.map((x) => x);
+ chats = [];
+
+ for (let i = 0; i < copy.length; i++) {
+ let item = copy[i];
+ resetAnimatables();
+
+ messageBeingAnimated = i;
+
+ chats[i] = item;
+ messageMaxFrames = Math.round(10 + (0.05 * item.message.length) * 30);
+
+ if (!chatmode) {
+ if (item.attachment != null) {
+ messageMaxFrames = 20;
+ }
+ } else {
+ messageMaxFrames = 10;
+ }
+
+ while (curMessageFrames < messageMaxFrames) {
+ generateBlabla();
+ encoder.addFrameRgba(ctx.getImageData(0, 0, canvas.width, canvas.height).data);
+ curMessageFrames++;
+
+ document.title = "Exporting: " + chats.length + "/" + maxChats + " (" + (curMessageFrames / messageMaxFrames * 100).toFixed(2) + "%)";
+ }
+ }
+
+ if (document.getElementById("choices").value.trim().length > 0) {
+ messageBeingAnimated = chats.length;
+ resetAnimatables();
+ messageMaxFrames = 30;
+
+ while (curMessageFrames < messageMaxFrames) {
+ generateBlabla();
+ encoder.addFrameRgba(ctx.getImageData(0, 0, canvas.width, canvas.height).data);
+ curMessageFrames++;
+
+ document.title = "Exporting choices..." + " (" + (curMessageFrames / messageMaxFrames * 100).toFixed(2) + "%)";
+ }
+ }
+
+ encoder.finalize();
+
+ let output = encoder.FS.readFile(encoder.outputFilename);
+ let b64 = Buffer.from(output).toString('base64');
+
+ var link = document.createElement('a');
+ link.download = 'nikke-blabla.mp4';
+ link.href = "data:video/mp4;base64," + b64;
+ link.click();
+
+ encoder.delete();
+
+ resetAnimatables();
+ exporting = false;
+ document.title = "Barely Accurate NIKKE Blabla Generator";
+ ypos = defaultYpos;
+ generateBlabla();
+ })
+}
+
function getLinesForParagraphs(ctx, text, maxWidth) {
let ass = text.split("\n").map(para => getLines(ctx, para, maxWidth))
let res = []
|