فهرست منبع

编辑器-热点-序列帧热点 完成

任一存 2 سال پیش
والد
کامیت
eb7b10aca1

+ 59 - 44
packages/qjkankan-editor/src/views/hotspot/EditPanel.vue

@@ -38,7 +38,7 @@
             ref="icon-setting-component"
             @addHotspot="addHotspot"
           />
-          <div class="bars">
+          <div class="bars" v-if="hotspot.hotspotIconType !== 'personalized_tag'">
             <RangeItem :value="rang" @input="onRangeChange" />
           </div>
         </div>
@@ -53,45 +53,57 @@
             />
             <span class="count">{{ hotspot.hotspotTitle.length }}/15</span>
           </div>
-          <div class="remark">{{ $i18n.t('hotspot.title_show_mode') }}</div>
-          <TabbarSwitcher
-            class="display-mode-selector"
-            :tabList="[
-              $i18n.t('hotspot.show_on_hover'),
-              $i18n.t('hotspot.always_show'),
-              $i18n.t('hotspot.never_show'),
-            ]"
-            :activeIdx="currentTitleDispayModeIdx"
-            @select="onSelectTitleDisplayMode"
-          />
-          <div class="remark">{{ $i18n.t('hotspot.title_position') }}</div>
-          <TabbarSwitcherIcon
-            class="display-mode-selector"
-            :tabList="[
-              {
-                icon: 'icon-up',
-                tip: $i18n.t('hotspot.title_top'),
-              },
-              {
-                icon: 'icon-down',
-                tip: $i18n.t('hotspot.title_bottom'),
-              },
-              {
-                icon: 'icon-left',
-                tip: $i18n.t('hotspot.title_left'),
-              },
-              {
-                icon: 'icon-right',
-                tip: $i18n.t('hotspot.title_right'),
-              },
-              {
-                icon: 'icon-mobile',
-                tip: $i18n.t('hotspot.title_custom'),
-              },
-            ]"
-            :activeIdx="currentTitlePositionIdx"
-            @select="onSelectTitlePosition"
-          />
+          <template
+            v-if="hotspot.hotspotIconType !== 'personalized_tag'"
+          >
+            <div
+              class="remark"
+            >
+              {{ $i18n.t('hotspot.title_show_mode') }}
+            </div>
+            <TabbarSwitcher
+              class="display-mode-selector"
+              :tabList="[
+                $i18n.t('hotspot.show_on_hover'),
+                $i18n.t('hotspot.always_show'),
+                $i18n.t('hotspot.never_show'),
+              ]"
+              :activeIdx="currentTitleDispayModeIdx"
+              @select="onSelectTitleDisplayMode"
+            />
+            <div
+              class="remark"
+            >
+              {{ $i18n.t('hotspot.title_position') }}
+            </div>
+            <TabbarSwitcherIcon
+              class="display-mode-selector"
+              :tabList="[
+                {
+                  icon: 'icon-up',
+                  tip: $i18n.t('hotspot.title_top'),
+                },
+                {
+                  icon: 'icon-down',
+                  tip: $i18n.t('hotspot.title_bottom'),
+                },
+                {
+                  icon: 'icon-left',
+                  tip: $i18n.t('hotspot.title_left'),
+                },
+                {
+                  icon: 'icon-right',
+                  tip: $i18n.t('hotspot.title_right'),
+                },
+                {
+                  icon: 'icon-mobile',
+                  tip: $i18n.t('hotspot.title_custom'),
+                },
+              ]"
+              :activeIdx="currentTitlePositionIdx"
+              @select="onSelectTitlePosition"
+            />
+          </template>
         </div>
         
         <div class="effect-setting">
@@ -404,16 +416,19 @@ export default {
       this.$emit("save", this.hotspot);
     },
     addHotspot(data) {
+      this.hotspot.hotspotIconType = data.type
+      this.hotspot.img = data.img
+      if (data.serialFrameInfo) {
+        this.hotspot.serialFrameInfo.frameNumber = data.serialFrameInfo.frameNumber
+        this.hotspot.serialFrameInfo.duration = data.serialFrameInfo.duration
+      }
+      
       if (this.isAdd && (this.editTitle != '编辑' && this.editTitle != this.$i18n.t('hotspot.edit'))) {
         this.isAdd = false
-        this.hotspot.hotspotIconType = data.type
-        this.hotspot.img = data.img
         this.$bus.emit('addhotspot', this.hotspot)
         this.$getKrpano().set('layer[tooltip_' + this.hotspot.name + '].css', `text-align:center; color:#FFFFFF;
           font-family:STXihei;font-size:${this.hotspot.fontSize}px;`)
       } else {
-        this.hotspot.hotspotIconType = data.type
-        this.hotspot.img = data.img
         this.$getKrpano().set(`hotspot[${this.hotspot.name}].url`, data.img)
         this.$getKrpano().set(`hotspot[${this.hotspot.name}].hotspottitle`, this.hotspot.hotspotTitle)
       }

+ 16 - 3
packages/qjkankan-editor/src/views/hotspot/HotSpotList.vue

@@ -235,19 +235,25 @@ export default {
       if (data.isAdd) {
         this.editTitle = this.$i18n.t('hotspot.add')
         hotspotData = {
+          hotspotType: data.hotspotType, // 热点类型,切换场景、图片、视频、音频、链接、文本等等
+          
+          hotspotIconType: 'system_icon', // 热点图标的类型,系统图标(system_icon)、自定义图片(custom_image)、序列帧(serial_frame)、个性标签(personalized_tag)
+          img: '', // 热点图标
+          serialFrameInfo: { // 热点图标类型为序列帧时,序列帧的数据
+            frameNumber: 0, // 总帧数
+            duration: 0, // 总播放时长(秒)
+          },
+          
           name: "_" + this.$randomWord(true, 8, 8),
           hotspotTitle: this.$i18n.t('hotspot.click_to_comfirm'),
           fontSize: 12,
           type: '',
-          img: '',
           link: '',
           titleDisplayMode: 'always', // 'always' | 'never' | 'hover' 标题显示方式
           titlePosition: 'top', // 'top' | 'bottom' | 'left' | 'right' | 'custom' 标题相对图标位置
           ath: '',
           atv: '',
           size: 1,
-          hotspotType: data.hotspotType, // 热点类型,切换场景、图片、视频、音频、链接、文本等等
-          hotspotIconType: 'system_icon', // 热点图标的类型,系统图标(system_icon)、自定义图片(custom_image)、序列帧(serial_frame)、个性标签(personalized_tag)
           secne: '',
           hyperlink: '',
           textarea: '',
@@ -261,6 +267,13 @@ export default {
         if (!hotspotData.hotspotIconType) {
           hotspotData.hotspotIconType = 'system_icon'
         }
+        // v1.3针对序列帧类型的热点图标,新增了序列帧信息
+        if (!hotspotData.serialFrameInfo) {
+          hotspotData.serialFrameInfo = {
+            frameNumber: 30,
+            duration: 3,
+          }
+        }
         // v1.3把visible: Boolean换成了titleDisplayMode
         if (hotspotData.visible) {
           hotspotData.titleDisplayMode = 'always'

+ 1 - 0
packages/qjkankan-editor/src/views/hotspot/hotspotIconType/custom_image.vue

@@ -9,6 +9,7 @@
       <div class="button-name">添加图标</div>
       <div class="tip">300*300px,支持jpg/png格式</div>
     </button>
+
     <div v-if="selectedIcon" class="icon-selected">
       <div class="icon-wrap">
         <img class="thumb" :src="selectedIcon" alt="">

+ 314 - 2
packages/qjkankan-editor/src/views/hotspot/hotspotIconType/serial_frame.vue

@@ -1,5 +1,317 @@
 <template>
   <div class="hotspot-icon-serial-frame">
-    序列帧
+    <button
+      v-if="!selectedIcon"
+      class="add-icon"
+      @click="isShowSelectionWindow = true"
+    >
+      <img src="@/assets/images/default/hotspot_scene_add.png" alt="">
+      <div class="button-name">添加图标</div>
+      <div class="tip">最大宽度:300px,高度不限</div>
+    </button>
+
+    <div v-if="selectedIcon" class="icon-selected">
+      <div
+        class="icon-wrap"
+        :style="{
+          height: frameHeight ? frameHeight + 'px' : '',
+          width: frameWidth ? frameWidth + 'px' : '',
+        }"
+      >
+        <img
+          class="serial-frame-preview"
+          ref="serial-frame-preview"
+          :src="selectedIcon"
+          alt=""
+          :style="{
+            top: -this.frameHeight * this.currentFrameIdx + 'px',
+          }"
+        >
+        <button class="delete-btn" @click="onClickDelete">
+          <img class="normal" src="@/assets/images/icons/close01_normal@2x.png" alt="">
+          <img class="hover" src="@/assets/images/icons/close01_hover@2x.png" alt="">
+        </button>
+      </div>
+      <div class="right-wrap">
+        <button class="select-icon ui-button submit" @click="isShowSelectionWindow = true">选择图标</button>
+        <div class="tip">最大宽度:300px,高度不限</div>
+      </div>
+    </div>
+
+    <div class="frame-num-setting serial-setting-item">
+      <div class="remark">序列帧总帧数</div>
+      <div class="right-wrap">
+        <input
+          v-model.trim.number="frameNumber"
+          @blur="onFrameNumberInputBlur"
+          type="number"
+          min="2"
+          step="1"
+        />
+        <span>帧</span>
+      </div>
+    </div>
+
+    <div class="duration-setting serial-setting-item">
+      <div class="remark">总播放时长</div>
+      <div class="right-wrap">
+        <input
+          v-model.trim.number="duration"
+          @blur="onDurationInputBlur"
+          type="number"
+          min="0"
+        />
+        <span>秒</span>
+      </div>
+    </div>
+
+    <div class="dialog" style="z-index: 2000" v-if="isShowSelectionWindow">
+      <MaterialSelector
+        :title="$i18n.t(`gather.select_material`)"
+        @cancle="isShowSelectionWindow = false"
+        @submit="handleSubmitFromMaterialSelector"
+        :selectableType="['image']"
+      />
+    </div>
   </div>
-</template>
+</template>
+
+<script>
+import { mapGetters } from "vuex";
+
+import MaterialSelector from "@/components/materialSelector.vue";
+
+export default {
+  components: {
+    MaterialSelector,
+  },
+  data() {
+    return {
+      selectedIcon: null,
+      isShowSelectionWindow: false,
+
+      frameNumber: 30,
+      duration: 3,
+
+      frameHeight: 0,
+      frameWidth: 0,
+      currentFrameIdx: 0,
+      intervalId: null,
+    }
+  },
+  computed: {
+  },
+  watch: {
+    frameNumber: {
+      handler(vNew) {
+        if (vNew >= 2 && Number.isInteger(vNew)) {
+          this.computeFrameHeight()
+        }
+      },
+      immediate: true,
+    },
+    frameHeight: {
+      handler() {
+        this.playSerialFrame()
+      },
+      immediate: true,
+    },
+    duration: {
+      handler() {
+        this.playSerialFrame()
+      },
+      immediate: true,
+    },
+  },
+  methods: {
+    onFrameNumberInputBlur() {
+      if (this.frameNumber < 2) {
+        this.frameNumber = 2
+      } else {
+        this.frameNumber = Math.round(this.frameNumber)
+      }
+    },
+    onDurationInputBlur() {
+      if (this.duration < 0) {
+        this.duration = 0
+      }
+    },
+    handleSubmitFromMaterialSelector(data) {
+      this.isShowSelectionWindow = false
+      this.selectedIcon = data[0].icon
+
+      this.computeFrameHeight()
+    },
+    computeFrameHeight() {
+      this.frameHeight = 0
+      this.frameWidth = 0
+
+      setTimeout(() => {
+        try {
+          this.frameHeight = this.$refs['serial-frame-preview'].clientHeight / this.frameNumber
+          if (this.frameHeight > 110) {
+            this.frameWidth = 110 * 110 / this.frameHeight
+            this.frameHeight = 110
+          }
+        } catch(e) {
+          this.frameHeight = 0
+          this.frameWidth = 0
+        }
+      }, 200);
+    },
+    playSerialFrame() {
+      clearInterval(this.intervalId)
+      this.currentFrameIdx = 0
+      if (!this.selectedIcon || this.frameHeight <= 0 || this.duration <= 0) {
+        return
+      }
+      this.intervalId = setInterval(() => {
+        this.currentFrameIdx++
+        if (this.currentFrameIdx >= this.frameNumber) {
+          this.currentFrameIdx = 0
+        }
+      }, this.duration / this.frameNumber * 1000);
+      this.$emit('addHotspot', {
+        type: 'serial_frame',
+        img: this.selectedIcon,
+        serialFrameInfo: {
+          frameNumber: this.frameNumber,
+          duration: this.duration
+        }
+      })
+
+    },
+    onClickDelete() {
+      this.selectedIcon = null
+      clearInterval(this.intervalId)
+      this.frameHeight = 0
+      this.frameWidth = 0
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.hotspot-icon-serial-frame {
+  width: 100%;
+  
+  > button.add-icon {
+    width: 100%;
+    height: 110px;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    background: #1A1B1D;
+    border-radius: 2px;
+    border: 1px solid #404040;
+    cursor: pointer;
+    > img {
+      width: 30px;
+      height: 30px;
+      margin-bottom: 6px;
+    }
+    > .button-name {
+      font-size: 14px;
+      color: #0076F6;
+      margin-bottom: 4px;
+    }
+    > .tip {
+      font-size: 12px;
+      color: rgba(255, 255, 255, 0.3);
+    }
+  }
+
+  > .icon-selected {
+    display: flex;
+    justify-content: space-between;
+    > .icon-wrap {
+      width: 110px;
+      height: 110px;
+      position: relative;
+      overflow: hidden;
+      border-radius: 2px;
+      > .serial-frame-preview {
+        position: absolute;
+        width: 100%;
+      }
+      > .delete-btn {
+        position: absolute;
+        top: 0;
+        right: 0;
+        width: 20px;
+        height: 20px;
+        background: none;
+        border: none;
+        padding: 0;
+        cursor: pointer;
+        &:hover {
+          .normal {
+            display: none;
+          }
+          .hover {
+            display: initial;
+          }
+        }
+        > .normal {
+          display: initial;
+          width: 100%;
+          height: 100%;
+        }
+        > .hover {
+          display: none;
+          width: 100%;
+          height: 100%;
+        }
+      }
+    }
+    > .right-wrap {
+      width: 108px;
+      > button.select-icon {
+        margin-bottom: 14px;
+      }
+      > .tip {
+        font-size: 14px;
+        color: rgba(255,255,255,0.3);
+      }
+    }
+  }
+
+  > .serial-setting-item {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    &:first-of-type {
+      margin-top: 16px;
+      margin-bottom: 10px;;
+    }
+    > .remark {
+      font-size: 14px;
+      color: #ababab;
+    }
+    > .right-wrap {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      > input {
+        width: 60px;
+        height: 36px;
+        background: #1A1B1D;
+        border-radius: 2px;
+        border: 1px solid #404040;
+        font-size: 14px;
+        color: #FFFFFF;
+        margin-right: 10px;
+        padding-left: 16px;
+        &:focus {
+          border-color: @color;
+        }
+      }
+      > span {
+        font-size: 14px;
+        color: #ababab;
+      }
+    }
+  }
+}
+</style>