globalTools.vue 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. <!--
  2. * @FileDescription: 公用工具 - 【白板/批注】
  3. * @Author: 王新雷
  4. * @Date:2024年10月14日10:03:11
  5. -->
  6. <template>
  7. <div class="globalTools" :class="[isPlay ? 'isPlay' : '', isHidden ? 'isHidden' : '']">
  8. <div class="mask" v-if="isMask"></div>
  9. <div :class="['iconTools', toolOpen ? 'hideTools' : '']" ref="iconToolsDom">
  10. <img @click="openTool" src="@/img/layout/icon-tool.png" />
  11. </div>
  12. <div :class="['expendTools', toolOpen ? 'showTools' : '']" ref="expendToolsDom">
  13. <img @click="openType('note')" src="@/img/layout/icon-note.png" />
  14. <img @click="openType('whiteboard')" class="iconWhiteboard" src="@/img/layout/icon-whiteboard.png" />
  15. <img @click="openTool" class="iconArrow" src="@/img/layout/g-arrow-right.png" />
  16. </div>
  17. </div>
  18. <pen
  19. :close="
  20. () => {
  21. penShow = false
  22. isHidden = false
  23. }
  24. "
  25. v-model="penShow"
  26. />
  27. <pen
  28. :is-white="true"
  29. :close="
  30. () => {
  31. whitePenShow = false
  32. isHidden = false
  33. }
  34. "
  35. v-model="whitePenShow"
  36. />
  37. </template>
  38. <script setup lang="ts">
  39. import { baseSize, baseWidth, size } from "@/libs/rem"
  40. import pen from "@/views/coursewarePlay/components/pen"
  41. import { toolOpen, whitePenShow, penShow, isPlay, isHidden } from "./globalTools"
  42. import { onMounted, onUnmounted, ref, watch } from "vue"
  43. import { useRoute } from "vue-router"
  44. const isMask = ref(false) // 是否显示遮罩层,为了处理云教练里面拖动不了的问题
  45. const route = useRoute()
  46. watch(
  47. () => route.path,
  48. () => {
  49. handleStatus()
  50. }
  51. )
  52. const iconToolsDom = ref<HTMLDivElement>()
  53. const expendToolsDom = ref<HTMLDivElement>()
  54. function openTool() {
  55. if (isLock) return
  56. toolOpen.value = !toolOpen.value
  57. }
  58. function openType(type: "note" | "whiteboard") {
  59. if (isLock) return
  60. if (type === "note") {
  61. penShow.value = true
  62. isHidden.value = true
  63. } else if (type === "whiteboard") {
  64. whitePenShow.value = true
  65. isHidden.value = true
  66. }
  67. }
  68. function handleStatus() {
  69. isHidden.value = route.path === "/login" ? true : false
  70. }
  71. function computePos(type: "width" | "height", value: number | string) {
  72. const clientNum = type == "width" ? baseWidth : document.documentElement.clientHeight
  73. return typeof value === "string"
  74. ? {
  75. pos:
  76. type == "width"
  77. ? ((clientNum - (parseInt(value) / 100) * clientNum) / 2 / baseSize).toFixed(5)
  78. : (clientNum - (parseInt(value) / 100) * clientNum) / 2,
  79. unit: value
  80. }
  81. : {
  82. pos: type == "width" ? ((clientNum - value) / 2 / baseSize).toFixed(5) : (clientNum - value * (size / baseSize)) / 2,
  83. unit: (value / baseSize).toFixed(5) + "rem"
  84. }
  85. }
  86. /* 拖拽还没有兼容rem */
  87. let isLock = false
  88. let toolMoveY = 0 // 移动的距离
  89. function drag(el: HTMLElement) {
  90. function mousedown(e: MouseEvent | TouchEvent) {
  91. const isTouchEv = isTouchEvent(e)
  92. const event = isTouchEv ? e.touches[0] : e
  93. isLock = false
  94. isMask.value = true
  95. const parentElement = el
  96. const parentElementRect = parentElement.getBoundingClientRect()
  97. const downX = event.clientX
  98. const downY = event.clientY
  99. // const clientWidth = document.documentElement.clientWidth
  100. const clientHeight = document.documentElement.clientHeight
  101. // const minLeft = 0
  102. const minTop = 0
  103. // const maxLeft = clientWidth - parentElementRect.width
  104. const maxTop = clientHeight - parentElementRect.height
  105. function onMousemove(e: MouseEvent | TouchEvent) {
  106. const event = isTouchEvent(e) ? e.touches[0] : e
  107. // let moveX = parentElementRect.left + (e.clientX - downX)
  108. let moveY = parentElementRect.top + (event.clientY - downY)
  109. // let moveY = e.clientY - downY
  110. // moveX = moveX < minLeft ? minLeft : moveX > maxLeft ? maxLeft : moveX
  111. moveY = moveY < minTop ? minTop : moveY > maxTop ? maxTop : moveY
  112. toolMoveY = moveY
  113. document.documentElement.style.setProperty("--toolTranslateY", `${moveY}px`)
  114. // 计算移动的距离
  115. const cX = event.clientX - downX
  116. const cY = event.clientY - downY
  117. // 如果移动距离超过一定阈值,则认为是拖动
  118. if (Math.abs(cX) > 3 || Math.abs(cY) > 3) {
  119. isLock = true // 设置为拖动状态
  120. }
  121. }
  122. function onMouseup() {
  123. document.removeEventListener(isTouchEv ? "touchmove" : "mousemove", onMousemove)
  124. document.removeEventListener(isTouchEv ? "touchend" : "mouseup", onMouseup)
  125. isMask.value = false
  126. }
  127. document.addEventListener(isTouchEv ? "touchmove" : "mousemove", onMousemove)
  128. document.addEventListener(isTouchEv ? "touchend" : "mouseup", onMouseup)
  129. }
  130. el.addEventListener("mousedown", mousedown)
  131. el.addEventListener("touchstart", mousedown)
  132. }
  133. function isTouchEvent(e: MouseEvent | TouchEvent): e is TouchEvent {
  134. return window.TouchEvent && e instanceof window.TouchEvent
  135. }
  136. //重新计算位置 居中
  137. function refreshPos() {
  138. const posHeight = computePos("height", iconToolsDom.value?.clientHeight || 0)
  139. if (iconToolsDom.value) {
  140. document.documentElement.style.setProperty("--toolTranslateY", `${posHeight.pos}px`)
  141. }
  142. }
  143. let rect: any
  144. function onResize() {
  145. rect = rect ? rect : iconToolsDom.value?.getBoundingClientRect()
  146. const clientHeight = document.documentElement.clientHeight
  147. const maxTop = clientHeight - rect.height
  148. if (toolMoveY >= maxTop) {
  149. document.documentElement.style.setProperty("--toolTranslateY", `${maxTop}px`)
  150. }
  151. }
  152. onMounted(() => {
  153. drag(iconToolsDom.value!)
  154. drag(expendToolsDom.value!)
  155. refreshPos()
  156. window.addEventListener("resize", onResize)
  157. })
  158. onUnmounted(() => {
  159. window.removeEventListener("resize", onResize)
  160. })
  161. </script>
  162. <style lang="scss" scoped>
  163. .globalTools {
  164. &.isPlay {
  165. .iconTools,
  166. .expendTools {
  167. opacity: $opacity-disabled;
  168. }
  169. }
  170. &.isHidden {
  171. .iconTools,
  172. .expendTools {
  173. opacity: 0;
  174. display: none;
  175. }
  176. }
  177. .mask {
  178. position: fixed;
  179. left: 0;
  180. right: 0;
  181. top: 0;
  182. bottom: 0;
  183. background-color: transparent;
  184. z-index: 2998;
  185. }
  186. .iconTools,
  187. .expendTools {
  188. position: fixed;
  189. right: -6px;
  190. top: 0;
  191. transform: translateY(var(--toolTranslateY));
  192. // margin-top: -29px;
  193. z-index: 2999;
  194. // padding: 0 5px;
  195. background: rgba(0, 0, 0, 0.4);
  196. border-radius: 200px 0px 0px 200px;
  197. border: 2px solid rgba(255, 255, 255, 0.3);
  198. border-right-width: 0;
  199. cursor: pointer;
  200. font-size: 0;
  201. // transition: transform 0.2s ease;
  202. img {
  203. padding: 8px 15px;
  204. width: 34px;
  205. height: 34px;
  206. box-sizing: content-box;
  207. -moz-user-select: none;
  208. /* 火狐浏览器 */
  209. -webkit-user-drag: none;
  210. /* 谷歌、Safari和Opera浏览器 */
  211. -webkit-user-select: none;
  212. /* 谷歌、Safari和Opera浏览器 */
  213. -ms-user-select: none;
  214. /* IE10+浏览器 */
  215. user-select: none;
  216. /* 通用 */
  217. -webkit-touch-callout: none;
  218. /* iOS Safari */
  219. &:hover {
  220. opacity: $opacity-hover;
  221. }
  222. }
  223. }
  224. .iconTools {
  225. // transition-delay: 0.2s;
  226. }
  227. .expendTools {
  228. // transform: translateX(100%);
  229. display: none;
  230. img {
  231. cursor: pointer;
  232. }
  233. .iconWhiteboard {
  234. // margin: 0 30px;
  235. }
  236. .iconArrow {
  237. padding: 7px 15px;
  238. width: 28px;
  239. height: 28px;
  240. }
  241. }
  242. .hideTools {
  243. // transition: transform 0.2s ease;
  244. transform: translateY(var(--toolTranslateY));
  245. display: none;
  246. }
  247. .showTools {
  248. // transition: transform 0.2s ease;
  249. // transition-delay: 0.2s;
  250. transform: translateY(var(--toolTranslateY));
  251. display: flex;
  252. align-items: center;
  253. }
  254. }
  255. </style>