|
@@ -2,9 +2,11 @@
|
|
|
import ProjectsContent from '@/api/inherit/ProjectsContent';
|
|
|
import SimplePageContentLoader from '@/components/SimplePageContentLoader.vue';
|
|
|
import SimpleRichHtml from '@/components/SimpleRichHtml.vue';
|
|
|
+import ActiveableButton from '@/components/small/ActiveableButton.vue';
|
|
|
import Box1 from '@/components/small/Box1.vue';
|
|
|
import { useSimpleDataLoader } from '@/composeable/SimpleDataLoader';
|
|
|
import { ScrollRect } from '@imengyu/vue-scroll-rect';
|
|
|
+import { ref } from 'vue';
|
|
|
import { useRouter, useRoute } from 'vue-router';
|
|
|
|
|
|
const route = useRoute();
|
|
@@ -12,6 +14,42 @@ const router = useRouter();
|
|
|
const data = useSimpleDataLoader(async () =>
|
|
|
await ProjectsContent.getContentDetail(parseInt(route.query.id as string))
|
|
|
);
|
|
|
+
|
|
|
+const video = ref<HTMLVideoElement>();
|
|
|
+const leftAudio = ref(true);
|
|
|
+let source: MediaElementAudioSourceNode;
|
|
|
+let leftGain: GainNode, rightGain: GainNode;
|
|
|
+
|
|
|
+function handleLoaded() {
|
|
|
+ if (source)
|
|
|
+ return;
|
|
|
+
|
|
|
+ const audioContext = new AudioContext();
|
|
|
+ source = audioContext.createMediaElementSource(video.value!);
|
|
|
+ const splitter = audioContext.createChannelSplitter(2); // 分离左右声道
|
|
|
+ leftGain = audioContext.createGain();
|
|
|
+ rightGain = audioContext.createGain();
|
|
|
+ const merger = audioContext.createChannelMerger(2); // 合并声道
|
|
|
+
|
|
|
+ // 连接节点
|
|
|
+ source.connect(splitter);
|
|
|
+ splitter.connect(leftGain, 0); // 左声道
|
|
|
+ splitter.connect(rightGain, 1); // 右声道
|
|
|
+ leftGain.connect(merger, 0, 0);
|
|
|
+ rightGain.connect(merger, 0, 1);
|
|
|
+ merger.connect(audioContext.destination);
|
|
|
+}
|
|
|
+function switchAudio(left: boolean) {
|
|
|
+ leftAudio.value = left;
|
|
|
+ if (left) {
|
|
|
+ leftGain.gain.value = 1;
|
|
|
+ rightGain.gain.value = 0;
|
|
|
+ } else {
|
|
|
+ leftGain.gain.value = 0;
|
|
|
+ rightGain.gain.value = 1;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
@@ -26,7 +64,17 @@ const data = useSimpleDataLoader(async () =>
|
|
|
<div class="d-flex flex-row align-stretch flex-six margin-left-ll w-55 h-100">
|
|
|
<SimplePageContentLoader :loader="data">
|
|
|
<div class="d-flex flex-col align-stretch flex-six flex-shrink-1">
|
|
|
- <video :src="data.content.value?.video" class="w-100 h-100" :controls="true" :autoplay="true" />
|
|
|
+ <video
|
|
|
+ ref="video"
|
|
|
+ crossorigin="anonymous"
|
|
|
+ :src="data.content.value?.video"
|
|
|
+ class="w-100 h-100"
|
|
|
+ :controls="true"
|
|
|
+ :autoplay="true"
|
|
|
+ webkit-playsinline
|
|
|
+ playsinline
|
|
|
+ @play="handleLoaded"
|
|
|
+ />
|
|
|
</div>
|
|
|
<div class="d-flex flex-col align-stretch flex-four margin-left-base flex-shrink-1 w-30 gap-l">
|
|
|
<Box1 class="desc h-50">
|
|
@@ -37,6 +85,10 @@ const data = useSimpleDataLoader(async () =>
|
|
|
img: 'max-width: 100%'
|
|
|
}" />
|
|
|
</ScrollRect>
|
|
|
+ <div class="d-flex flex-row align-center gap-s">
|
|
|
+ <ActiveableButton text="原唱" :active="leftAudio" @click="switchAudio(true)" />
|
|
|
+ <ActiveableButton text="伴奏" :active="!leftAudio" @click="switchAudio(false)" />
|
|
|
+ </div>
|
|
|
</Box1>
|
|
|
|
|
|
<Box1 class="desc h-45">
|