autoformat
This commit is contained in:
parent
bfa569853b
commit
7bff008853
4 changed files with 131 additions and 83 deletions
|
@ -1,32 +1,40 @@
|
|||
<template>
|
||||
<SVGContent id="root" url="content/intro.svg" @set-background="setBackground"/>
|
||||
<SVGContent
|
||||
id="root"
|
||||
url="content/intro.svg"
|
||||
@set-background="setBackground"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent} from "vue";
|
||||
import { defineComponent } from "vue";
|
||||
import SVGContent from "@/components/SVGContent.vue";
|
||||
import "normalize.css";
|
||||
|
||||
export default defineComponent({
|
||||
name: "App",
|
||||
components: {
|
||||
SVGContent
|
||||
SVGContent,
|
||||
},
|
||||
methods: {
|
||||
setBackground(background: string) {
|
||||
document.body.style.background = background;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
html,
|
||||
body {
|
||||
overflow: hidden;
|
||||
background: black;
|
||||
}
|
||||
|
||||
html, body, #app, #root {
|
||||
html,
|
||||
body,
|
||||
#app,
|
||||
#root {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: default;
|
||||
|
|
|
@ -1,23 +1,21 @@
|
|||
<template>
|
||||
<audio ref="audio"
|
||||
:src="definition.src"
|
||||
loop preload="auto"/>
|
||||
<audio ref="audio" :src="definition.src" loop preload="auto" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent, PropType, ref, watch} from "vue";
|
||||
import {BoundingBox} from "@/components/SVGContent.vue";
|
||||
import { defineComponent, PropType, ref, watch } from "vue";
|
||||
import { BoundingBox } from "@/components/SVGContent.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "AudioArea",
|
||||
props: {
|
||||
definition: {
|
||||
type: Object as PropType<AudioAreaDef>,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
bbox: {
|
||||
type: Object as PropType<BoundingBox>,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
|
@ -34,38 +32,46 @@ export default defineComponent({
|
|||
const onBBoxChange = () => {
|
||||
const x = props.bbox.x + props.bbox.w / 2;
|
||||
const y = props.bbox.y + props.bbox.h / 2;
|
||||
const distance = Math.sqrt(Math.pow(x - props.definition.cx, 2) + Math.pow(y - props.definition.cy, 2));
|
||||
const distance = Math.sqrt(
|
||||
Math.pow(x - props.definition.cx, 2) +
|
||||
Math.pow(y - props.definition.cy, 2)
|
||||
);
|
||||
|
||||
if (distance < props.definition.radius) {
|
||||
if (audio.value!.paused) {
|
||||
console.debug(`[AUDIOAREA] Entered audio area "${props.definition.src}", starting playback...`);
|
||||
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);
|
||||
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 {
|
||||
if (!audio.value!.paused) {
|
||||
console.debug(`[AUDIOAREA] Left audio area "${props.definition.src}", pausing playback...`);
|
||||
console.debug(
|
||||
`[AUDIOAREA] Left audio area "${props.definition.src}", pausing playback...`
|
||||
);
|
||||
audio.value!.pause();
|
||||
}
|
||||
}
|
||||
};
|
||||
watch(props.bbox, onBBoxChange, {deep: true});
|
||||
watch(props.bbox, onBBoxChange, { deep: true });
|
||||
|
||||
return {
|
||||
audio
|
||||
audio,
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export interface AudioAreaDef {
|
||||
id: string,
|
||||
cx: number,
|
||||
cy: number,
|
||||
radius: number,
|
||||
src: string
|
||||
id: string;
|
||||
cx: number;
|
||||
cy: number;
|
||||
radius: number;
|
||||
src: string;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
|
|
|
@ -5,10 +5,19 @@
|
|||
</div>
|
||||
<div class="content" ref="root">
|
||||
<div class="video-scrolls">
|
||||
<VideoScroll v-for="scroll in scrolls" :definition="scroll" :key="scroll.id"/>
|
||||
<VideoScroll
|
||||
v-for="scroll in scrolls"
|
||||
:definition="scroll"
|
||||
:key="scroll.id"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<AudioArea v-for="audio in audioAreas" :definition="audio" :bbox="bbox" :key="audio.id"/>
|
||||
<AudioArea
|
||||
v-for="audio in audioAreas"
|
||||
:definition="audio"
|
||||
:bbox="bbox"
|
||||
:key="audio.id"
|
||||
/>
|
||||
<div class="dev devpanel">
|
||||
<div>
|
||||
<span>Current viewport position:</span>
|
||||
|
@ -382,10 +391,10 @@ export default defineComponent({
|
|||
|
||||
if (gp) {
|
||||
if (gp.buttons[7].pressed) {
|
||||
gamePadZoomSpeed += .1;
|
||||
gamePadZoomSpeed += 0.1;
|
||||
}
|
||||
if (gp.buttons[5].pressed) {
|
||||
gamePadZoomSpeed -= .1;
|
||||
gamePadZoomSpeed -= 0.1;
|
||||
}
|
||||
if (gamePadZoomSpeed < 1) {
|
||||
gamePadZoomSpeed = 1;
|
||||
|
@ -469,11 +478,14 @@ async function processScrolls(svg: XMLDocument): Promise<VideoScrollDef[]> {
|
|||
direction as VideoScrollDirection
|
||||
)
|
||||
) {
|
||||
console.error(`Unknown direction definition: "${direction}" (in #${el.id})`);
|
||||
console.error(
|
||||
`Unknown direction definition: "${direction}" (in #${el.id})`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return direction as VideoScrollDirection;
|
||||
}).filter((d) => Boolean(d)) as VideoScrollDirection[];
|
||||
})
|
||||
.filter((d) => Boolean(d)) as VideoScrollDirection[];
|
||||
|
||||
console.debug(`[SVG/VIDEOSCROLLS] Fetching ${filesURL}...`);
|
||||
const fileFetch = await fetch(`content/${filesURL}`);
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
<template>
|
||||
<div class="video-scroll" ref="root" v-if="definition.directions.length > 0">
|
||||
<img class="visible displayed loaded"
|
||||
<img
|
||||
class="visible displayed loaded"
|
||||
:src="definition.files[0]"
|
||||
:style="{
|
||||
top: `${Math.round(definition.top)}px`,
|
||||
left: `${Math.round(definition.left)}px`,
|
||||
width: isHorizontal ? `${Math.round(definition.width)}px` : 'auto',
|
||||
height: isVertical ? `${Math.round(definition.height)}px` : 'auto',
|
||||
transform: `rotate(${definition.angle}deg)`
|
||||
transform: `rotate(${definition.angle}deg)`,
|
||||
}"
|
||||
/>
|
||||
|
||||
<!--suppress RequiredAttributes -->
|
||||
<img v-for="(file, idx) in dynamicFiles"
|
||||
<img
|
||||
v-for="(file, idx) in dynamicFiles"
|
||||
:key="`${idx}_${file.src}`"
|
||||
:data-src="file.src"
|
||||
:style="{
|
||||
|
@ -20,51 +22,70 @@
|
|||
left: `${Math.round(file.left)}px`,
|
||||
width: `${Math.round(definition.width)}px`,
|
||||
height: `${Math.round(definition.height)}px`,
|
||||
transform: `rotate(${definition.angle}deg)`
|
||||
transform: `rotate(${definition.angle}deg)`,
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent, PropType} from "vue";
|
||||
import {rotate} from "@/utils";
|
||||
import { defineComponent, PropType } from "vue";
|
||||
import { rotate } from "@/utils";
|
||||
|
||||
export default defineComponent({
|
||||
name: "VideoScroll",
|
||||
props: {
|
||||
definition: {
|
||||
type: Object as PropType<VideoScrollDef>,
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
dynamicFiles(): { top: number, left: number, src: string }[] {
|
||||
dynamicFiles(): { top: number; left: number; src: string }[] {
|
||||
return this.definition.files.slice(1).map((src: string, idx: number) => {
|
||||
const cy = this.definition.top +
|
||||
(this.isVertical ? (this.definition.height * (idx + 1) * this.verticalDirection) : 0);
|
||||
const cx = this.definition.left +
|
||||
(this.isHorizontal ? (this.definition.width * (idx + 1) * this.horizontalDirection) : 0);
|
||||
const [left, top] = rotate(cx, cy, this.definition.left, this.definition.top, this.definition.angle);
|
||||
return {top, left, src};
|
||||
const cy =
|
||||
this.definition.top +
|
||||
(this.isVertical
|
||||
? this.definition.height * (idx + 1) * this.verticalDirection
|
||||
: 0);
|
||||
const cx =
|
||||
this.definition.left +
|
||||
(this.isHorizontal
|
||||
? this.definition.width * (idx + 1) * this.horizontalDirection
|
||||
: 0);
|
||||
const [left, top] = rotate(
|
||||
cx,
|
||||
cy,
|
||||
this.definition.left,
|
||||
this.definition.top,
|
||||
this.definition.angle
|
||||
);
|
||||
return { top, left, src };
|
||||
});
|
||||
},
|
||||
isHorizontal(): boolean {
|
||||
return this.definition.directions.some(
|
||||
(dir: VideoScrollDirection) => dir === VideoScrollDirection.LEFT || dir === VideoScrollDirection.RIGHT
|
||||
(dir: VideoScrollDirection) =>
|
||||
dir === VideoScrollDirection.LEFT ||
|
||||
dir === VideoScrollDirection.RIGHT
|
||||
);
|
||||
},
|
||||
isVertical(): boolean {
|
||||
return this.definition.directions.some(
|
||||
(dir: VideoScrollDirection) => dir === VideoScrollDirection.UP || dir === VideoScrollDirection.DOWN
|
||||
(dir: VideoScrollDirection) =>
|
||||
dir === VideoScrollDirection.UP || dir === VideoScrollDirection.DOWN
|
||||
);
|
||||
},
|
||||
horizontalDirection(): number {
|
||||
return this.definition.directions.includes(VideoScrollDirection.RIGHT) ? 1 : -1;
|
||||
return this.definition.directions.includes(VideoScrollDirection.RIGHT)
|
||||
? 1
|
||||
: -1;
|
||||
},
|
||||
verticalDirection(): number {
|
||||
return this.definition.directions.includes(VideoScrollDirection.DOWN) ? 1 : -1;
|
||||
}
|
||||
return this.definition.directions.includes(VideoScrollDirection.DOWN)
|
||||
? 1
|
||||
: -1;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
const observer = new IntersectionObserver((entries, _) => {
|
||||
|
@ -73,7 +94,9 @@ export default defineComponent({
|
|||
if (entry.isIntersecting) {
|
||||
element.classList.add("visible");
|
||||
if (!element.src) {
|
||||
console.debug(`[VIDEOSCROLL] Intersected, loading ${element.dataset.src}`);
|
||||
console.debug(
|
||||
`[VIDEOSCROLL] Intersected, loading ${element.dataset.src}`
|
||||
);
|
||||
element.src = element.dataset.src!;
|
||||
setTimeout(() => {
|
||||
element.classList.add("displayed");
|
||||
|
@ -93,32 +116,31 @@ export default defineComponent({
|
|||
}
|
||||
});
|
||||
});
|
||||
if (this.$refs.root){
|
||||
if (this.$refs.root) {
|
||||
Array.from((this.$refs.root as Element).children).forEach((el) => {
|
||||
observer.observe(el);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export enum VideoScrollDirection {
|
||||
RIGHT = "right",
|
||||
LEFT = "left",
|
||||
UP = "up",
|
||||
DOWN = "down"
|
||||
DOWN = "down",
|
||||
}
|
||||
|
||||
export interface VideoScrollDef {
|
||||
id: string,
|
||||
top: number,
|
||||
left: number,
|
||||
angle: number,
|
||||
width: number,
|
||||
height: number,
|
||||
directions: VideoScrollDirection[],
|
||||
files: string[]
|
||||
id: string;
|
||||
top: number;
|
||||
left: number;
|
||||
angle: number;
|
||||
width: number;
|
||||
height: number;
|
||||
directions: VideoScrollDirection[];
|
||||
files: string[];
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
|
@ -129,7 +151,7 @@ export interface VideoScrollDef {
|
|||
background: grey;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity .5s;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
.video-scroll img.visible {
|
||||
|
|
Loading…
Reference in a new issue