diff --git a/app/src/components/AudioArea.vue b/app/src/components/AudioArea.vue index 1d29902..61657b4 100644 --- a/app/src/components/AudioArea.vue +++ b/app/src/components/AudioArea.vue @@ -22,6 +22,9 @@ export default defineComponent({ setup(props) { const audio = ref(null); + console.debug(`[AUDIOAREA] Initializing ${props.definition.src}...`); + console.debug(props.definition); + const MIN_SCALE = 0.02; const MIN_VOLUME_MULTIPLIER = 0.33; const vol_x = (1 - MIN_VOLUME_MULTIPLIER) / (1 - MIN_SCALE); @@ -33,11 +36,17 @@ export default defineComponent({ const distance = Math.sqrt(Math.pow(x - props.definition.cx, 2) + Math.pow(y - props.definition.cy, 2)); if (distance < props.definition.radius) { - audio.value!.play(); + if (audio.value!.paused) { + console.debug(`[AUDIOAREA] Entered audio area "${props.definition.src}", starting playback...`); + audio.value!.play(); + } const volume = (props.definition.radius - distance) / props.definition.radius; audio.value!.volume = volume * (props.bbox.z < 1 ? (props.bbox.z * vol_x + vol_b) : 1); } else { - audio.value!.pause(); + if (!audio.value!.paused) { + console.debug(`[AUDIOAREA] Left audio area "${props.definition.src}", pausing playback...`); + audio.value!.pause(); + } } }; watch(props.bbox, onBBoxChange, {deep: true}); diff --git a/app/src/components/SVGContent.vue b/app/src/components/SVGContent.vue index ae48b7a..5291df0 100644 --- a/app/src/components/SVGContent.vue +++ b/app/src/components/SVGContent.vue @@ -76,15 +76,20 @@ export default defineComponent({ onMounted(async () => { const element = root.value as unknown as HTMLDivElement; + console.info("[SVG] Initializing."); // Fetch & load SVG + console.info(`[SVG] Fetching "${props.url}..."`); const fetchResult = await fetch(props.url); + console.debug("[SVG] Fetched, parsing..."); const svgParsed = new DOMParser().parseFromString(await fetchResult.text(), "image/svg+xml") as Document; + console.debug("[SVG] Parsed."); const svg = element.appendChild(svgParsed.firstElementChild as Element) as any; // Set document background const pageColor = svg.getElementById("base")?.attributes.getNamedItem("pagecolor"); if (pageColor) { + console.debug(`[SVG] Found pageColor attribute: ${pageColor.value}`); emit("setBackground", pageColor.value); } @@ -97,8 +102,10 @@ export default defineComponent({ zoomDoubleClickSpeed: 1, onDoubleClick: function (e) { if (!document.fullscreenElement) { + console.debug("[SVG] Fullscreen requested."); document.body.requestFullscreen(); } else { + console.debug("[SVG] Fullscreen exited."); document.exitFullscreen(); } @@ -120,6 +127,7 @@ export default defineComponent({ }); function panToElement(target: SVGRectElement, smooth: boolean) { + console.debug(`[SVG] Panning to element: #${target.id}`); const transform = pz.getTransform(); const currentRatio = svg.clientWidth * transform.scale / svg.viewBox.baseVal.width; const ratio = svg.clientWidth / svg.viewBox.baseVal.width; @@ -151,9 +159,15 @@ export default defineComponent({ } } + panToAnchor.value = (anchor: SVGRectElement) => { + panToElement(anchor, true); + }; + + // Pan to start element const start = processStart(svg); if (start) { + console.info("[SVG] Found start element."); panToElement(start, false); window.addEventListener("keydown", (ev) => { @@ -164,20 +178,24 @@ export default defineComponent({ } // Anchors + console.debug("[SVG] Processing anchors."); anchors.value = processAnchors(svg); - panToAnchor.value = (anchor: SVGRectElement) => { - panToElement(anchor, true); - }; + console.info(`[SVG] Found ${anchors.value.length} anchors.`); // Audio areas + console.debug("[SVG] Processing audio areas."); audioAreas.value = processAudio(svg); + console.info(`[SVG] Found ${audioAreas.value.length} audio areas.`); // Videoscrolls + console.debug("[SVG] Processing video scrolls."); scrolls.value = await processScrolls(svg); + console.info(`[SVG] Found ${scrolls.value.length} video scrolls.`); // Debug Stats let stats: Stats | undefined; if (process.env.VUE_APP_DEMO) { + console.info("[SVG] DEMO mode active, turning on stats & dev panel."); stats = new Stats(); document.body.appendChild(stats.dom); } else { @@ -268,6 +286,7 @@ function processAnchors(document: XMLDocument): SVGRectElement[] { if (anchor === null) { break; } + console.debug(`[SVG/ANCHORS] Found anchor #${anchor.id}.`); anchor.classList.add("internal"); result.push(anchor); i++; @@ -282,13 +301,15 @@ async function processScrolls(svg: XMLDocument): Promise { Array.from(svg.getElementsByTagName("image")) .filter((el) => Array.from(el.children).some((el) => el.tagName == "desc")) .map(async (el) => { - const descNode = el.children[0]; // to fix - const [directionString, filesURL] = descNode.textContent!.split("\n"); + const descNode = Array.from(el.children).find((el) => el.tagName == "desc"); + console.debug(`[SVG/VIDEOSCROLLS] Found video scroll #${el.id}: ${descNode?.textContent}`); + const [directionString, filesURL] = descNode!.textContent!.split("\n"); if (!Object.values(VideoScrollDirection).includes(directionString as VideoScrollDirection)) { throw new Error("Unknown direction string."); } + console.debug(`[SVG/VIDEOSCROLLS] Fetching ${filesURL}...`); const fileFetch = await fetch(`content/${filesURL}`); const preURL = fileFetch.url.replace(/\/files.lst$/, ""); const files = (await fileFetch.text()).split("\n").filter(Boolean).map((str) => `${preURL}/${str}`); @@ -322,15 +343,14 @@ async function processScrolls(svg: XMLDocument): Promise { } function processAudio(svg: XMLDocument): AudioAreaDef[] { - const ratio = (svg as any).clientWidth / (svg as any).viewBox.baseVal.width; - return Array.from(svg.getElementsByTagName("circle")) .filter((el) => Array.from(el.children).some((el) => el.tagName == "desc")) .map((el) => { - el.classList.add("internal"); + const descNode = Array.from(el.children).find((el) => el.tagName == "desc"); + console.debug(`[SVG/AUDIOAREAS] Found audio area #${el.id}: ${descNode?.textContent}`); + const audioSrc = descNode!.textContent!.trim(); - const descNode = el.children[0]; // to fix - const audioSrc = descNode.textContent!.trim(); + el.classList.add("internal"); return { cx: el.cx.baseVal.value, diff --git a/app/src/components/VideoScroll.vue b/app/src/components/VideoScroll.vue index 0d9e204..77149d0 100644 --- a/app/src/components/VideoScroll.vue +++ b/app/src/components/VideoScroll.vue @@ -59,12 +59,16 @@ export default defineComponent({ setup(props) { const root = ref(null); + console.debug(`[VIDEOSCROLL] Initializing ${props.definition.files[0]}...`); + console.debug(props.definition); + onMounted(() => { const observer = new IntersectionObserver((entries, observer) => { entries.forEach((entry) => { const element = entry.target as HTMLImageElement; if (entry.isIntersecting) { if (!element.src) { + console.debug(`[VIDEOSCROLL] Intersected, loading ${element.dataset.src}`); element.src = element.dataset.src!; if (element.dataset.direction == "left" || element.dataset.direction == "right") { element.style.height = "auto";