|
@@ -1,13 +1,20 @@
|
|
|
+import { IconMusic } from "@/assets/icons";
|
|
|
+import { isWeixinBrowser } from "@/controllers/wxController";
|
|
|
import { useEditor } from "@/modules/editor";
|
|
|
-import { PauseCircleOutlined, PlayCircleOutlined } from "@ant-design/icons-vue";
|
|
|
+import {
|
|
|
+ PauseCircleOutlined,
|
|
|
+ PlayCircleOutlined,
|
|
|
+ LoadingOutlined,
|
|
|
+} from "@ant-design/icons-vue";
|
|
|
import { css } from "@linaria/core";
|
|
|
-import { Button, Slider } from "ant-design-vue";
|
|
|
-import { defineComponent, onMounted, reactive, ref, watch } from "vue";
|
|
|
-import { number, bool } from "vue-types";
|
|
|
-import { IconMusic } from "@/assets/icons";
|
|
|
+import { Button, Slider, Spin } from "ant-design-vue";
|
|
|
+import { Howl } from "howler";
|
|
|
+import { defineComponent, reactive, ref, watch } from "vue";
|
|
|
+import { bool, number } from "vue-types";
|
|
|
+declare const WeixinJSBridge: any;
|
|
|
export const PageMusic = defineComponent({
|
|
|
- setup(props) {
|
|
|
- const { store, actions, helper, controls } = useEditor();
|
|
|
+ setup() {
|
|
|
+ const { store, helper } = useEditor();
|
|
|
const state = reactive({
|
|
|
playStatus: false,
|
|
|
duration: 0,
|
|
@@ -15,48 +22,101 @@ export const PageMusic = defineComponent({
|
|
|
muted: true,
|
|
|
});
|
|
|
const rootComp = helper.findRootComp();
|
|
|
+ let audioBgm = ref();
|
|
|
|
|
|
- const audioRef = ref();
|
|
|
+ const initAudioBgm = () => {
|
|
|
+ audioBgm.value = null;
|
|
|
+ audioBgm.value = new Howl({
|
|
|
+ src: [rootComp?.value.music],
|
|
|
+ loop: store.isEditMode ? false : true,
|
|
|
+ preload: true,
|
|
|
+ HTML5: true,
|
|
|
+ autoplay: store.isEditMode ? false : true,
|
|
|
+ });
|
|
|
+ audioBgm.value.on("load", () => {
|
|
|
+ state.duration = audioBgm.value.duration();
|
|
|
+ if (!store.isEditMode) {
|
|
|
+ if (isWeixinBrowser()) {
|
|
|
+ WeixinJSBridge.invoke(
|
|
|
+ "getNetworkType",
|
|
|
+ {},
|
|
|
+ () => {
|
|
|
+ playAudio(true);
|
|
|
+ },
|
|
|
+ false
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ audioBgm.value.on("loaderror", () => {
|
|
|
+ console.log("音频加载失败");
|
|
|
+ });
|
|
|
+ audioBgm.value.on("playerror", () => {
|
|
|
+ console.log("音频播放失败");
|
|
|
+ });
|
|
|
+ audioBgm.value.on("play", () => {
|
|
|
+ if (!state.playStatus) {
|
|
|
+ state.playStatus = true;
|
|
|
+ }
|
|
|
+ playStep();
|
|
|
+ });
|
|
|
+ audioBgm.value.on("end", () => {
|
|
|
+ audioRest();
|
|
|
+ });
|
|
|
+ };
|
|
|
+ initAudioBgm();
|
|
|
+ const playStep = () => {
|
|
|
+ if (!audioBgm.value) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ let playing = audioBgm.value.playing();
|
|
|
+ if (!playing) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ let seek = audioBgm.value.seek();
|
|
|
+ state.currentTime = seek;
|
|
|
+ requestAnimationFrame(playStep);
|
|
|
+ };
|
|
|
|
|
|
const playAudio = async (status: boolean) => {
|
|
|
+ if (!audioBgm.value) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
if (status) {
|
|
|
- audioRef.value.play();
|
|
|
+ audioBgm.value.play();
|
|
|
} else {
|
|
|
- audioRef.value.pause();
|
|
|
audioRest();
|
|
|
}
|
|
|
- state.playStatus = status;
|
|
|
+ let playing = audioBgm.value.playing();
|
|
|
+ if (status && playing) {
|
|
|
+ state.playStatus = true;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ state.playStatus = false;
|
|
|
};
|
|
|
watch(
|
|
|
() => rootComp?.value.music,
|
|
|
() => {
|
|
|
audioRest();
|
|
|
+ initAudioBgm();
|
|
|
}
|
|
|
);
|
|
|
+ const seekChange = (v: number) => {
|
|
|
+ state.currentTime = v;
|
|
|
+ audioBgm.value && audioBgm.value.seek(v);
|
|
|
+ };
|
|
|
const audioRest = () => {
|
|
|
+ if (!audioBgm.value) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ let playing = audioBgm.value.playing();
|
|
|
+ if (playing) {
|
|
|
+ audioBgm.value.pause();
|
|
|
+ }
|
|
|
state.playStatus = false;
|
|
|
- audioRef.value.currentTime = 0;
|
|
|
+ state.currentTime = 0;
|
|
|
+ audioBgm.value.seek(0);
|
|
|
};
|
|
|
- onMounted(() => {
|
|
|
- setTimeout(() => {
|
|
|
- if (!store.isEditMode && !state.playStatus) {
|
|
|
- document.body.addEventListener(
|
|
|
- "touchend",
|
|
|
- () => {
|
|
|
- playAudio(true);
|
|
|
- },
|
|
|
- {
|
|
|
- once: true,
|
|
|
- }
|
|
|
- );
|
|
|
- }
|
|
|
- }, 500);
|
|
|
- });
|
|
|
- const timeChange = (v: number) => {
|
|
|
- state.currentTime = v;
|
|
|
- audioRef.value.currentTime = v;
|
|
|
- };
|
|
|
-
|
|
|
return () => {
|
|
|
const music = rootComp?.value.music;
|
|
|
return (
|
|
@@ -66,7 +126,7 @@ export const PageMusic = defineComponent({
|
|
|
key={music}
|
|
|
playStatus={state.playStatus}
|
|
|
onStatus={playAudio}
|
|
|
- onDuration={timeChange}
|
|
|
+ onSeekChange={seekChange}
|
|
|
duration={state.duration}
|
|
|
currentTime={state.currentTime}
|
|
|
/>
|
|
@@ -80,32 +140,6 @@ export const PageMusic = defineComponent({
|
|
|
<IconMusic />
|
|
|
</div>
|
|
|
)}
|
|
|
- <audio
|
|
|
- controls={false}
|
|
|
- ref={audioRef}
|
|
|
- key={music}
|
|
|
- loop={store.isEditMode ? false : true}
|
|
|
- autoplay={store.isEditMode ? false : true}
|
|
|
- onPlay={() => {
|
|
|
- if (!state.playStatus) {
|
|
|
- state.playStatus = true;
|
|
|
- }
|
|
|
- }}
|
|
|
- onLoadedmetadata={(e: any) => {
|
|
|
- state.duration = e.target?.duration;
|
|
|
- }}
|
|
|
- onTimeupdate={(e: any) => {
|
|
|
- state.currentTime = e.target.currentTime;
|
|
|
- }}
|
|
|
- onEnded={() => {
|
|
|
- if (store.isEditMode) {
|
|
|
- audioRest();
|
|
|
- }
|
|
|
- }}
|
|
|
- >
|
|
|
- <source src={music} type="audio/mpeg" />
|
|
|
- <p>你的浏览器不支持音频播放</p>
|
|
|
- </audio>
|
|
|
</div>
|
|
|
);
|
|
|
};
|
|
@@ -118,42 +152,28 @@ const AudioPlayer = defineComponent({
|
|
|
currentTime: number(),
|
|
|
duration: number(),
|
|
|
},
|
|
|
- emits: ["status", "duration"],
|
|
|
+ emits: ["status", "seekChange"],
|
|
|
setup(props, { emit }) {
|
|
|
const audioControl = (playStatus: boolean) => {
|
|
|
emit("status", playStatus);
|
|
|
};
|
|
|
- const durationChange = (v: any) => {
|
|
|
- emit("duration", v);
|
|
|
+ const seekChange = (v: any) => {
|
|
|
+ emit("seekChange", v);
|
|
|
};
|
|
|
- const transTime = (value?: number) => {
|
|
|
- let time = "";
|
|
|
- if (!value) {
|
|
|
+ const formatTime = (secs?: number) => {
|
|
|
+ if (!secs) {
|
|
|
return "00:00";
|
|
|
}
|
|
|
- const h = parseInt(`${value / 3600}`);
|
|
|
- value %= 3600;
|
|
|
- let m = parseInt(`${value / 60}`);
|
|
|
- let s = parseInt(`${value % 60}`);
|
|
|
- if (h > 0) {
|
|
|
- time = formatTime(h + ":" + m + ":" + s);
|
|
|
- } else {
|
|
|
- time = formatTime(m + ":" + s);
|
|
|
- }
|
|
|
+ secs = Math.round(secs);
|
|
|
|
|
|
- return time;
|
|
|
- };
|
|
|
- const formatTime = (value: string) => {
|
|
|
- let time = "";
|
|
|
- const s = value.split(":");
|
|
|
- let i = 0;
|
|
|
- for (; i < s.length - 1; i++) {
|
|
|
- time += s[i].length === 1 ? "0" + s[i] : s[i];
|
|
|
- time += ":";
|
|
|
- }
|
|
|
- time += s[i].length === 1 ? "0" + s[i] : s[i];
|
|
|
+ const minutes = Math.floor(secs / 60) || 0;
|
|
|
+ const seconds = secs - minutes * 60 || 0;
|
|
|
|
|
|
- return time;
|
|
|
+ return (
|
|
|
+ String(minutes).padStart(2, "0") +
|
|
|
+ ":" +
|
|
|
+ String(seconds).padStart(2, "0")
|
|
|
+ );
|
|
|
};
|
|
|
return () => {
|
|
|
return (
|
|
@@ -178,11 +198,11 @@ const AudioPlayer = defineComponent({
|
|
|
min={0}
|
|
|
max={Math.floor(props.duration || 0)}
|
|
|
value={props.currentTime}
|
|
|
- onChange={durationChange}
|
|
|
+ onChange={seekChange}
|
|
|
></Slider>
|
|
|
</div>
|
|
|
<div>
|
|
|
- {transTime(props.currentTime)}/{transTime(props.duration)}
|
|
|
+ {formatTime(props.currentTime)}/{formatTime(props.duration)}
|
|
|
</div>
|
|
|
</div>
|
|
|
);
|