collage-maker/web/index.js

312 lines
8.3 KiB
JavaScript

"use strict";
const pageSizes = {
"letter-portrait" : {
"ap": "85 / 110",
"width": 3264,
"height": 4224,
},
"letter-landscape" : {
"ap": "110 / 85",
"width": 4224,
"height": 3264,
},
"fiveseven-portrait" : {
"ap": "5 / 7",
"width": 2250,
"height": 3150,
},
"fiveseven-landscape" : {
"ap": "7 / 5",
"width": 3150,
"height": 2250,
},
"foursix-portrait" : {
"ap": "4 / 6",
"width": 1800,
"height": 2700,
},
"foursix-landscape" : {
"ap": "6 / 4",
"width": 2700,
"height": 1800,
},
}
// elements
/** @type {HTMLButtonElement} */
let snapButton
/** @type {HTMLDivElement} */
let collageDiv
/** @type {HTMLSelectElement} */
let pageSizeSelect
/** @type {HTMLDialogElement} */
let noticeDialog
/** @type {HTMLAnchorElement} */
let collageUrlA
/** @type {HTMLButtonElement} */
let reorderButton
/** @type {HTMLDivElement} */
let previewDiv
/** @type {HTMLButtonElement} */
let imagePrevBtn
/** @type {HTMLButtonElement} */
let imageNextBtn
/** @type {HTMLButtonElement} */
let imageDoneBtn
/** @type {HTMLDivElement} */
let imageChooserControls
// collage state
let crops = [];
let currentCropIndex = -1;
let pageSize = "letter-landscape";
/** @type {String[]} */
let imageUrls = []
function main() {
snapButton = document.getElementById("snapper")
collageDiv = document.getElementById("collage")
pageSizeSelect = document.getElementById("page_size_selector")
noticeDialog = document.getElementById("notice_dialog")
collageUrlA = document.getElementById("collage-url")
reorderButton = document.getElementById("reorder_btn")
previewDiv = document.getElementById("image-preview-box")
imageChooserControls = document.getElementById("image-chooser-controls")
imagePrevBtn = document.getElementById("image_prev_btn")
imageNextBtn = document.getElementById("image_next_btn")
imageDoneBtn = document.getElementById("image_done_btn")
imagePrevBtn.onclick = () => {
let newIndex = currentCropIndex - 1;
if(newIndex < 0) {
newIndex = 0
}
currentCropIndex = newIndex
changeOpacity()
}
imageNextBtn.onclick = () => {
let newIndex = currentCropIndex + 1;
if(newIndex >= crops.length) {
newIndex = crops.length - 1
}
currentCropIndex = newIndex
changeOpacity()
}
imageDoneBtn.onclick = () => {
currentCropIndex = -1
imageChooserControls.style.display = 'none';
/** @type {HTMLCollectionOf<HTMLDivElement> */
const divs = collageDiv.getElementsByClassName("img")
for(const elem of divs) {
elem.style.opacity = "100%"
}
}
snapButton.onclick = () => snap()
pageSizeSelect.onchange = () => pageSizeChange()
reorderButton.onclick = () => reorder()
for(const tmpl of document.getElementsByClassName("template")) {
// Assumes second class in the template is the collage's template
const [_, collageTemplate] = tmpl.classList
tmpl.dataset.collageTemplate = collageTemplate
tmpl.onclick = () => applyTemplate(tmpl)
}
const queryUrls = loadImageUrlsFromQuery()
// Skipping first entry in array to make the images start with index 1
imageUrls = [,].concat(queryUrls)
pageSizeChange()
applyTemplate(document.getElementById("default_template"))
}
/**
* @param {HTMLDivElement} elem
* @param {string} imgUrl
*/
function makeCroppieElem(elem, imgUrl) {
const cpie = new Croppie(elem, {
viewport: {
width: elem.clientWidth,
height: elem.clientHeight,
type: 'square'
},
showZoomer: false,
});
cpie.bind({
url: imgUrl
});
return cpie
}
function initCollage() {
for(const cpie of crops) {
cpie.destroy()
}
crops = []
for(const elem of collageDiv.getElementsByClassName("img")) {
const cpie = makeCroppieElem(elem, elem.dataset.collageImageUrl)
const lastLen = crops.push(cpie)
elem.dataset.collageCropieIndex = lastLen - 1
}
}
async function makeCollage(req) {
const resp = await fetch("make-collage", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(req),
})
return await resp.text();
}
function snap() {
const col = collageDiv.offsetLeft;
const cot = collageDiv.offsetTop;
const req = {
background_image: "",
aspect: {
width: pageSizes[pageSize]["width"],
height: pageSizes[pageSize]["height"],
},
dimension: {
width: collageDiv.clientWidth,
height: collageDiv.clientHeight,
},
photos: [],
};
for(const elem of collageDiv.getElementsByClassName("img")) {
const cpie = crops[elem.dataset.collageCropieIndex]
const fsx = elem.offsetLeft - col
const fsy = elem.offsetTop - cot
const [sx, sy, ex, ey] = cpie.get().points;
const photo = {
image: elem.dataset.collageImageUrl.slice("/photos/".length),
crop: {
start: {
x: parseInt(sx),
y: parseInt(sy),
},
end: {
x: parseInt(ex),
y: parseInt(ey),
},
},
frame: {
start: {
x: fsx,
y: fsy,
},
end: {
x: fsx + elem.clientWidth,
y: fsy + elem.clientHeight,
},
},
};
req.photos.push(photo)
}
(async () => {
noticeDialog.show()
collageUrlA.text = "Collage is being generated...";
const collagFile = await makeCollage(req)
collageUrlA.href = `collages/${collagFile}`;
collageUrlA.text = `${collagFile} generated`;
})();
}
function pageSizeChange() {
// https://stackoverflow.com/a/37802204
document.documentElement.style.setProperty('--collage-ap', pageSizes[pageSizeSelect.value]["ap"])
pageSize = pageSizeSelect.value
initCollage()
}
/** @param {HTMLDivElement} templateDiv */
function applyTemplate(templateDiv) {
document.getElementsByClassName("template-selected").item(0)?.classList.remove("template-selected")
/** @type {HTMLDivElement} */
const templateClone = templateDiv.cloneNode(true)
templateDiv.classList.add("template-selected")
for (const index in imageUrls) {
const url = imageUrls[index]
const imgClass = `img${index}`
const [imgDiv] = templateClone.getElementsByClassName(imgClass)
if(imgDiv === undefined) {
break;
}
imgDiv.dataset.collageImageUrl = url;
}
collageDiv.replaceChildren(...templateClone.children)
collageDiv.classList.remove(collageDiv.dataset.collageTemplate)
collageDiv.classList.add(templateDiv.dataset.collageTemplate)
collageDiv.dataset.collageTemplate = templateDiv.dataset.collageTemplate
initCollage()
imageDoneBtn.click()
}
function loadImageUrlsFromQuery() {
const params = new URLSearchParams(window.location.search)
const urlsstr = params.get('urls')
return JSON.parse(urlsstr)
}
function reorder() {
imageChooserControls.style.display = 'block';
previewDiv.replaceChildren()
currentCropIndex = 0;
for (const index in imageUrls) {
const url = imageUrls[index]
const img = new Image()
img.src = url
previewDiv.appendChild(img)
img.onclick = () => {
const cpie = crops[currentCropIndex]
const elem = cpie.element
elem.dataset.collageImageUrl = url
cpie.destroy()
crops[currentCropIndex] = makeCroppieElem(elem, url)
}
}
changeOpacity()
}
function changeOpacity() {
/** @type {HTMLCollectionOf<HTMLDivElement> */
const divs = collageDiv.getElementsByClassName("img")
for(const elem of divs) {
if (elem.dataset.collageCropieIndex != currentCropIndex) {
elem.style.opacity = "25%"
} else {
elem.style.opacity = "100%"
}
}
}
main()