|
@@ -0,0 +1,116 @@
|
|
|
+<template>
|
|
|
+ <div
|
|
|
+ class="rhythmPracticeElement"
|
|
|
+ :class="{ lock: elementInfo.lock }"
|
|
|
+ :style="{
|
|
|
+ top: elementInfo.top + 'px',
|
|
|
+ left: elementInfo.left + 'px',
|
|
|
+ width: elementInfo.width + 'px',
|
|
|
+ height: elementInfo.height + 'px'
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <div class="rotate-wrapper" :style="{ transform: `rotate(${elementInfo.rotate}deg)` }">
|
|
|
+ <div
|
|
|
+ class="element-content"
|
|
|
+ v-contextmenu="contextmenus"
|
|
|
+ @mousedown="$event => handleSelectElement($event)"
|
|
|
+ @touchstart="$event => handleSelectElement($event)"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ v-if="isShowMask"
|
|
|
+ @mousedown.stop="$event => handleSelectElement($event, false)"
|
|
|
+ @touchstart.stop="$event => handleSelectElement($event, false)"
|
|
|
+ class="mask"
|
|
|
+ ></div>
|
|
|
+ <rhythmPracticePlayer :width="elementInfo.width" :height="elementInfo.height" :scale="canvasScale" :dataJson="elementInfo.dataJson" />
|
|
|
+ <div
|
|
|
+ :class="['handler-border', item]"
|
|
|
+ v-for="item in ['t', 'b', 'l', 'r']"
|
|
|
+ :key="item"
|
|
|
+ @mousedown="$event => handleSelectElement($event)"
|
|
|
+ @touchstart="$event => handleSelectElement($event)"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { storeToRefs } from "pinia"
|
|
|
+import { useMainStore } from "@/store"
|
|
|
+import type { PPTRhythmPracticeElement } from "@/types/slides"
|
|
|
+import type { ContextmenuItem } from "@/components/Contextmenu/types"
|
|
|
+import rhythmPracticePlayer from "./rhythmPracticePlayer"
|
|
|
+import { computed } from "vue"
|
|
|
+
|
|
|
+const props = defineProps<{
|
|
|
+ elementInfo: PPTRhythmPracticeElement
|
|
|
+ selectElement: (e: MouseEvent | TouchEvent, element: PPTRhythmPracticeElement, canMove?: boolean) => void
|
|
|
+ contextmenus: () => ContextmenuItem[] | null
|
|
|
+}>()
|
|
|
+
|
|
|
+const mainStore = useMainStore()
|
|
|
+const { canvasScale } = storeToRefs(mainStore)
|
|
|
+
|
|
|
+/* 当没有选中 或者 拖动过程中加一个遮罩,以免事件进入 iframe 无法触发*/
|
|
|
+const isShowMask = computed(() => {
|
|
|
+ return mainStore.handleElementId !== props.elementInfo.id || props.elementInfo.isMove
|
|
|
+})
|
|
|
+const handleSelectElement = (e: MouseEvent | TouchEvent, canMove = true) => {
|
|
|
+ if (props.elementInfo.lock) return
|
|
|
+ e.stopPropagation()
|
|
|
+
|
|
|
+ props.selectElement(e, props.elementInfo, canMove)
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.rhythmPracticeElement {
|
|
|
+ position: absolute;
|
|
|
+ &.lock .handler-border {
|
|
|
+ cursor: default;
|
|
|
+ }
|
|
|
+}
|
|
|
+.rotate-wrapper {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+.element-content {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ position: relative;
|
|
|
+ .mask {
|
|
|
+ position: absolute;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+}
|
|
|
+.handler-border {
|
|
|
+ position: absolute;
|
|
|
+ cursor: move;
|
|
|
+ &.t {
|
|
|
+ width: 100%;
|
|
|
+ height: 10px;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ }
|
|
|
+ &.b {
|
|
|
+ width: 100%;
|
|
|
+ height: 10px;
|
|
|
+ bottom: 0;
|
|
|
+ left: 0;
|
|
|
+ }
|
|
|
+ &.l {
|
|
|
+ width: 10px;
|
|
|
+ height: 100%;
|
|
|
+ left: 0;
|
|
|
+ top: 0;
|
|
|
+ }
|
|
|
+ &.r {
|
|
|
+ width: 10px;
|
|
|
+ height: 100%;
|
|
|
+ right: 0;
|
|
|
+ top: 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|