Compare commits
6 Commits
671a4d9b90
...
b46ed80a00
| Author | SHA1 | Date | |
|---|---|---|---|
|
b46ed80a00
|
|||
|
3bede11393
|
|||
|
84e3a14677
|
|||
|
04537eadb8
|
|||
|
c3a8904f4d
|
|||
|
a0d90eedca
|
@@ -4,35 +4,20 @@ class IPUBElement extends HTMLElement {
|
|||||||
static observedAttributes = ["id"];
|
static observedAttributes = ["id"];
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.ensureID();
|
this.#ensureID();
|
||||||
}
|
}
|
||||||
|
|
||||||
attributeChangedCallback(_name, _oldValue, _newValue) {
|
attributeChangedCallback(_name, _oldValue, _newValue) {
|
||||||
this.ensureID();
|
this.#ensureID();
|
||||||
}
|
}
|
||||||
|
|
||||||
ensureID() {
|
/**
|
||||||
if (this.id) {
|
* @private
|
||||||
return;
|
*/
|
||||||
|
#ensureID() {
|
||||||
|
if (!this.id) {
|
||||||
|
this.id = hashFromHTML(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// INFO: Hash algorithm by Joe Freeman & Cristian Sanchez at StackOverflow:
|
|
||||||
// https://stackoverflow.com/a/16348977
|
|
||||||
// https://stackoverflow.com/a/3426956
|
|
||||||
//
|
|
||||||
// Licensed under CC BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0/)
|
|
||||||
|
|
||||||
let hash = 0;
|
|
||||||
this.innerHTML.split("").forEach((char) => {
|
|
||||||
hash = char.charCodeAt(0) + ((hash << 5) - hash);
|
|
||||||
});
|
|
||||||
|
|
||||||
let id = "";
|
|
||||||
for (let i = 0; i < 3; i++) {
|
|
||||||
id += ((hash >> (i * 8)) & 0xff).toString(16).padStart(2, "0");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.id = id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,49 +28,84 @@ class IPUBBackground extends IPUBElement {
|
|||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
static #observer = (() => {
|
static #instancesOnScreen = {
|
||||||
/** @type {Map<string, IPUBBackground>} */
|
/** @type {Map<IPUBBody, Set<IPUBBackground>>} */
|
||||||
const instancesOnScreen = new Map();
|
map: new Map(),
|
||||||
|
/**
|
||||||
document.addEventListener("scroll", () => {
|
* @param {IPUBBackground} background
|
||||||
for (const [_, instance] of instancesOnScreen) {
|
*/
|
||||||
const perc = getPercentageInView(
|
add(background) {
|
||||||
instance.querySelector("img") || instance,
|
const body = getAncestor(background, IPUBBody.elementName);
|
||||||
|
if (!body) {
|
||||||
|
console.error(
|
||||||
|
`IPUBBackground: ${background.id} does not have a valid ipub-body ancestor`,
|
||||||
);
|
);
|
||||||
instance.fade(perc);
|
return;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
return new IntersectionObserver((entries) =>
|
let set = this.map.get(body);
|
||||||
entries.forEach((e) => {
|
if (!set) {
|
||||||
let instance = e.target.parentElement;
|
set = new Set();
|
||||||
|
body.addEventListener("scroll", () => {
|
||||||
|
for (const instance of set.values()) {
|
||||||
|
const perc = getPercentageInView(
|
||||||
|
instance.querySelector("img") || instance,
|
||||||
|
);
|
||||||
|
instance.fade(perc);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
set.add(background);
|
||||||
|
this.map.set(body, set);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {IPUBBackground} background
|
||||||
|
*/
|
||||||
|
remove(background) {
|
||||||
|
const body = getAncestor(background, IPUBBody.elementName);
|
||||||
|
if (!body) {
|
||||||
|
console.error(
|
||||||
|
`IPUBBackground: ${background.id} does not have a valid ipub-body ancestor`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
const set = this.map.get(body);
|
||||||
instance.tagName.toLowerCase() !==
|
if (!set) {
|
||||||
IPUBBackground.elementName.toLowerCase()
|
return;
|
||||||
) {
|
}
|
||||||
instance = instance.parentElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
set.delete(background);
|
||||||
instance.tagName.toLowerCase() !==
|
|
||||||
IPUBBackground.elementName.toLowerCase()
|
|
||||||
) {
|
|
||||||
console.error(
|
|
||||||
"IPUBBackground: malformed <ipub-background> element",
|
|
||||||
e.target,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.intersectionRatio > 0 && instance.id) {
|
if (set.size === 0) {
|
||||||
instancesOnScreen.set(instance.id, instance);
|
this.map.delete(body);
|
||||||
} else if (instance.id) {
|
}
|
||||||
instancesOnScreen.delete(instance.id);
|
},
|
||||||
}
|
};
|
||||||
}),
|
static addToScreen() {}
|
||||||
);
|
|
||||||
})();
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static #observer = new IntersectionObserver((entries) => {
|
||||||
|
for (const { intersectionRatio, target: image } of entries) {
|
||||||
|
const instance = getAncestor(image, IPUBBackground.elementName);
|
||||||
|
|
||||||
|
if (!instance) {
|
||||||
|
console.error(
|
||||||
|
"IPUBBackground: malformed <ipub-background> element",
|
||||||
|
image,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intersectionRatio > 0 && instance.id) {
|
||||||
|
IPUBBackground.#instancesOnScreen.add(instance);
|
||||||
|
} else if (instance.id) {
|
||||||
|
IPUBBackground.#instancesOnScreen.remove(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
@@ -141,8 +161,6 @@ class IPUBBackground extends IPUBElement {
|
|||||||
* @returns {void | Promise<void>}
|
* @returns {void | Promise<void>}
|
||||||
*/
|
*/
|
||||||
fade(perc) {
|
fade(perc) {
|
||||||
console.debug(`IPUBBackground: ${this.id} is ${perc} on screen`);
|
|
||||||
|
|
||||||
if (!this.style.getPropertyValue("--ipub-fade")) {
|
if (!this.style.getPropertyValue("--ipub-fade")) {
|
||||||
this.style.setProperty("--ipub-fade", `${perc}%`);
|
this.style.setProperty("--ipub-fade", `${perc}%`);
|
||||||
return;
|
return;
|
||||||
@@ -159,8 +177,102 @@ class IPUBBackground extends IPUBElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class IPUBContent extends IPUBElement {
|
class IPUBBody extends IPUBElement {
|
||||||
static elementName = "ipub-content";
|
static elementName = "ipub-body";
|
||||||
|
|
||||||
|
static defineContentElements() {
|
||||||
|
for (const e of [
|
||||||
|
IPUBBackground,
|
||||||
|
IPUBImage,
|
||||||
|
IPUBInteraction,
|
||||||
|
]) {
|
||||||
|
console.info(`IPUBBody: Defining custom element <${e.elementName}>`);
|
||||||
|
globalThis.customElements.define(e.elementName, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
|
||||||
|
this.setAttribute("aria-busy", "true");
|
||||||
|
|
||||||
|
console.log("IPUBBody: defining custom element <ipub-cover>");
|
||||||
|
globalThis.customElements.define(IPUBCover.elementName, IPUBCover);
|
||||||
|
|
||||||
|
/** @type {IPUBCover} */
|
||||||
|
const cover = this.querySelector("ipub-cover");
|
||||||
|
if (!cover) {
|
||||||
|
IPUBBody.defineContentElements();
|
||||||
|
}
|
||||||
|
cover.onclose = IPUBBody.defineContentElements;
|
||||||
|
|
||||||
|
this.setAttribute("aria-busy", "false");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis.addEventListener("load", () => {
|
||||||
|
console.info("IPUB: Starting IPUB elements");
|
||||||
|
|
||||||
|
console.log("IPUB: Defining custom element <ipub-body>");
|
||||||
|
globalThis.customElements.define(IPUBBody.elementName, IPUBBody);
|
||||||
|
});
|
||||||
|
|
||||||
|
class IPUBCover extends IPUBElement {
|
||||||
|
static elementName = "ipub-cover";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {() => void} callback
|
||||||
|
*/
|
||||||
|
onclose = () => {};
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
|
||||||
|
console.debug("IPUBCover: Setting up cover");
|
||||||
|
this.setAttribute("aria-busy", "true");
|
||||||
|
|
||||||
|
const dialog = this.querySelector("dialog");
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
|
// HACK: Test if we can autoplay interactions, soundtracks, etc
|
||||||
|
|
||||||
|
/** @type {HTMLMediaElement | null} */
|
||||||
|
const media =
|
||||||
|
this.parentElement.querySelector("audio") ??
|
||||||
|
this.parentElement.querySelector("video");
|
||||||
|
|
||||||
|
if (!media) {
|
||||||
|
dialog.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pastVolume = media.volume;
|
||||||
|
media.volume = 0.1; // don't let the user hear the test audio
|
||||||
|
|
||||||
|
media
|
||||||
|
.play()
|
||||||
|
.then(() => {
|
||||||
|
media.pause();
|
||||||
|
media.volume = pastVolume;
|
||||||
|
media.currentTime = 0;
|
||||||
|
|
||||||
|
console.debug("IPUBCover: Can autoplay interactions, removing cover");
|
||||||
|
this.onclose();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
console.debug(
|
||||||
|
"IPUBCover: Cannot autoplay interactions, covering content",
|
||||||
|
);
|
||||||
|
|
||||||
|
dialog.parentElement.addEventListener("click", () => {
|
||||||
|
dialog.close();
|
||||||
|
this.onclose();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setAttribute("aria-busy", "false");
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class IPUBImage extends IPUBElement {
|
class IPUBImage extends IPUBElement {
|
||||||
@@ -171,72 +283,44 @@ class IPUBInteraction extends IPUBElement {
|
|||||||
static elementName = "ipub-interaction";
|
static elementName = "ipub-interaction";
|
||||||
}
|
}
|
||||||
|
|
||||||
globalThis.addEventListener("load", () => {
|
|
||||||
console.info("IPUB: STARTING DEFINITIONS");
|
|
||||||
|
|
||||||
[IPUBBackground, IPUBContent, IPUBImage, IPUBInteraction].forEach((e) => {
|
|
||||||
console.info(`IPUB: Defining custom element <${e.elementName}>`);
|
|
||||||
globalThis.customElements.define(e.elementName, e);
|
|
||||||
});
|
|
||||||
|
|
||||||
console.info("IPUB: FINISHED DEFINITIONS");
|
|
||||||
|
|
||||||
/** @type {Map<string, Element>} */
|
|
||||||
const onScreenMap = new Map();
|
|
||||||
|
|
||||||
const observer = new IntersectionObserver((entries) => {
|
|
||||||
entries.forEach((e) => {
|
|
||||||
if (e.intersectionRatio > 0) {
|
|
||||||
// console.debug(
|
|
||||||
// `IntersectionObserver: adding element #${e.target.id} to onScreenMap`,
|
|
||||||
// );
|
|
||||||
onScreenMap.set(e.target.id, e.target);
|
|
||||||
} else {
|
|
||||||
// console.debug(
|
|
||||||
// `IntersectionObserver: removing element #${e.target.id} to onScreenMap`,
|
|
||||||
// );
|
|
||||||
onScreenMap.delete(e.target.id);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
for (const element of document.querySelectorAll(
|
|
||||||
`[data-ipub-trigger="on-screen"]`,
|
|
||||||
)) {
|
|
||||||
observer.observe(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("scroll", async () => {
|
|
||||||
for (const [id, element] of onScreenMap) {
|
|
||||||
const perc = getPercentageInView(element);
|
|
||||||
// console.debug(`Element #${id} is now ${perc}% on screen`);
|
|
||||||
|
|
||||||
const played = element.getAttribute("data-ipub-trigger-played") == "true";
|
|
||||||
|
|
||||||
if (perc >= 100 && !played) {
|
|
||||||
await playIpubElement(element);
|
|
||||||
element.setAttribute("data-ipub-trigger-played", "true");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Element} element
|
|
||||||
*/
|
*/
|
||||||
async function playIpubElement(element) {
|
|
||||||
switch (element.tagName) {
|
|
||||||
case "audio": {
|
|
||||||
/** @type {HTMLAudioElement} */
|
|
||||||
const audio = element;
|
|
||||||
|
|
||||||
// await audio.play();
|
/**
|
||||||
|
* @param {Readonly<Element>} el
|
||||||
break;
|
* @param {number} [length=6]
|
||||||
}
|
* @returns {string}
|
||||||
default:
|
*
|
||||||
break;
|
* @copyright This function contains code from a hash algorithm by
|
||||||
|
* {@link https://stackoverflow.com/a/16348977|Joe Freeman} and
|
||||||
|
* {@link https://stackoverflow.com/a/3426956|Cristian Sanchez} at
|
||||||
|
* StackOverflow, licensed under {@link https://creativecommons.org/licenses/by-sa/3.0/|CC BY-SA 3.0}.
|
||||||
|
*/
|
||||||
|
function hashFromHTML(el, length = 6) {
|
||||||
|
const hexLength = length / 2;
|
||||||
|
if (hexLength % 2 !== 0) {
|
||||||
|
hexLength + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let hash = 0;
|
||||||
|
for (const char of el.innerHTML) {
|
||||||
|
hash = char.charCodeAt(0) + ((hash << 5) - hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
let hex = "";
|
||||||
|
for (let i = 0; i < hexLength; i++) {
|
||||||
|
hex += ((hash >> (i * 8)) & 0xff).toString(16).padStart(2, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex.substring(0, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -9,8 +9,19 @@
|
|||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body xmlns:epub="http://www.idpf.org/2007/ops" class="body">
|
<body xmlns:epub="http://www.idpf.org/2007/ops" class="body">
|
||||||
<ipub-content style="--ipub-padding: 10%;">
|
<ipub-body style="--ipub-padding: 10%;">
|
||||||
<main>
|
<ipub-cover>
|
||||||
|
<dialog>
|
||||||
|
<header>
|
||||||
|
<h1>Test comic</h1>
|
||||||
|
<form method="dialog">
|
||||||
|
<p>Click anywhere to
|
||||||
|
<button>start Reading</button></p>
|
||||||
|
</form>
|
||||||
|
</header>
|
||||||
|
</dialog>
|
||||||
|
</ipub-cover>
|
||||||
|
<main id="content">
|
||||||
<ipub-background id="background0001">
|
<ipub-background id="background0001">
|
||||||
<img src="../images/background0001.jpg" width="100" height="100" />
|
<img src="../images/background0001.jpg" width="100" height="100" />
|
||||||
</ipub-background>
|
</ipub-background>
|
||||||
@@ -25,60 +36,35 @@
|
|||||||
rel="external nofollow noopener noreferrer" target="_blank" />
|
rel="external nofollow noopener noreferrer" target="_blank" />
|
||||||
</ipub-interaction>
|
</ipub-interaction>
|
||||||
</ipub-image>
|
</ipub-image>
|
||||||
<section data-ipub-element="page" id="page02">
|
<ipub-image>
|
||||||
<span data-ipub-element="image">
|
<img src="../images/image0002.png" />
|
||||||
<img src="../images/image0002.png" />
|
</ipub-image>
|
||||||
</span>
|
|
||||||
<!--
|
|
||||||
This in the UI would be an "Area Interaction". The editor would first place
|
|
||||||
the first top-left point, and then the bottom-right one, to select an area/size
|
|
||||||
of the interaction.
|
|
||||||
|
|
||||||
The element wound not have a "action" per say, but would have a "on screen" trigger,
|
|
||||||
which in itself would have the action "play sound".
|
|
||||||
-->
|
|
||||||
<audio data-ipub-element="interaction" data-ipub-trigger="on-screen" controls="true"
|
|
||||||
volume="0" style="--ipub-x: 20%; --ipub-y: 25%; --ipub-width: 50%; --ipub-height: 50%;"
|
|
||||||
id="int-audio0001">
|
|
||||||
<source src="../audios/audio0001.wav.disable" />
|
|
||||||
</audio>
|
|
||||||
</section>
|
|
||||||
<ipub-background id="background0002">
|
<ipub-background id="background0002">
|
||||||
<picture>
|
<picture>
|
||||||
<img src="../images/background0002.jpg" />
|
<img src="../images/background0002.jpg" />
|
||||||
</picture>
|
</picture>
|
||||||
</ipub-background>
|
</ipub-background>
|
||||||
<section data-ipub-element="page" id="page03">
|
<ipub-image>
|
||||||
<span data-ipub-element="image">
|
<img src="../images/image0003.png" />
|
||||||
<img src="../images/image0003.png" />
|
</ipub-image>
|
||||||
</span>
|
<ipub-image>
|
||||||
</section>
|
<img src="../images/image0004.png" />
|
||||||
<section data-ipub-element="page" id="page04">
|
</ipub-image>
|
||||||
<span data-ipub-element="image">
|
|
||||||
<img src="../images/image0004.png" />
|
|
||||||
</span>
|
|
||||||
</section>
|
|
||||||
<ipub-background id="background0003">
|
<ipub-background id="background0003">
|
||||||
<picture>
|
<picture>
|
||||||
<img src="../images/background0003.jpg" />
|
<img src="../images/background0003.jpg" />
|
||||||
</picture>
|
</picture>
|
||||||
</ipub-background>
|
</ipub-background>
|
||||||
<section data-ipub-element="page" id="page02">
|
<ipub-image>
|
||||||
<span data-ipub-element="image">
|
<img src="../images/image0002.png" />
|
||||||
<img src="../images/image0002.png" />
|
</ipub-image>
|
||||||
</span>
|
<ipub-image>
|
||||||
</section>
|
<img src="../images/image0003.png" />
|
||||||
<section data-ipub-element="page" id="page04">
|
</ipub-image>
|
||||||
<span data-ipub-element="image">
|
<ipub-image>
|
||||||
<img src="../images/image0003.png" />
|
<img src="../images/image0004.png" />
|
||||||
</span>
|
</ipub-image>
|
||||||
</section>
|
|
||||||
<section data-ipub-element="page" id="page04">
|
|
||||||
<span data-ipub-element="image">
|
|
||||||
<img src="../images/image0004.png" />
|
|
||||||
</span>
|
|
||||||
</section>
|
|
||||||
</main>
|
</main>
|
||||||
</ipub-content>
|
</ipub-body>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -8,9 +8,45 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
|
max-height: 100vh;
|
||||||
|
overflow: clip;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
--z-cover: 9;
|
||||||
|
ipub-cover > dialog[open] {
|
||||||
|
--ipub-accent-color: #fff;
|
||||||
|
z-index: var(--z-cover);
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
backdrop-filter: blur(1rem);
|
||||||
|
background-image: linear-gradient(
|
||||||
|
rgba(from var(--ipub-accent-color) r g b / 0) 0%,
|
||||||
|
rgba(from var(--ipub-accent-color) r g b / 0.5)
|
||||||
|
calc(100% + calc(var(--ipub-fade, 50%) * -1))
|
||||||
|
);
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
ipub-content {
|
ipub-body {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
overflow: scroll;
|
||||||
|
&:has(ipub-cover > dialog[open]) {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
--ipub-padding: 0%;
|
--ipub-padding: 0%;
|
||||||
--ipub-gap: 0%;
|
--ipub-gap: 0%;
|
||||||
--ipub-padding-x: var(--ipub-padding, 0%);
|
--ipub-padding-x: var(--ipub-padding, 0%);
|
||||||
@@ -146,44 +182,3 @@ ipub-interaction {
|
|||||||
--ipub-radius: 100%;
|
--ipub-radius: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-ipub-element="image"] {
|
|
||||||
width: var(--ipub-width, unset);
|
|
||||||
height: var(--ipub-height, unset);
|
|
||||||
background-image: var(--ipub-image, unset);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: cover;
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-ipub-element="interaction"] {
|
|
||||||
position: absolute;
|
|
||||||
left: var(--ipub-x, 0%);
|
|
||||||
top: var(--ipub-y, 0%);
|
|
||||||
border-radius: var(--ipub-radius, unset);
|
|
||||||
width: var(--ipub-width, unset);
|
|
||||||
height: var(--ipub-height, unset);
|
|
||||||
transform: translate(
|
|
||||||
var(--ipub-origin-offset-x, 0%),
|
|
||||||
var(--ipub-origin-offset-y, 0%)
|
|
||||||
);
|
|
||||||
aspect-ratio: var(--ipub-ratio, unset);
|
|
||||||
/*
|
|
||||||
* The opacity would be, by default, zero. Here it is 0.3 for easier debugging and
|
|
||||||
* showing of the example ebook
|
|
||||||
*/
|
|
||||||
background-color: red;
|
|
||||||
opacity: 0.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
a[data-ipub-element="interaction"] {
|
|
||||||
/* The text inside the interaction anchor are for accessibility purposes */
|
|
||||||
font-size: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user