Procházet zdrojové kódy

Merge branch 'dev' of http://124.70.149.18:10880/lianghj/queenshow into dev

lianghongjie před 1 rokem
rodič
revize
e21e844f25

+ 1 - 0
package.json

@@ -54,6 +54,7 @@
     "dom-to-image": "^2.6.0",
     "file-saver": "^2.0.5",
     "hotkeys-js": "^3.10.2",
+    "howler": "^2.2.3",
     "jszip": "^3.10.1",
     "load-asset": "^1.2.0",
     "lodash": "^4.17.21",

+ 29 - 0
src/assets/icons/components/IconAi.tsx

@@ -0,0 +1,29 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconAi = createIcon(<svg viewBox="0 0 21.916 21.916">
+    <g transform="translate(-285.792 -250.411)">
+        <g transform="translate(286.492 251.111)">
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M10.594,6H6.919A.919.919,0,0,0,6,6.92V10.6"
+                transform="translate(-6 -6)" />
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M10.594,36.594H6.919A.919.919,0,0,1,6,35.675V32"
+                transform="translate(-6 -16.078)" />
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M32,36.594h3.675a.919.919,0,0,0,.919-.919V32"
+                transform="translate(-16.078 -16.078)" />
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M32,6h3.675a.919.919,0,0,1,.919.92V10.6"
+                transform="translate(-16.079 -6)" />
+        </g>
+        <g transform="translate(291.432 256.749)">
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M4471.314,754.778l3.792-8.764,3.857,8.764"
+                transform="translate(-4471.314 -746.015)" />
+            <path fill="none" stroke="#fff" stroke-width="1.4px"
+                d="M4473.049,754.619h5.048" transform="translate(-4471.853 -748.683)" />
+            <path stroke-linecap="round" fill="none" stroke="#fff" stroke-width="1.4px"
+                d="M4486.73,745.231v8.84" transform="translate(-4476.095 -745.231)" />
+        </g>
+    </g>
+</svg>)

+ 1 - 0
src/assets/icons/index.ts

@@ -1,6 +1,7 @@
 
 export * from "./components/Icon3D";
 export * from "./components/IconAdd";
+export * from "./components/IconAi";
 export * from "./components/IconAlignC";
 export * from "./components/IconAlignL";
 export * from "./components/IconAlignR";

+ 27 - 0
src/assets/icons/svg/ai.svg

@@ -0,0 +1,27 @@
+<svg viewBox="0 0 21.916 21.916">
+    <g transform="translate(-285.792 -250.411)">
+        <g transform="translate(286.492 251.111)">
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M10.594,6H6.919A.919.919,0,0,0,6,6.92V10.6"
+                transform="translate(-6 -6)" />
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M10.594,36.594H6.919A.919.919,0,0,1,6,35.675V32"
+                transform="translate(-6 -16.078)" />
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M32,36.594h3.675a.919.919,0,0,0,.919-.919V32"
+                transform="translate(-16.078 -16.078)" />
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M32,6h3.675a.919.919,0,0,1,.919.92V10.6"
+                transform="translate(-16.079 -6)" />
+        </g>
+        <g transform="translate(291.432 256.749)">
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M4471.314,754.778l3.792-8.764,3.857,8.764"
+                transform="translate(-4471.314 -746.015)" />
+            <path fill="none" stroke="#fff" stroke-width="1.4px"
+                d="M4473.049,754.619h5.048" transform="translate(-4471.853 -748.683)" />
+            <path stroke-linecap="round" fill="none" stroke="#fff" stroke-width="1.4px"
+                d="M4486.73,745.231v8.84" transform="translate(-4476.095 -745.231)" />
+        </g>
+    </g>
+</svg>

+ 108 - 88
src/modules/editor/components/CompUI/basicUI/Page/PageMusic.tsx

@@ -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>
       );

+ 5 - 0
src/modules/editor/components/TipIcons/index.ts

@@ -8,6 +8,7 @@ import {
   IconLayerDown,
   IconLayerUp,
   IconQueen,
+  IconAi,
 } from "@/assets/icons";
 import {
   IconCamera,
@@ -33,6 +34,10 @@ export const TipIcons = {
     icons: [IconCamera],
     tips: ["截屏并保存封面"],
   }),
+  AiText: createTipIcon({
+    icons: [IconAi],
+    tips: ["Ai文案"],
+  }),
   Align: createTipIcon({
     icons: [IconAlignL, IconAlignC, IconAlignR],
     tips: ["左对齐", "居中", "右对齐"],

+ 7 - 1
src/modules/editor/components/Viewport/Toolbar/index.tsx

@@ -23,7 +23,13 @@ export default defineUI({
             onClick={() => history.redo()}
           />
         </div>
-        <div class="absolute top-20px right-20px z-999">
+        <div class="absolute top-20px right-20px space-x-10px z-999">
+          <TipIcons.AiText
+            class={btnCls}
+            onClick={() => {
+              console.log(1);
+            }}
+          />
           <TipIcons.Screenshot
             class={btnCls}
             onClick={() => actions.updateThumbnailByScreenshot(true)}

+ 1 - 0
src/typings/pro.d.ts

@@ -21,3 +21,4 @@ declare module "vue-dndrop" {
   export const vueDndrop: any;
 }
 declare module "ckeditor5-line-height-latest/src/lineheight";
+declare module 'howler'

+ 5 - 0
yarn.lock

@@ -5162,6 +5162,11 @@ hotkeys-js@^3.10.2:
   resolved "http://124.70.149.18:4873/hotkeys-js/-/hotkeys-js-3.10.2.tgz#cf52661904f5a13a973565cb97085fea2f5ae257"
   integrity sha512-Z6vLmJTYzkbZZXlBkhrYB962Q/rZGc/WHQiyEGu9ZZVF7bAeFDjjDa31grWREuw9Ygb4zmlov2bTkPYqj0aFnQ==
 
+howler@^2.2.3:
+  version "2.2.3"
+  resolved "http://124.70.149.18:4873/howler/-/howler-2.2.3.tgz#a2eff9b08b586798e7a2ee17a602a90df28715da"
+  integrity sha512-QM0FFkw0LRX1PR8pNzJVAY25JhIWvbKMBFM4gqk+QdV+kPXOhleWGCB6AiAF/goGjIHK2e/nIElplvjQwhr0jg==
+
 hpack.js@^2.1.6:
   version "2.1.6"
   resolved "http://124.70.149.18:4873/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2"