lex hai 1 ano
pai
achega
b76805cda3

+ 2 - 0
src/views/tempo-practice/index.module.less

@@ -47,6 +47,8 @@
   flex: 1 auto;
   flex-wrap: wrap;
   gap: 22px 0;
+  max-width: 900px;
+  margin: 0 auto;
 }
 
 .beatSection {

+ 16 - 11
src/views/tempo-practice/index.tsx

@@ -17,13 +17,13 @@ import j1 from './images/music/j-1.png';
 import { Popup } from 'vant';
 import SettingModal from './setting-modal';
 import { randomScoreElement, renderScore, setting } from './setting';
+import { handleStartTick, hendleEndTick } from './tick';
 
 export default defineComponent({
   name: 'tempo-practice',
   setup() {
     const state = reactive({
       settingStatus: false,
-      playState: 'pause' as 'pause' | 'play',
       playType: 'beat' as 'beat' | 'tempo'
     });
     // 返回
@@ -32,11 +32,14 @@ export default defineComponent({
     };
 
     /** 播放切换 */
-    const handlePlay = () => {
-      if (state.playState === 'pause') {
-        state.playState = 'play';
+    const handlePlay = async () => {
+      if (setting.playState === 'pause') {
+        setting.playState = 'play';
+        const a = await handleStartTick();
+        console.log(a, 'play');
       } else {
-        state.playState = 'pause';
+        setting.playState = 'pause';
+        hendleEndTick();
       }
     };
     /** 播放类型 */
@@ -70,9 +73,11 @@ export default defineComponent({
             <div
               class={[
                 styles.beatSection,
-                styles.small,
-                // styles.minLength,
-                styles.maxLength
+                setting.scorePart.length >= 2 && styles.small,
+                ((item.length <= 2 && item.length > 4) ||
+                  (item.length == 2 && setting.scorePart.length == 4)) &&
+                  styles.minLength,
+                item.length <= 4 && item.length != 2 && styles.maxLength
               ]}>
               {item.map((child: any) => (
                 // , styles.active
@@ -94,7 +99,7 @@ export default defineComponent({
         <div class={styles.footer}>
           {/* 播放 */}
           <div class={styles.play} onClick={handlePlay}>
-            {state.playState === 'pause' ? (
+            {setting.playState === 'pause' ? (
               <img src={iconPause} />
             ) : (
               <img src={iconPlay} />
@@ -109,13 +114,13 @@ export default defineComponent({
             )}
           </div>
           {/* 随机生成 */}
-          <div class={styles.randomTempo}>
+          <div class={styles.randomTempo} onClick={() => renderScore()}>
             <img src={randDom} />
           </div>
           {/* 速度 */}
           <div class={styles.speedChange}>
             <img src={iconPlus} class={styles.speedPlus} />
-            <div class={styles.speedNum}>90</div>
+            <div class={styles.speedNum}>{setting.speed}</div>
             <img src={iconAdd} class={styles.speedAdd} />
           </div>
         </div>

+ 2 - 0
src/views/tempo-practice/setting-modal/index.tsx

@@ -5,6 +5,7 @@ import {
   barLineList,
   beatList,
   elementList,
+  renderScore,
   setting,
   tempo4,
   tempo8
@@ -46,6 +47,7 @@ export default defineComponent({
       setting.barLine = state.barLine;
       setting.tempo = state.tempo;
 
+      renderScore();
       emit('close');
     };
 

+ 7 - 3
src/views/tempo-practice/setting.ts

@@ -1,11 +1,14 @@
 import { reactive } from 'vue';
+import { handleInitTick } from './tick';
 
 export const setting = reactive({
   element: 'jianpu' as 'jianpu' | 'staff', // 元素
   beat: '4-4' as '4-2' | '4-3' | '4-4' | '8-3' | '8-6', // 拍号
   barLine: '1' as '1' | '2' | '4', // 小节数
   tempo: ['1', '2', '3'] as any[], // 节奏形筛选
-  scorePart: [] as any // 生成谱面
+  scorePart: [] as any, // 生成谱面
+  playState: 'pause' as 'pause' | 'play',
+  speed: 60 // 默认速度
 });
 
 /** 元素 */
@@ -88,7 +91,8 @@ export const renderScore = () => {
       };
     }
   }
-
   setting.scorePart = tempBeat;
-  console.log(tempBeat, 'tempBeat');
+
+  const beatLengthInMilliseconds = (60 / setting.speed) * 1000;
+  handleInitTick(beatLengthInMilliseconds, Number(beat) || 4);
 };

+ 106 - 0
src/views/tempo-practice/tick.ts

@@ -0,0 +1,106 @@
+import { reactive } from 'vue';
+import tockAndTick from './tockAndTick.json';
+import { Howl } from 'howler';
+import { setting } from './setting';
+
+const tickData = reactive({
+  list: [] as number[],
+  len: 0,
+  tickEnd: false,
+  /** 节拍器时间 */
+  beatLengthInMilliseconds: 0,
+  state: '',
+  source1: '' as any,
+  source2: '' as any,
+  index: 0,
+  show: false
+});
+
+let diffTime = 0; // 每一次节拍时间差
+const handlePlay = (i: number, source: any) => {
+  return new Promise(resolve => {
+    const startTime = +new Date();
+    setTimeout(() => {
+      diffTime = 0;
+      if (tickData.tickEnd) {
+        resolve(i);
+        return;
+      }
+      tickData.index++;
+
+      if (source) source.play();
+      resolve(i);
+      const endTime = +new Date();
+      // diffTime = endTime - startTime - tickData.beatLengthInMilliseconds;
+      // console.log(endTime - startTime, diffTime);
+    }, tickData.beatLengthInMilliseconds - diffTime);
+  });
+};
+
+// let timeSppedEnum = 16.7;
+// let payBeatTime = new Date().getTime();
+// const proofTime = () => {
+//   if (setting.playState !== 'play') {
+//     return;
+//   }
+//   let startTime = Date.now();
+//   requestAnimationFrame(() => {
+//     const endTime = Date.now();
+//     // 渲染时间大于16.6,就会让页面卡顿, 如果渲染时间大与16.6就下一个渲染帧去计算
+//     if (endTime - startTime < 16.7) {
+//       handlePlaying();
+//       proofTime();
+//     } else {
+//       setTimeout(() => {
+//         handlePlaying();
+//         proofTime();
+//       }, 16.7);
+//     }
+//   });
+// };
+
+/** 设置节拍器
+ * @param beatLengthInMilliseconds 节拍间隔时间
+ * @param beat 节拍数
+ */
+export const handleInitTick = (
+  beatLengthInMilliseconds: number,
+  beat: number
+) => {
+  tickData.state = '';
+  tickData.beatLengthInMilliseconds = beatLengthInMilliseconds;
+  tickData.len = beat;
+};
+
+/** 开始节拍器 */
+export const handleStartTick = async () => {
+  tickData.show = true;
+  tickData.tickEnd = false;
+  if (tickData.state !== 'ok') {
+    tickData.source1 = new Howl({
+      src: tockAndTick.tick
+    });
+    tickData.source2 = new Howl({
+      src: tockAndTick.tock
+    });
+    tickData.state = 'ok';
+  }
+  tickData.index = 0;
+  tickData.beatLengthInMilliseconds = (60 / setting.speed) * 1000;
+  for (let i = 0; i <= tickData.len; i++) {
+    // 提前结束, 直接放回false
+    if (tickData.tickEnd) return false;
+    const source =
+      i === 0 ? tickData.source1 : i === tickData.len ? null : tickData.source2;
+
+    await handlePlay(i, source);
+  }
+  console.log(+new Date());
+  tickData.show = false;
+  return true;
+};
+
+/** 节拍器暂停 */
+export const hendleEndTick = () => {
+  tickData.tickEnd = true;
+};

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 0
src/views/tempo-practice/tockAndTick.json


Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio