gemercheung 2 年之前
父节点
当前提交
f5bd661e28

+ 143 - 6
packages/qjkankan-kankan-view/src/components/Tags/metas/metas-image.vue

@@ -10,14 +10,18 @@
                 <ui-icon type="right"></ui-icon>
             </div>
         </div>
-        <div class="over-box">
+        <div class="over-box" ref="containerRef">
             <div v-show="!loading" class="image-list" :style="`transform:translateX(${-100 * imageNum}%);`">
-                <div
+                <!-- <div
                     @click="openScale(i.src)"
                     :style="`transform:translateX(${100 * index}%);background-image:url(${common.changeUrl(i.src)});`"
                     class="image-item"
                     v-for="(i, index) in imageList"
-                ></div>
+                ></div> -->
+                <div @click="openScale(i.src)" :style="`transform:translateX(${100 * index}%);`" class="image-item" v-for="(i, index) in imageList">
+                    <img :id="`domImg${index}`" :src="common.changeUrl(i.src)" alt="" />
+                </div>
+
                 <!-- <div v-else :style="`transform:translateX(${100 * index}%);`" class="image-item" v-for="(i, index) in imageList">
                     <img @error="filesError(index)" :src="common.changeUrl(i.src)" alt="" />
                 </div> -->
@@ -120,12 +124,13 @@ const chengeImgae = type => {
     } else {
         imageNum.value++
     }
+    if (props.viewer && !isMobile.value) {
+        resetScale()
+    }
 }
 const hanlderFiles = data => {
-    console.log(data[0])
     // store.commit('tag/setImageList', data[0])
     setImageList(data[0])
-    console.log(imageList.value.length, data[0].length - 1)
     // if (imageNum.value < imageList.value.length + data[0].length - 1) {
     //     imageNum.value = imageList.value.length + data[0].length - 1
     // }
@@ -153,6 +158,128 @@ const setImageList = data => {
     }
     store.commit('tag/setImageList', list)
 }
+let result = { width: 0, height: 0 },
+    x,
+    y,
+    scale = 1,
+    minScale = 0.5,
+    maxScale = 4,
+    isPointerdown = false, // 按下标识
+    diff = { x: 0, y: 0 }, // 相对于上一次pointermove移动差值
+    lastPointermove = { x: 0, y: 0 }, // 用于计算diff
+    transform = { x: 0, y: 0 }
+
+const drag = image => {
+    // 绑定 pointerdown
+    image.addEventListener('pointerdown', function (e) {
+        isPointerdown = true
+        image.setPointerCapture(e.pointerId)
+        lastPointermove = { x: e.clientX, y: e.clientY }
+    })
+    // 绑定 pointermove
+    image.addEventListener('pointermove', function (e) {
+        if (isPointerdown) {
+            const current = { x: e.clientX, y: e.clientY }
+            diff.x = current.x - lastPointermove.x
+            diff.y = current.y - lastPointermove.y
+            lastPointermove = { x: current.x, y: current.y }
+            x += diff.x
+            y += diff.y
+
+            image.style.transform = 'translate3d(' + x + 'px, ' + y + 'px, 0) scale(' + scale + ')'
+            // log.innerHTML = `x = ${x.toFixed(0)}<br>y = ${y.toFixed(0)}<br>scale = ${scale.toFixed(5)}`
+        }
+        e.preventDefault()
+    })
+    // 绑定 pointerup
+    image.addEventListener('pointerup', function (e) {
+        if (isPointerdown) {
+            isPointerdown = false
+        }
+    })
+    // 绑定 pointercancel
+    image.addEventListener('pointercancel', function (e) {
+        if (isPointerdown) {
+            isPointerdown = false
+        }
+    })
+}
+const containerRef = ref(null)
+
+const zoomFun = e => {
+    let ratio = 1.1
+    // 缩小
+    if (e.deltaY > 0) {
+        ratio = 1 / 1.1
+    }
+    const _scale = scale * ratio
+    if (_scale > maxScale) {
+        ratio = maxScale / scale
+        scale = maxScale
+    } else if (_scale < minScale) {
+        ratio = minScale / scale
+        scale = minScale
+    } else {
+        scale = _scale
+    }
+    // 目标元素是img说明鼠标在img上,以鼠标位置为缩放中心,否则默认以图片中心点为缩放中心
+    console.log(e.target.tagName)
+    if (e.target.tagName === 'IMG') {
+        const origin = {
+            x: (ratio - 1) * result.width * 0.5,
+            y: (ratio - 1) * result.height * 0.5,
+        }
+        // 计算偏移量
+        x -= (ratio - 1) * (e.clientX - x) - origin.x
+        y -= (ratio - 1) * (e.clientY - y) - origin.y
+    }
+    imgEle.style.transform = 'translate3d(' + x + 'px, ' + y + 'px, 0) scale(' + scale + ')'
+    // log.innerHTML = `x = ${x.toFixed(0)}<br>y = ${y.toFixed(0)}<br>scale = ${scale.toFixed(5)}`
+    e.preventDefault()
+}
+
+// 滚轮缩放
+const initWheelZoom = imgEle => {
+    containerRef.value.addEventListener('wheel', zoomFun)
+}
+const removeWheelZoom = () => {
+    containerRef.value.removeEventListener('wheel', zoomFun)
+}
+
+const resetScale = () => {
+    result = { width: 0, height: 0 }
+    x = null
+    y = null
+    scale = 1
+    minScale = 0.5
+    maxScale = 4
+    isPointerdown = false // 按下标识
+    diff = { x: 0, y: 0 } // 相对于上一次pointermove移动差值
+    lastPointermove = { x: 0, y: 0 } // 用于计算diff
+
+    nextTick(() => {
+        removeWheelZoom()
+        initScale()
+    })
+}
+let imgEle = null
+const initScale = () => {
+    imgEle = document.getElementById(`domImg${imageNum.value}`)
+
+    x = 0
+    y = 0
+    imgEle.style.transform = 'translate3d(' + x + 'px, ' + y + 'px, 0) scale(1)'
+
+    result.width = imgEle.width
+    result.height = imgEle.height
+
+    nextTick(() => {
+        // 拖拽查看
+        drag(imgEle)
+        // 滚轮缩放
+        initWheelZoom(imgEle)
+    })
+}
 onMounted(async () => {
     const app = await useApp()
     isMobile.value = app.config.mobile
@@ -161,6 +288,13 @@ onMounted(async () => {
         let img = new Image()
         img.onload = () => {
             loading.value = false
+
+            if (props.viewer && !isMobile.value) {
+                //pc端缩放
+                nextTick(() => {
+                    initScale()
+                })
+            }
         }
         img.src = common.changeUrl(imageList.value[0].src)
         // if (imageList.value.length > 1) {
@@ -374,9 +508,12 @@ const openScale = src => {
             background-repeat: no-repeat;
             background-size: contain;
             background-position: center;
+            overflow: hidden;
             img {
                 height: 100%;
-                width: auto;
+                width: 100%;
+                object-fit: contain;
+                touch-action: none;
             }
         }
     }

+ 149 - 125
packages/qjkankan-kankan-view/src/components/Tags/tag-view.vue

@@ -1,154 +1,178 @@
 <!--  -->
 <template>
-    <teleport to="body">
-        <div class="tag-layer" @click.stop="" :class="{ mobile: isMobile, full: hotData.type == 'image' }">
-            <div class="tag-info" @click.stop="" id="tag-info">
-                <ui-icon class="close-btn" @click.stop="close" type="close"></ui-icon>
-                <div class="tag-metas" v-if="hotData.media[hotData.type].length > 0 && hotData.type != 'audio'">
-                    <metasImage @close="close" :scale="true" :viewer="true" v-if="hotData.type == 'image' && !isMobile" />
-                    <imageView v-if="hotData.type == 'image' && isMobile"></imageView>
-                    <metasWeb v-if="hotData.type == 'link'" />
-                </div>
-            </div>
+  <teleport to="body">
+    <div
+      class="tag-layer"
+      @click.stop=""
+      :class="{ mobile: isMobile, full: hotData.type == 'image' }"
+    >
+      <div class="tag-info" @click.stop="" id="tag-info">
+        <ui-icon class="close-btn" @click.stop="close" type="close"></ui-icon>
+        <div
+          class="tag-metas"
+          v-if="
+            hotData.media[hotData.type].length > 0 && hotData.type != 'audio'
+          "
+        >
+          <metasImage
+            @close="close"
+            :scale="true"
+            :viewer="true"
+            v-if="hotData.type == 'image' && !isMobile"
+          />
+          <imageView v-if="hotData.type == 'image' && isMobile"></imageView>
+          <metasWeb v-if="hotData.type == 'link'" />
         </div>
-    </teleport>
+      </div>
+    </div>
+  </teleport>
 </template>
 
 <script setup>
-import { reactive, defineEmits, onBeforeMount, onMounted, ref, watchEffect, computed, watch, nextTick } from 'vue'
-import { Dialog } from '@/global_components'
-import metasImage from './metas/metas-image'
-import imageView from './metas/image-view'
+import {
+  reactive,
+  defineEmits,
+  onBeforeMount,
+  onMounted,
+  ref,
+  watchEffect,
+  computed,
+  watch,
+  nextTick,
+} from "vue";
+import { Dialog } from "@/global_components";
+import metasImage from "./metas/metas-image";
+import imageView from "./metas/image-view";
 
-import metasWeb from './metas/metas-web'
-import common from '@/utils/common'
-import { useStore } from 'vuex'
-import { getApp, useApp } from '@/app'
-const isMobile = ref(false)
-const metasHeight = ref(0)
+import metasWeb from "./metas/metas-web";
+import common from "@/utils/common";
+import { useStore } from "vuex";
+import { getApp, useApp } from "@/app";
+const isMobile = ref(false);
+const metasHeight = ref(0);
 onMounted(async () => {
-    const app = await useApp()
-    isMobile.value = app.config.mobile
-    nextTick(() => {
-        let Layer = document.getElementById('tag-info')
-        let layerHeight = Layer.getBoundingClientRect().height
-        metasHeight.value = layerHeight * 0.85
-    })
-})
-import { useMusicPlayer } from '@/utils/sound'
-const musicPlayer = useMusicPlayer()
-const store = useStore()
-const emit = defineEmits(['close'])
+  const app = await useApp();
+  isMobile.value = app.config.mobile;
+  nextTick(() => {
+    let Layer = document.getElementById("tag-info");
+    let layerHeight = Layer.getBoundingClientRect().height;
+    metasHeight.value = layerHeight * 0.85;
+  });
+});
+import { useMusicPlayer } from "@/utils/sound";
+const musicPlayer = useMusicPlayer();
+const store = useStore();
+const emit = defineEmits(["close"]);
 const hotData = computed(() => {
-    let data = store.getters['tag/hotData']
-    if ((data && data.type == 'audio') || (data && data.type == 'video')) {
-        // musicPlayer.pause(true)
-    }
-    return store.getters['tag/hotData']
-})
+  let data = store.getters["tag/hotData"];
+  if ((data && data.type == "audio") || (data && data.type == "video")) {
+    // musicPlayer.pause(true)
+  }
+  return store.getters["tag/hotData"];
+});
 
 const audioInfo = computed(() => {
-    return hotData.value.media.audio
-})
-const audio = ref(null)
+  return hotData.value.media.audio;
+});
+const audio = ref(null);
 watchEffect(() => {
-    if (audio.value) {
-        audio.value.play()
-    }
-})
+  if (audio.value) {
+    audio.value.play();
+  }
+});
 
 const close = () => {
-    emit('close')
-}
-onMounted(() => {})
+  emit("close");
+};
+onMounted(() => {});
 </script>
 <style lang="scss">
 .tag-layer {
-    width: 100vw;
-    height: 100vh;
-    z-index: 10000;
-    top: 0;
-    position: fixed;
+  width: 100vw;
+  height: 100vh;
+  z-index: 10000;
+  top: 0;
+  position: fixed;
+  left: 0;
+  // padding: calc(var(--editor-head-height) + 20px) calc(var(--editor-toolbox-width) + 20px) 60px calc(var(--editor-menu-width) + 20px);
+  background-color: rgba(0, 0, 0, 0.7);
+
+  .tag-info {
+    color: #fff;
+    width: 100%;
+    height: 85%;
+    position: absolute;
+    top: 7.5%;
     left: 0;
-    // padding: calc(var(--editor-head-height) + 20px) calc(var(--editor-toolbox-width) + 20px) 60px calc(var(--editor-menu-width) + 20px);
-    background-color: rgba(0, 0, 0, 0.7);
+    .close-btn {
+      position: fixed;
+      right: 36px;
+      top: 36px;
+      font-size: 18px;
+      cursor: pointer;
+      z-index: 100;
+    }
+    .tag-metas {
+      width: 100%;
+      height: 100%;
+      position: relative;
 
-    .tag-info {
-        color: #fff;
+      .pic-box {
         width: 100%;
-        height: 85%;
-        position: absolute;
-        top: 7.5%;
-        left: 0;
-        .close-btn {
-            position: fixed;
-            right: 36px;
-            top: 36px;
-            font-size: 18px;
-            cursor: pointer;
-            z-index: 100;
-        }
-        .tag-metas {
-            width: 100%;
-            height: 100%;
-            position: relative;
-
-            .pic-box {
-                width: 100%;
-                height: 100%;
-                border: none;
+        height: 100%;
+        border: none;
 
-                .image-list {
-                    .image-item {
-                        background-size: contain;
-                    }
-                }
-            }
-            .video-box {
-                height: auto;
-                border: none;
-                video {
-                    width: 100%;
-                    height: auto;
-                    object-fit: contain;
-                }
-            }
-            .web-box {
-                // height: 500px;
-                width: 91%;
-                height: 100%;
-                border: none;
-                left: 50%;
-                transform: translateX(-50%);
-            }
+        .image-list {
+          .image-item {
+            background-size: contain;
+          }
         }
+      }
+      .video-box {
+        height: auto;
+        border: none;
+        video {
+          width: 100%;
+          height: auto;
+          object-fit: contain;
+        }
+      }
+      .web-box {
+        // height: 500px;
+        width: 91%;
+        height: 100%;
+        border: none;
+        left: 50%;
+        transform: translateX(-50%);
+      }
     }
+  }
 }
 [is-mobile] {
-    .tag-layer {
-        .tag-info {
-            .close-btn {
-                position: absolute;
-                right: 20px;
-                top: -30px;
-                font-size: 18px;
-                cursor: pointer;
-            }
-        }
-        &.full {
-            background-color: #141414;
-            .tag-info {
-                height: 100%;
-                top: 0;
-                .close-btn {
-                    position: absolute;
-                    right: 20px;
-                    top: 20px;
-                    font-size: 18px;
-                    cursor: pointer;
-                }
-            }
+  .tag-layer {
+    .tag-info {
+      .close-btn {
+        position: absolute;
+        right: 20px;
+        top: -30px;
+        font-size: 18px;
+        cursor: pointer;
+      }
+    }
+    &.full {
+      background-color: #141414;
+      .tag-info {
+        height: 100%;
+        top: 0;
+        .close-btn {
+          position: absolute;
+          right: 20px;
+          top: 20px;
+          font-size: 18px;
+          cursor: pointer;
         }
+      }
     }
+  }
 }
 </style>

+ 5 - 3
packages/qjkankan-view/src/components/Fdkk/index.vue

@@ -7,7 +7,9 @@
         :key="currentScene.sceneCode"
         v-if="!isMobile"
         id="fdkkifr"
-        :src="`/spc.html?m=${currentScene.sceneCode}&lang=${lang}`"
+        :src="`/spc.html?m=${
+          currentScene.sceneCode
+        }&lang=${lang}`"
         frameborder="0"
       ></iframe>
       <!-- <iframe :key="currentScene.sceneCode" v-if="!isMobile" id="fdkkifr" :src="`https://test.4dkankan.com/spc.html?m=${currentScene.sceneCode}`" -->
@@ -76,11 +78,11 @@ const V4IframeUrl = computed(() => {
   if (process.env.VUE_APP_DEBBUG_V4 == 1) {
     return `${v4Mi}/${unref(isMobile) ? "smg" : "spg"}.html?m=${
       unref(currentScene).sceneCode
-    }&lang=${unref(lang)}`;
+    }&lang=${unref(lang)}&rnd=${Math.floor(Math.random()*100000)}`;
   } else {
     return `${unref(isMobile) ? "smg" : "spg"}.html?m=${
       unref(currentScene).sceneCode
-    }&lang=${unref(lang)}`;
+    }&lang=${unref(lang)}&rnd=${Math.floor(Math.random()*100000)}`;
   }
 });