/*
HTML5 Reader for Electric Zine Maker, made by Jeremy Oduber & contributors 2019-2021
v21.5
Me:
https://twitter.com/JeremyOduber
This:
https://jeremyoduber.itch.io/js-zine
Electric Zine Maker:
https://alienmelon.itch.io/electric-zine-maker
GitHub:
https://github.com/jeremyoduber/EZM-Reader
Licensed under the MIT License:
https://github.com/jeremyoduber/EZM-Reader/blob/main/LICENSE
*/
//---- USER OPTIONS ----//
const TEMPLATE = 1; // Change this value to set the template
/*
Available templates:
1: 8 pages (default)
8 Page Folded Zine
8 Page Z-Fold
Quarter Size
2: 12 pages
Fancy T-Cut Zine
3: 14 pages
Easy Long Cut
4: 16 pages
16 Page Micro-Mini
Mini-Booklet
Fancy Flapbook
5: 24 pages
Tetraflexagon
6: 26 pages
Square Accordion
Normal Accordion
7: 32 pages
Mini-Mini-Booklet
8: 64 pages
Micro
*/
const BGCOLOR = '#f5f5f5'; // Change this hex value to set the background color. Remember to keep the quotes!
const ALT = 'Reader for Electric Zine Maker'; // Change this to a plaintext copy or description of your content to make it visible to screen-readers
const SMOOTH = true; // Set to false if you want crispy pixels. Leave true if you like the blur.
//---- END USER OPTIONS ----//
// Setup constants and variables
const FOV = 45;
const LOADING_OVERLAY = document.querySelector('#loading');
let card_amount;
let current_state = 0;
let textures = [];
let pages = [];
document.body.style.background = BGCOLOR;
document.body.ariaLabel = ALT;
if (SMOOTH) {
document.body.style.imageRendering = 'auto';
} else {
document.body.style.imageRendering = 'pixelated';
}
const metaTheme = document.createElement('meta');
metaTheme.name = 'theme-color';
metaTheme.content = BGCOLOR;
document.head.appendChild(metaTheme);
function getTextures(num) {
return ['pages/FRONT.png', 'pages/INNERFRONT.png'].concat(
new Array(num).fill().map((_, idx) => 'pages/' + (idx + 1) + '.png'),
['pages/BACK.png']
);
}
// Select template
switch (TEMPLATE) {
default:
case 1:
card_amount = 4;
textures = getTextures(5);
break;
case 2:
card_amount = 6;
textures = getTextures(9);
break;
case 3:
card_amount = 7;
textures = getTextures(11);
break;
case 4:
card_amount = 8;
textures = getTextures(13);
break;
case 5:
card_amount = 12;
textures = getTextures(21);
break;
case 6:
card_amount = 13;
textures = getTextures(23);
break;
case 7:
card_amount = 16;
textures = getTextures(29);
break;
case 8:
card_amount = 32;
textures = getTextures(61);
break;
}
// Preloader
Promise.all(
textures.map(
src =>
new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = src;
img.alt = src.split('/')[1].split('.')[0];
})
)
)
.then(imgs => {
LOADING_OVERLAY.remove();
const list = document.createElement('ul');
list.ariaHidden = true;
pages = imgs.map((img, idx) => {
const li = document.createElement('li');
const flip = idx % 2;
li.className = 'depth-' + Math.min(2, idx);
li.style.transform = 'translateX(100%) rotateY(0deg) scaleZ(' + (flip ? -1 : 1) + ')';
li.appendChild(img);
list.appendChild(li);
return li;
});
document.body.appendChild(list);
function updatePerspective() {
const w = window.innerWidth;
const h = window.innerHeight;
list.style.perspective = Math.sqrt(((w / 2) * w) / 2 + ((h / 2) * h) / 2) / Math.tan(((FOV / 2) * Math.PI) / 180) + 'px';
}
window.addEventListener('resize', updatePerspective);
updatePerspective();
})
.catch(error => {
console.error(error);
LOADING_OVERLAY.textContent = 'Something went wrong! Make sure your images are in the pages folder! See console for details.';
});
// Keyboard input
document.addEventListener('keyup', function onKeyUp(key) {
if (key.key === 'ArrowLeft' || key.key === 'a') {
flipLeft();
} else if (key.key === 'ArrowRight' || key.key === 'd') {
flipRight();
}
});
// Mouse input
document.addEventListener('pointerup', function onPointerUp(event) {
if (event.button !== 0) return;
if (event.clientX < window.innerWidth / 2) {
flipRight();
} else {
flipLeft();
}
});
function getPages(state) {
return [pages[state * 2], pages[state * 2 + 1]].filter(i => i);
}
function replaceTransformPerPage(state, search, replace) {
getPages(state).forEach(page => {
page.style.transform = page.style.transform.replace(search, replace);
});
}
function setDepth(state, depth) {
getPages(state).forEach(page => {
page.className = page.className.replace(/depth-\d+/, 'depth-' + Math.min(depth, 2));
});
}
// Flip page left
function flipLeft() {
if (current_state >= card_amount) return;
replaceTransformPerPage(current_state, '0deg', '-180deg');
setDepth(current_state - 1, 1);
setDepth(current_state - 2, 2);
setDepth(current_state + 1, 0);
setDepth(current_state + 2, 1);
++current_state;
}
// Flip page right
function flipRight() {
if (current_state <= 0) return;
replaceTransformPerPage(current_state - 1, '-180deg', '0deg');
setDepth(current_state - 3, 1);
setDepth(current_state - 2, 0);
setDepth(current_state + 1, 2);
setDepth(current_state, 1);
--current_state;
}