123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433 |
- import { IconMusic } from "@/assets/icons";
- import { isWeixinBrowser } from "@/controllers/wxController";
- import { useEditor } from "@/modules/editor";
- import {
- PauseCircleOutlined,
- PlayCircleOutlined,
- SoundOutlined,
- } from "@ant-design/icons-vue";
- import { css } from "@linaria/core";
- import { Button, Dropdown, Slider } from "ant-design-vue";
- import { Howl } from "howler";
- import { nanoid } from "nanoid";
- import { isPc } from "@queenjs/utils";
- import {
- defineComponent,
- reactive,
- ref,
- watch,
- onUnmounted,
- onMounted,
- } from "vue";
- import { bool, number } from "vue-types";
- import { MusicOptions } from "./localMusic";
- declare const WeixinJSBridge: any;
- export const PageMusic = defineComponent({
- setup() {
- const { store, helper, controls, actions } = useEditor();
- const rootComp = helper.findRootComp();
- const volume =
- rootComp?.value.volume != undefined ? rootComp?.value.volume : 0.3;
- const state = reactive({
- playStatus: false,
- duration: 0,
- currentTime: 0,
- muted: true,
- volume: volume,
- });
- let audioKey = nanoid();
- let audioBgm = ref();
- const initAudioBgm = () => {
- audioBgm.value = null;
- const curAudio = MusicOptions.find((e) => {
- return e.value == rootComp?.value.music;
- });
- const src = curAudio?.src || "";
- audioBgm.value = new Howl({
- src: [src],
- loop: store.isEditMode ? false : true,
- preload: true,
- HTML5: true,
- volume: volume,
- });
- controls.mediaCtrl.setMediasInstance(audioKey, audioBgm.value);
- audioBgm.value.on("load", () => {
- state.duration = audioBgm.value.duration();
- if (!store.isEditMode) {
- if (isWeixinBrowser()) {
- WeixinJSBridge.invoke(
- "getNetworkType",
- {},
- () => {
- playAudio(true);
- },
- false
- );
- } else {
- playAudio(true);
- }
- }
- });
- audioBgm.value.on("loaderror", () => {
- console.log("音频加载失败");
- });
- audioBgm.value.on("playerror", () => {
- console.log("音频播放失败");
- });
- audioBgm.value.on("play", () => {
- controls.mediaCtrl.pauseOtherMedia(audioKey);
- if (!state.playStatus) {
- state.playStatus = true;
- }
- playStep();
- });
- audioBgm.value.on("pause", () => {
- audioRest();
- });
- audioBgm.value.on("end", () => {
- if (store.isEditMode) {
- audioRest();
- }
- });
- setTimeout(() => {
- checkAutoPlay();
- }, 500);
- };
- const checkAutoPlay = () => {
- const duration = audioBgm.value.duration();
- if (duration) {
- state.duration = duration;
- }
- if (!audioBgm.value || store.isEditMode) {
- return;
- }
- let playing = audioBgm.value.playing();
- if (!playing) {
- playAudio(true);
- }
- };
- 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) {
- audioBgm.value.play();
- } else {
- audioRest();
- }
- let playing = audioBgm.value.playing();
- if (status && playing) {
- state.playStatus = true;
- return;
- }
- state.playStatus = false;
- };
- watch(
- () => rootComp?.value.music,
- () => {
- audioRest();
- initAudioBgm();
- }
- );
- onMounted(() => {
- initAudioBgm();
- });
- onUnmounted(() => {
- audioRest();
- audioBgm.value = null;
- controls.mediaCtrl.removeMedia(audioKey);
- });
- const seekChange = (v: number) => {
- state.currentTime = v;
- audioBgm.value && audioBgm.value.seek(v);
- };
- const volumeChange = (v: number) => {
- state.volume = v;
- if (rootComp) {
- actions.updateCompData(rootComp, "value.volume", v);
- }
- audioBgm.value && audioBgm.value.volume(v);
- };
- const audioRest = () => {
- if (!audioBgm.value) {
- return;
- }
- let playing = audioBgm.value.playing();
- if (playing) {
- audioBgm.value.pause();
- }
- state.playStatus = false;
- state.currentTime = 0;
- audioBgm.value.seek(0);
- };
- return () => {
- const music = rootComp?.value.music;
- return (
- <div
- class={[
- store.isEditMode ? MusicEditStyle : MusicStyle,
- isPc() ? "absolute" : "fixed",
- ]}
- >
- {store.isEditMode ? (
- <AudioPlayer
- key={music}
- playStatus={state.playStatus}
- volume={state.volume}
- onStatus={playAudio}
- onSeekChange={seekChange}
- onVolumeChange={volumeChange}
- duration={state.duration}
- currentTime={state.currentTime}
- />
- ) : (
- <div
- class={["music_button", state.playStatus ? "rotating" : ""]}
- onClick={() => {
- playAudio(!state.playStatus);
- }}
- >
- <IconMusic />
- </div>
- )}
- </div>
- );
- };
- },
- });
- const AudioPlayer = defineComponent({
- props: {
- volume: number(),
- playStatus: bool(),
- currentTime: number(),
- duration: number(),
- },
- emits: ["status", "seekChange", "volumeChange"],
- setup(props, { emit }) {
- const audioControl = (playStatus: boolean) => {
- emit("status", playStatus);
- };
- const seekChange = (v: any) => {
- emit("seekChange", v);
- };
- const volumeChange = (v: any) => {
- emit("volumeChange", v);
- };
- const formatTime = (secs?: number) => {
- if (!secs) {
- return "00:00";
- }
- secs = Math.round(secs);
- const minutes = Math.floor(secs / 60) || 0;
- const seconds = secs - minutes * 60 || 0;
- return (
- String(minutes).padStart(2, "0") +
- ":" +
- String(seconds).padStart(2, "0")
- );
- };
- const volumeSlider = () => {
- return (
- <div class={[VolumeSliderStyle]}>
- <div class={"h-100px pb-8px"}>
- <Slider
- tooltipVisible={false}
- min={0}
- max={1}
- step={0.1}
- vertical={true}
- value={props.volume}
- onChange={volumeChange}
- ></Slider>
- </div>
- </div>
- );
- };
- return () => {
- return (
- <div class={AudioPlayerStyle}>
- {!props.playStatus ? (
- <Button
- type="link"
- icon={<PlayCircleOutlined style={{ fontSize: "24px" }} />}
- onClick={() => audioControl(true)}
- ></Button>
- ) : (
- <Button
- type="link"
- icon={<PauseCircleOutlined style={{ fontSize: "24px" }} />}
- onClick={() => audioControl(false)}
- ></Button>
- )}
- <div class={"flex-1 px-10px"}>
- <Slider
- disabled={!props.playStatus}
- tooltipVisible={false}
- min={0}
- max={Math.floor(props.duration || 0)}
- value={props.currentTime}
- onChange={seekChange}
- ></Slider>
- </div>
- <div>
- {formatTime(props.currentTime)}/{formatTime(props.duration)}
- </div>
- <div>
- <Dropdown
- disabled={!props.playStatus}
- overlay={volumeSlider()}
- trigger="click"
- placement="top"
- >
- <Button
- type="link"
- icon={<SoundOutlined style={{ fontSize: "18px" }} />}
- ></Button>
- </Dropdown>
- </div>
- </div>
- );
- };
- },
- });
- const VolumeSliderStyle = css`
- margin-bottom: -4px;
- padding: 8px 4px;
- background-color: #303030;
- border-radius: 4px;
- box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.2);
- /* slider style */
- .ant-slider-disabled {
- opacity: 0.7;
- }
- .ant-slider-step {
- background-color: rgba(255, 255, 255, 0.27);
- }
- .ant-slider-track {
- border-radius: 4px;
- background-color: rgba(255, 255, 255, 1);
- }
- .ant-slider:not(.ant-slider-disabled):hover {
- .ant-slider-handle {
- background-color: @inf-primary-color;
- &:not(.ant-tooltip-open) {
- border-color: #fff;
- }
- }
- }
- .ant-slider {
- &.ant-slider-disabled {
- .ant-slider-handle {
- background-color: #bbb;
- opacity: 0.8;
- }
- }
- }
- .ant-slider-handle {
- width: 14px;
- height: 8px;
- border-radius: 2px;
- border-color: #fff;
- background-color: #fff;
- }
- .ant-slider-handle-click-focused {
- border-color: #fff;
- background-color: @inf-primary-color;
- }
- `;
- const MusicEditStyle = css`
- flex: 1;
- `;
- const AudioPlayerStyle = css`
- width: 100%;
- display: flex;
- align-items: center;
- /* slider style */
- .ant-slider-disabled {
- opacity: 0.7;
- }
- .ant-slider-step {
- background-color: rgba(255, 255, 255, 0.27);
- }
- .ant-slider-track {
- border-radius: 4px;
- background-color: rgba(255, 255, 255, 1);
- }
- .ant-slider:not(.ant-slider-disabled):hover {
- .ant-slider-handle {
- background-color: @inf-primary-color;
- &:not(.ant-tooltip-open) {
- border-color: #fff;
- }
- }
- }
- .ant-slider {
- &.ant-slider-disabled {
- .ant-slider-handle {
- background-color: #bbb;
- opacity: 0.8;
- }
- }
- }
- .ant-slider-handle {
- width: 8px;
- border-radius: 2px;
- border-color: #fff;
- background-color: #fff;
- }
- .ant-slider-handle-click-focused {
- border-color: #fff;
- background-color: @inf-primary-color;
- }
- `;
- const MusicStyle = css`
- top: 10px;
- right: 10px;
- z-index: 999;
- .music_button {
- width: 48px;
- height: 48px;
- display: inline-flex;
- justify-content: center;
- align-items: center;
- background-color: rgba(0, 0, 0, 0.5);
- font-size: 28px;
- border-radius: 24px;
- cursor: pointer;
- &.rotating {
- animation: myRotate 5s linear infinite;
- }
- }
- @keyframes myRotate {
- 0% {
- transform: rotate(0);
- }
- 100% {
- transform: rotate(360deg);
- }
- }
- `;
|