LeftButtons.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. <template>
  2. <div class="controls-left-buttons">
  3. <!-- <div class="buttons tour">
  4. <div @click="onModeChange('panorama')">
  5. <i class="iconfont" :class="['icon-preview']"></i>
  6. </div>
  7. <div class="show-list">
  8. <i class="iconfont" :class="['icon-pull-down']"></i>
  9. </div>
  10. </div> -->
  11. <div class="buttons mode" :class="{ disabled: isPlay || flying }">
  12. <div v-if="controls.showPanorama" :class="{ active: viewmode == 'panorama' }" @click="onModeChange('panorama')">
  13. <ui-icon tipV="top" :type="viewmode == 'panorama' ? 'show_roaming_selected' : 'show_roaming_normal'"></ui-icon>
  14. </div>
  15. <div v-if="controls.showFloorplan" :class="{ active: viewmode == 'floorplan' }" @click="onModeChange('floorplan')">
  16. <ui-icon tipV="top" :type="viewmode == 'floorplan' ? 'show_plane_selected' : 'show_plane_normal'"></ui-icon>
  17. </div>
  18. <div v-if="controls.showDollhouse" :class="{ active: viewmode == 'dollhouse' }" @click="onModeChange('dollhouse')">
  19. <ui-icon tipV="top" :type="viewmode == 'dollhouse' ? 'show_3d_selected' : 'show_3d_normal'"></ui-icon>
  20. </div>
  21. </div>
  22. <!-- <div class="play-control" v-if="tours.length > 0" :class="{ disabled: flying || isSelect }"> -->
  23. <div class="play-control" v-if="tours.length > 0">
  24. <div class="play-btn tour-btn" @click="playTour">
  25. <ui-icon tipV="top" :type="isPlay ? 'pause' : 'preview'"></ui-icon>
  26. </div>
  27. <div class="tour-btn bor" @click="openTours">
  28. <ui-icon type="pull-down" :class="{ active: showTours }"></ui-icon>
  29. </div>
  30. <!-- <teleport :to="editorMain"> -->
  31. <div class="tour-list 111" :class="{ ban: flying || isSelect }" :style="`height:${showTours ? '120px' : '0px'};`">
  32. <div class="part-content" ref="tourScroll">
  33. <!-- 多个片段 -->
  34. <div class="part-list" v-if="tours.length > 1">
  35. <div
  36. @click="changeFrame(1, index)"
  37. :class="{ disabled: isPlay && partId != index }"
  38. class="part-item"
  39. :name="index"
  40. v-for="(i, index) in tours"
  41. :style="`background-image:url(${i.frameId ? common.changeUrl(i.list[i.frameId].enter.cover) : common.changeUrl(i.list[0].enter.cover)});`"
  42. >
  43. <div class="part-title">{{ i.name }}</div>
  44. <div v-if="partId == index && progressNum > 0" class="precent" :style="`width:${progressNum}%;`"></div>
  45. </div>
  46. </div>
  47. <!-- 只有一个片段 -->
  48. <div class="part-list frame-list" v-else>
  49. <div
  50. @click="changeFrame(2, index)"
  51. :class="{ disabled: isPlay && frameId != index }"
  52. class="part-item"
  53. v-for="(i, index) in tours[0].list"
  54. :name="index"
  55. :style="`background-image:url(${common.changeUrl(i.enter.cover)});`"
  56. >
  57. <div class="part-title"></div>
  58. <div v-if="frameId == index && progressNum > 0" class="precent" :style="`width:${progressNum}%;`"></div>
  59. </div>
  60. </div>
  61. </div>
  62. </div>
  63. <!-- </teleport> -->
  64. </div>
  65. </div>
  66. </template>
  67. <script setup>
  68. import { computed, defineProps, onMounted, ref, nextTick } from 'vue'
  69. import { Scrollbar, Dialog } from '@/global_components'
  70. import { useApp, getApp } from '@/app'
  71. import { useStore } from 'vuex'
  72. import common from '@/utils/common'
  73. import { useMusicPlayer } from '@/utils/sound'
  74. const musicPlayer = useMusicPlayer()
  75. const props = defineProps({
  76. isEdit: Boolean,
  77. })
  78. let timer = null
  79. const isSelect = ref(false)
  80. const store = useStore()
  81. const tourScroll = ref(null)
  82. const viewmode = computed(() => store.getters.mode)
  83. const flying = computed(() => store.getters['flying'])
  84. const controls = computed(() => store.getters['scene/metadata'].controls || {})
  85. const showTours = computed(() => store.getters['tour/showTours'])
  86. const partId = computed(() => {
  87. let id = store.getters['tour/partId']
  88. if (isPlay.value) {
  89. slideScroll()
  90. }
  91. return id
  92. })
  93. const frameId = computed(() => {
  94. let id = store.getters['tour/frameId']
  95. if (isPlay.value) {
  96. slideScroll()
  97. }
  98. return id
  99. })
  100. const progressNum = ref(0)
  101. const isPlay = computed(() => {
  102. let status = store.getters['tour/isPlay']
  103. let map = document.querySelector('.kankan-app div[xui_min_map]')
  104. if (map) {
  105. if (status) {
  106. map.classList.add('disabled')
  107. } else {
  108. map.classList.remove('disabled')
  109. }
  110. }
  111. return status
  112. })
  113. const isInit = ref(false)
  114. const tours = computed(() => {
  115. let tours = store.getters['tour/tours']
  116. if (tours.length > 0) {
  117. if (tourScroll.value && !isInit.value) {
  118. isInit.value = true
  119. new Scrollbar(tourScroll.value, { onlyHorizontal: true })
  120. }
  121. }
  122. return tours
  123. })
  124. const onModeChange = name => {
  125. store.commit('setMode', name)
  126. }
  127. const playTour = async () => {
  128. let player = await getApp().TourManager.player
  129. if (isPlay.value) {
  130. store.commit('tour/setData', { isPlay: true })
  131. player.pause()
  132. } else {
  133. store.commit('tour/setData', { isPlay: true })
  134. player.play(partId.value)
  135. }
  136. }
  137. const hanlderTourPartPlay = time => {
  138. if (!timer) {
  139. timer = KanKan.Animate.transitions.start(progress => {
  140. // console.log(progress)
  141. progressNum.value = progress * 100
  142. }, time)
  143. }
  144. }
  145. const cancelTimer = () => {
  146. if (timer) {
  147. KanKan.Animate.transitions.cancel(timer)
  148. timer = null
  149. }
  150. }
  151. const slideScroll = () => {
  152. nextTick(() => {
  153. let t = setTimeout(() => {
  154. clearTimeout(t)
  155. let id = tours.value.length > 1 ? partId.value : frameId.value
  156. let item = document.querySelector(`.part-item[name="${id}"]`)
  157. // let itemLeft = (item.offsetWidth + 10) * (id + 1)
  158. // let Scroll = document.querySelector('.part-content .x-scrollbar__container')
  159. // // let contentW = slideType == 1 ? document.querySelector('.part-content').offsetWidth : document.querySelector('.iframe-content').offsetWidth
  160. // let ScrollW = Scroll.offsetWidth
  161. // Scroll.scrollTo({ left: itemLeft - ScrollW + 10, behavior: 'smooth' })
  162. item.scrollIntoView({ block: 'center', behavior: 'smooth', inline: 'center' })
  163. }, 100)
  164. })
  165. }
  166. const hanlderTour = async () => {
  167. let player = await getApp().TourManager.player
  168. player.on('play', data => {
  169. musicPlayer.pause(true)
  170. // if (tours.value.length > 1) {
  171. // let time = getPartTime(data.partId)
  172. // console.log(time)
  173. // hanlderTourPartPlay(time)
  174. // }
  175. })
  176. player.on('pause', tours => {
  177. console.log('pause', player)
  178. musicPlayer.resume()
  179. progressNum.value = 0
  180. cancelTimer()
  181. store.commit('tour/setData', { isPlay: false })
  182. })
  183. player.on('end', tours => {
  184. musicPlayer.resume()
  185. progressNum.value = 100
  186. slideScroll()
  187. store.commit('tour/setData', { isPlay: false })
  188. cancelTimer()
  189. })
  190. let currPartId = null
  191. let currProgress = 0
  192. let currFrames = 0
  193. player.on('progress', data => {
  194. if (tours.value.length == 1) {
  195. progressNum.value = data.progress * 100
  196. } else {
  197. // let time = getPartTime(data.partId)
  198. // hanlderTourPartPlay(time)
  199. if (currPartId != data.partId) {
  200. currPartId = data.partId
  201. currFrames = tours.value[data.partId].list.length
  202. currProgress = 0
  203. } else {
  204. currProgress += data.progress / currFrames
  205. if (currProgress >= 100) {
  206. currProgress = 100
  207. }
  208. progressNum.value = currProgress
  209. }
  210. }
  211. store.commit('tour/setData', { partId: data.partId, frameId: data.frameId, isPlay: true })
  212. })
  213. // nextTick(() => {
  214. // editorMain.value = document.querySelector('.ui-editor-main')
  215. // })
  216. }
  217. const getPartTime = partId => {
  218. cancelTimer()
  219. let time = 0
  220. // for (let i = 0; i < tours.value[partId].list.length - 1; i++) {
  221. // time += tours.value[partId].list[i].time - 0
  222. // }
  223. // if (tours.value[partId].list[tours.value[partId].list.length - 1]._end) {
  224. // if (tours.value[partId].list.length > 2) {
  225. // time += (tours.value[partId].list.length - 1) * 1000
  226. // }
  227. // } else {
  228. // time += (tours.value[partId].list.length - 1) * 1000
  229. // }
  230. // for (let i = 0; i < tours.value[partId].list.length; i++) {
  231. // time += tours.value[partId].list[i].time - 0 + 1000
  232. // }
  233. for (let i = 0; i < tours.value[partId].list.length; i++) {
  234. if (!tours.value[partId].list[i]._end) {
  235. time += tours.value[partId].list[i].time - 0
  236. if (!tours.value[partId].list[i]._notrans) {
  237. time += 1000
  238. }
  239. }
  240. }
  241. return time
  242. }
  243. const openTours = () => {
  244. // showTours.value = !showTours.value
  245. store.commit('tour/setData', { showTours: !showTours.value })
  246. let bot = '20px'
  247. if (showTours.value) {
  248. bot = '140px'
  249. } else {
  250. bot = '20px'
  251. }
  252. store.commit('setControlsBottom', bot)
  253. nextTick(() => {
  254. if (isPlay.value) {
  255. slideScroll()
  256. }
  257. })
  258. }
  259. const changeFrame = async (type, id) => {
  260. progressNum.value = 0
  261. // recorder.selectFrame(id)
  262. let player = await getApp().TourManager.player
  263. // player.selectFrame(id)
  264. isSelect.value = true
  265. if (type == 1) {
  266. player.selectPart(id)
  267. console.log(tours.value[id].frameId)
  268. let f_id = 0
  269. if (tours.value[id].frameId) {
  270. f_id = tours.value[id].frameId
  271. }
  272. player.selectFrame(f_id).then(() => {
  273. isSelect.value = false
  274. })
  275. store.commit('tour/setData', {
  276. frameId: f_id,
  277. partId: id,
  278. })
  279. } else {
  280. player.selectFrame(id).then(() => {
  281. isSelect.value = false
  282. })
  283. store.commit('tour/setData', {
  284. frameId: id,
  285. })
  286. }
  287. slideScroll()
  288. }
  289. const onClickHandler = async () => {
  290. if (isPlay.value) {
  291. let player = await getApp().TourManager.player
  292. player.pause()
  293. musicPlayer.resume()
  294. }
  295. }
  296. onMounted(() => {
  297. useApp().then(async sdk => {
  298. hanlderTour()
  299. })
  300. nextTick(() => {
  301. let player = document.querySelector('.player[name="main"]')
  302. player.addEventListener('click', onClickHandler)
  303. })
  304. })
  305. </script>
  306. <style lang="scss" scoped>
  307. .controls-left-buttons {
  308. margin-left: 20px;
  309. margin-bottom: 20px;
  310. display: flex;
  311. }
  312. .buttons.tour {
  313. margin-right: 10px;
  314. > div {
  315. margin-left: 0px;
  316. margin-right: 0px;
  317. padding: 0 10px;
  318. &.show-list {
  319. border-left: solid 1px var(--editor-font-color);
  320. }
  321. .icon-pull-down {
  322. font-size: 12px;
  323. }
  324. span {
  325. right: -10px;
  326. }
  327. }
  328. }
  329. .tour-list {
  330. width: 100%;
  331. position: fixed;
  332. bottom: 0;
  333. left: 0;
  334. display: flex;
  335. align-items: center;
  336. justify-content: center;
  337. flex: 1;
  338. // height: var(--editor-toolbar-height);
  339. height: 120px;
  340. background-color: var(--editor-menu-back);
  341. pointer-events: all;
  342. left: var(--editor-toolbox-left);
  343. z-index: 1;
  344. transition: all 0.3s ease;
  345. &.ban {
  346. pointer-events: none;
  347. }
  348. .part-content {
  349. display: flex;
  350. flex-direction: row;
  351. overflow: hidden;
  352. width: 100%;
  353. height: 100%;
  354. .part-list {
  355. width: 100%;
  356. height: 120px;
  357. display: flex;
  358. align-items: center;
  359. justify-content: flex-start;
  360. padding: 0 10px;
  361. box-sizing: border-box;
  362. .part-item {
  363. width: 120px;
  364. height: 80px;
  365. position: relative;
  366. cursor: pointer;
  367. margin-right: 10px;
  368. background-repeat: no-repeat;
  369. background-size: 100%;
  370. background-position: center;
  371. &:last-of-type {
  372. margin-right: 0px;
  373. }
  374. &:hover {
  375. opacity: 0.6;
  376. }
  377. .precent {
  378. width: 0%;
  379. height: 24px;
  380. position: absolute;
  381. bottom: 0;
  382. left: 0;
  383. background: #00c8af;
  384. opacity: 0.4;
  385. z-index: 1;
  386. // transition: width 0.1s;
  387. }
  388. .part-title {
  389. width: 100%;
  390. height: 24px;
  391. background: linear-gradient(180deg, rgba(0, 0, 0, 0.25) 0%, rgba(0, 0, 0, 0.5) 100%);
  392. position: absolute;
  393. bottom: 0;
  394. left: 0;
  395. display: flex;
  396. align-items: center;
  397. justify-content: center;
  398. font-size: 14px;
  399. z-index: 10;
  400. }
  401. }
  402. }
  403. }
  404. }
  405. .play-control {
  406. pointer-events: all;
  407. display: flex;
  408. align-items: center;
  409. justify-content: center;
  410. height: 34px;
  411. border-radius: 17px;
  412. background-color: rgba(0, 0, 0, 0.3);
  413. min-width: 34px;
  414. border-radius: 17px;
  415. margin-left: 10px;
  416. .tour-btn {
  417. width: 34px;
  418. height: 22px;
  419. cursor: pointer;
  420. display: flex;
  421. align-items: center;
  422. justify-content: center;
  423. > .iconfont {
  424. font-size: 14px;
  425. }
  426. &.play-btn {
  427. // border-right: 1px solid rgba(255, 255, 255, 0.2);
  428. > .iconfont {
  429. font-size: 20px;
  430. transition: rotate 0.3s;
  431. }
  432. }
  433. &.bor {
  434. border-left: 1px solid rgba(255, 255, 255, 0.2);
  435. > .iconfont {
  436. transform: rotate(180deg);
  437. &.active {
  438. transform: rotate(0deg);
  439. }
  440. }
  441. }
  442. }
  443. }
  444. </style>