Browse Source

双指缩放

liushengqiang 1 year ago
parent
commit
b12aa23877

+ 1 - 1
instrument.html

@@ -4,7 +4,7 @@
 <head>
   <meta charset="UTF-8" />
   <meta name="viewport"
-    content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
+    content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
   <title>AI学练</title>
   <link rel="icon" href="/favicon.ico?v=1" />
   <script src="/flexible.js" charset="UTF-8"></script>

+ 27 - 0
package-lock.json

@@ -14,6 +14,7 @@
         "consola": "^2.15.3",
         "dayjs": "^1.11.7",
         "eventemitter3": "^5.0.0",
+        "hammerjs": "^2.0.8",
         "howler": "^2.2.3",
         "html2canvas": "^1.4.1",
         "lodash": "^4.17.21",
@@ -28,6 +29,7 @@
         "ws": "^8.13.0"
       },
       "devDependencies": {
+        "@types/hammerjs": "^2.0.41",
         "@types/howler": "^2.2.7",
         "@types/lodash": "^4.14.192",
         "@types/node": "^18.15.11",
@@ -2402,6 +2404,12 @@
       "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==",
       "dev": true
     },
+    "node_modules/@types/hammerjs": {
+      "version": "2.0.41",
+      "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.41.tgz",
+      "integrity": "sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA==",
+      "dev": true
+    },
     "node_modules/@types/howler": {
       "version": "2.2.7",
       "resolved": "https://registry.npmjs.org/@types/howler/-/howler-2.2.7.tgz",
@@ -3673,6 +3681,14 @@
       "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
       "dev": true
     },
+    "node_modules/hammerjs": {
+      "version": "2.0.8",
+      "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
+      "integrity": "sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==",
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
     "node_modules/has": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -6877,6 +6893,12 @@
       "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==",
       "dev": true
     },
+    "@types/hammerjs": {
+      "version": "2.0.41",
+      "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.41.tgz",
+      "integrity": "sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA==",
+      "dev": true
+    },
     "@types/howler": {
       "version": "2.2.7",
       "resolved": "https://registry.npmjs.org/@types/howler/-/howler-2.2.7.tgz",
@@ -7886,6 +7908,11 @@
       "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
       "dev": true
     },
+    "hammerjs": {
+      "version": "2.0.8",
+      "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
+      "integrity": "sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ=="
+    },
     "has": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",

+ 2 - 0
package.json

@@ -14,6 +14,7 @@
     "consola": "^2.15.3",
     "dayjs": "^1.11.7",
     "eventemitter3": "^5.0.0",
+    "hammerjs": "^2.0.8",
     "howler": "^2.2.3",
     "html2canvas": "^1.4.1",
     "lodash": "^4.17.21",
@@ -28,6 +29,7 @@
     "ws": "^8.13.0"
   },
   "devDependencies": {
+    "@types/hammerjs": "^2.0.41",
     "@types/howler": "^2.2.7",
     "@types/lodash": "^4.14.192",
     "@types/node": "^18.15.11",

+ 4 - 0
src/page-instrument/view-figner/index.module.less

@@ -30,6 +30,7 @@
         justify-content: center;
         border: none;
         background: none;
+        cursor: pointer;
 
         img {
             width: 100%;
@@ -80,6 +81,7 @@
         color: #A14927;
         line-height: 33px;
         text-align: center;
+        cursor: pointer;
 
         &:active {
             transform: translateY(2px);
@@ -107,6 +109,7 @@
             flex-direction: column;
             justify-content: center;
             align-items: center;
+            cursor: pointer;
 
             &:first-child {
                 border-right: 1Px solid #a149275e;
@@ -251,6 +254,7 @@
     position: relative;
     width: 84%;
     height: 100%;
+    pointer-events: none;
 
     &>img {
         position: absolute;

+ 70 - 11
src/page-instrument/view-figner/index.tsx

@@ -1,4 +1,4 @@
-import { PropType, computed, defineComponent, onBeforeMount, reactive } from "vue";
+import { PropType, computed, defineComponent, nextTick, onBeforeMount, onMounted, reactive } from "vue";
 import styles from "./index.module.less";
 import icons from "./image/icons.json";
 import { FIGNER_INSTRUMENT_DATA, IFIGNER_INSTRUMENT_Note } from "/src/view/figner-preview";
@@ -11,7 +11,7 @@ import {
 import { Howl } from "howler";
 import { storeData } from "/src/store";
 import { api_back } from "/src/helpers/communication";
-import { Button, Icon } from "vant";
+import Hammer from "hammerjs";
 
 export default defineComponent({
 	name: "viewFigner",
@@ -35,6 +35,15 @@ export default defineComponent({
 			soundFonts: {} as any,
 			viewIndex: 0,
 			noteAudio: null as unknown as Howl,
+			transform: {
+				scale: 1,
+				x: 0,
+				y: 0,
+				startScale: 1,
+				startX: 0,
+				startY: 0,
+				transition: "",
+			},
 		});
 		const fingerData = reactive({
 			relationshipIndex: 0,
@@ -108,6 +117,54 @@ export default defineComponent({
 			api_back();
 		};
 
+		onMounted(() => {
+			loadElement();
+		});
+		const loadElement = () => {
+			const fingeringContainer = document.getElementById("fingeringContainer");
+			// console.log("🚀 ~ fingeringContainer:", fingeringContainer);
+			const mc = new Hammer.Manager(fingeringContainer as HTMLElement);
+			mc.add(new Hammer.Pan({ threshold: 0, pointers: 0 }));
+			mc.add(new Hammer.Pinch({ threshold: 0 })).recognizeWith([mc.get("pan")]);
+			// mc.get("pan").set({ direction: Hammer.DIRECTION_ALL });
+			// mc.get("pinch").set({ enable: true });
+			mc.on("panstart pinchstart", function (ev) {
+				data.transform.transition = "";
+			});
+			mc.on("panmove pinchmove", function (ev) {
+				if (ev.type === "pinchmove") {
+					// console.log("🚀 ~ ev:", ev.type, ev.scale, ev.deltaX, ev.deltaY);
+					data.transform.scale = ev.scale * data.transform.startScale;
+					data.transform.x = data.transform.startX + ev.deltaX;
+					data.transform.y = data.transform.startY + ev.deltaY;
+				}
+				if (ev.type === "panmove") {
+					// console.log("🚀 ~ ev:", ev.type, ev.deltaX, ev.deltaY);
+					data.transform.x = data.transform.startX + ev.deltaX;
+					data.transform.y = data.transform.startY + ev.deltaY;
+				}
+			});
+			//
+			mc.on("hammer.input", function (ev) {
+				// console.log("🚀 ~ ev:", ev.type, ev.isFinal);
+				if (ev.isFinal) {
+					data.transform.startScale = data.transform.scale;
+					data.transform.startX = data.transform.x;
+					data.transform.startY = data.transform.y;
+				}
+			});
+		};
+		const resetElement = () => {
+			data.transform.transition = "all 0.3s";
+			nextTick(() => {
+				data.transform.scale = 1;
+				data.transform.x = 0;
+				data.transform.y = 0;
+				data.transform.startScale = 1;
+				data.transform.startX = 0;
+				data.transform.startY = 0;
+			});
+		};
 		return () => {
 			const relationship = fingerData.subject?.relationship?.[data.realKey] || [];
 			const rs: number[] = Array.isArray(relationship[1])
@@ -140,18 +197,24 @@ export default defineComponent({
 							)}
 						</div>
 						<div class={styles.rightBtn}>
-							<div class={[styles.item, styles.disabled]}>
+							<div class={[styles.item]} onClick={() => resetElement()}>
 								<img src={icons.icon_2_0} />
 								<span>还原</span>
 							</div>
-							<div class={[styles.item, styles.disabled]}>
+							<div class={[styles.item]}>
 								<img src={icons.icon_2_1} />
 								<span>小技巧</span>
 							</div>
 						</div>
 					</div>
-					<div class={styles.fingerContent}>
-						<div class={[styles.fingeringContainer]}>
+					<div id="fingeringContainer" class={styles.fingerContent}>
+						<div
+							style={{
+								transform: `translate3d(${data.transform.x}px,${data.transform.y}px,0px) scale(${data.transform.scale})`,
+								transition: data.transform.transition,
+							}}
+							class={[styles.fingeringContainer]}
+						>
 							<div class={styles.imgs}>
 								<img src={fingerData.subject?.json?.full} />
 								{rs.map((key: number | string, index: number) => {
@@ -179,11 +242,7 @@ export default defineComponent({
 								{data.notes.map((note: IFIGNER_INSTRUMENT_Note) => {
 									const steps = new Array(Math.abs(note.step)).fill(1);
 									return (
-										<div
-											draggable={false}
-											class={styles.note}
-											onClick={() => noteClick(note)}
-										>
+										<div draggable={false} class={styles.note} onClick={() => noteClick(note)}>
 											{data.realKey === note.realKey ? (
 												<img draggable={false} src={icons.icon_btn_ylow} />
 											) : (