viewerjs+vue3 using typescript

安装包

npm install v-viewer viewerjs
npm i fontawesome-4.7
npm install @fortawesome/fontawesome-svg-core
npm install @fortawesome/free-solid-svg-icons
npm install @fortawesome/vue-fontawesome@prerelease
npm install @fortawesome/free-regular-svg-icons
npm install @fortawesome/free-brands-svg-icons
npm install viewerjs @types/viewerjs --save

  

main.ts

/*
 * @creater: geovindu
 * @since: 2025-06-24 20:03:42
 * @LastAuthor: geovindu
 * @lastTime: 2025-10-31 22:06:13
 * @文件相对于项目的路径: \jsstudy\markmapdemo\src\main.ts
 * @message: geovindu
 * @IDE: vscode
 * @Development: node.js 20, vuejs3.0
 * @package: 
 * @ISO: windows10
 * @database: mysql 8.0 sql server 2019 postgresSQL 16 
 * Copyright (c) 2025 by geovindu email:geovindu@163.com, All Rights Reserved.
 */
import './assets/main.css'
 
import { createApp } from 'vue'
import { createPinia } from 'pinia'
 
import App from './App.vue'
import router from './router'
import Viewer from 'viewerjs';
import "fontawesome-4.7/css/font-awesome.css";
import { library } from '@fortawesome/fontawesome-svg-core';
import { faUserSecret } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
 
import 'viewerjs/dist/viewer.css';
Viewer.setDefaults({
  navbar: true,
  title: true,
  toolbar: {
    prev: true,
    next: true,
  },
});
 
const app = createApp(App)
.component('font-awesome-icon', FontAwesomeIcon)
app.use(createPinia())
app.use(router)
app.use(() => Viewer); 
app.mount('#app')

  

<template>
  <div class="about">
    <h1>This is an about page</h1>
  </div>
<!-- 图片容器 -->
<div ref="viewerContainer" style="margin: 20px;">
    <img
      v-for="src in images"
      :key="src"
      :src="getThumbnail(src)"
      :data-src="src"
      :alt="extractFilename(src)"
      class="viewer-image"
    />
  </div>
 
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import Viewer from 'viewerjs';
import 'viewerjs/dist/viewer.css';
 
// 🔹 定义类型
type ImageSrc = string;
 
// 🔹 图片列表(类型化)
const images = ref<ImageSrc[]>([
  '1.png',
  '2.jpg',
  '3.jpg',
]);
 
// 🔹 提取文件名(带类型)
const extractFilename = (url: string): string => {
  try {
    const pathname = new URL(url, location.origin).pathname;
    const filename = pathname.split('/').pop() || 'image';
    return filename.split('?')[0].split('#')[0]; // 去除查询参数
  } catch (e) {
    const match = url.match(/[^/\\?#]+(?=[^/\\]*$)/);
    return match ? match[0] : 'image';
  }
};
 
// 🔹 缩略图生成函数
const getThumbnail = (url: string): string => {
  if (url.includes('picsum.photos')) {
    return url.replace(/(\d+)\/(\d+)/, '200/150');
  }
  return url;
};
 
// 🔹 Viewer 容器引用
const viewerContainer = ref<HTMLDivElement | null>(null);
let viewer: Viewer | null = null;
 
// 🔹 下载处理函数
const handleDownload = (): void => {
  if (!viewer || !viewer.image) return;
 
  const currentImage = viewer.image;
  const filename = extractFilename(currentImage.src);
 
  const tempImage = new Image();
  tempImage.crossOrigin = 'Anonymous';
 
  tempImage.onload = () => {
    const canvas = document.createElement('canvas');
    canvas.width = tempImage.width;
    canvas.height = tempImage.height;
    const ctx = canvas.getContext('2d');
    if (!ctx) return;
 
    ctx.drawImage(tempImage, 0, 0);
 
    const ext = filename.split('.').pop()?.toLowerCase() || 'jpeg';
    const mimeTypeMap: Record<string, string> = {
      jpg: 'image/jpeg',
      jpeg: 'image/jpeg',
      png: 'image/png',
      gif: 'image/gif',
      webp: 'image/webp',
      bmp: 'image/bmp',
    };
    const mimeType = mimeTypeMap[ext] || 'image/jpeg';
 
    const dataUrl = canvas.toDataURL(mimeType, 0.95);
 
    const a = document.createElement('a');
    a.href = dataUrl;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
  };
 
  tempImage.onerror = () => {
    const a = document.createElement('a');
    a.href = currentImage.src;
    a.download = filename;
    a.target = '_blank';
    a.rel = 'noopener noreferrer';
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
  };
 
  tempImage.src = currentImage.src;
};
 
// 🔹 组件生命周期
onMounted(() => {
  if (viewerContainer.value) {
    viewer = new Viewer(viewerContainer.value, {
      url: 'data-src',
      toolbar: {
        zoomIn: true,
        zoomOut: true,
        oneToOne: true,
        reset: true,
        prev: true,
        next: true,
        download: {
          show: true,
          //size: 'large',
          click() {
            handleDownload();
          },
        },
      },
      navbar: true,
      title: (image: HTMLImageElement) => extractFilename(image.src),
      viewed() {
        console.log('查看:', extractFilename(viewer?.image.src || ''));
      },
    });
  }
});
 
onUnmounted(() => {
  if (viewer) {
    viewer.destroy();
    viewer = null;
  }
});
</script>
<style>
@media (min-width: 1024px) {
  .about {
    min-height: 100vh;
    display: flex;
    align-items: center;
  }
}
</style>

  

输出:

c1a72cfd5fcc1a2591e5fbaae74a91ed

 

posted @ 2025-10-31 22:12  ®Geovin Du Dream Park™  阅读(0)  评论(0)    收藏  举报