add audio areas

This commit is contained in:
Tomáš Mládek 2021-01-10 21:55:40 +01:00
parent 7c0d83b8ed
commit 631ba25f9f
2 changed files with 111 additions and 3 deletions

View file

@ -0,0 +1,63 @@
<template>
<audio ref="audio"
:src="definition.src"
loop/>
</template>
<script lang="ts">
import {defineComponent, ref, watch} from "vue";
export default defineComponent({
name: "AudioArea",
props: {
definition: {
type: Object,
required: true
},
bbox: {
type: Object,
required: true
},
},
setup(props) {
const audio = ref<HTMLAudioElement | null>(null);
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));
if (distance < props.definition.radius) {
audio.value!.play();
audio.value!.volume = (props.definition.radius - distance) / props.definition.radius;
} else {
audio.value!.pause();
}
};
watch(props.bbox, onBBoxChange, {deep: true});
return {
audio
};
}
});
export interface AudioAreaDef {
cx: number,
cy: number,
radius: number,
src: string
}
export interface BoundingBox {
x: number,
y: number,
w: number,
h: number
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

View file

@ -3,17 +3,19 @@
<div class="video-scrolls">
<VideoScroll v-for="scroll in scrolls" :definition="scroll"/>
</div>
<AudioArea v-for="audio in audioAreas" :definition="audio" :bbox="bbox"/>
</div>
</template>
<script lang="ts">
import {defineComponent, onMounted, ref} from "vue";
import {defineComponent, onMounted, reactive, ref} from "vue";
import createPanZoom, {PanZoom} from "panzoom";
import VideoScroll, {VideoScrollDef, VideoScrollDirection} from "@/components/VideoScroll.vue";
import AudioArea, {AudioAreaDef} from "@/components/AudioArea.vue";
export default defineComponent({
name: "SVGContent",
components: {VideoScroll},
components: {AudioArea, VideoScroll},
props: {
url: {
type: String,
@ -26,6 +28,13 @@ export default defineComponent({
const anchors = ref<SVGRectElement[]>([]);
const scrolls = ref<VideoScrollDef[]>([]);
const panToAnchor = ref();
const audioAreas = ref<AudioAreaDef[]>([]);
const bbox = reactive({
x: ref(0),
y: ref(0),
w: ref(0),
h: ref(0)
});
onMounted(async () => {
const element = root.value as unknown as HTMLDivElement;
@ -56,6 +65,16 @@ export default defineComponent({
});
panzoom.value = pz;
pz.on("transform", function (_) {
const transform = pz.getTransform();
const currentRatio = svg.clientWidth * transform.scale / svg.viewBox.baseVal.width;
bbox.x = transform.x / currentRatio * -1;
bbox.y = transform.y / currentRatio * -1;
bbox.w = window.innerWidth / currentRatio;
bbox.h = window.innerHeight / currentRatio;
});
// Edge scrolling
const MOVE_EDGE = 75;
const MAX_SPEED = 20;
@ -127,6 +146,9 @@ export default defineComponent({
// Videoscrolls
scrolls.value = await processScrolls(svg);
// Audio areas
audioAreas.value = processAudio(svg);
});
return {
@ -134,7 +156,9 @@ export default defineComponent({
panzoom,
anchors,
panToAnchor,
scrolls
scrolls,
audioAreas,
bbox
};
},
});
@ -184,10 +208,31 @@ async function processScrolls(svg: XMLDocument): Promise<VideoScrollDef[]> {
);
}
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("svgcontent_audio");
const descNode = el.children[0]; // to fix
const audioSrc = descNode.textContent!.trim();
return {
cx: el.cx.baseVal.value,
cy: el.cy.baseVal.value,
radius: el.r.baseVal.value,
src: `content/${audioSrc}`,
};
});
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
/*.svgcontent_audio*/
.svgcontent_anchor {
visibility: hidden;
}