【sgUploadTileImage】自定义组件:浏览器端生成瓦片图,并转换为File文件序列上传瓦片图

简介: 【sgUploadTileImage】自定义组件:浏览器端生成瓦片图,并转换为File文件序列上传瓦片图


特性

  1. 支持自定义瓦片图尺寸
  2. 支持显示预览最小尺寸100x100像素大小,切换为实际切割尺寸
  3. 支持获取切割后的文件Files数组

sgUploadTileImage源码

<template>
  <div :class="$options.name">
    <div class="sg-ctrl">
      <div class="px">
        <span>瓦片图边长</span>
        <el-input-number
          style="width: 130px"
          v-model.trim="tileSize"
          :precision="0"
          :step="100"
          :min="100"
          :max="500"
          :controls-position="`left`"
        /><span>像素</span>
      </div>
      <div class="btns" v-if="!changeSizeloading">
        <el-switch
          v-model="view100px"
          inactive-color="#ccc"
          active-color="#409EFF"
          inactive-text=""
          active-text="固定100宽高显示预览"
          :inactive-value="false"
          :active-value="true"
        />
        <el-button
          :loading="loading"
          type="primary"
          icon="el-icon-upload2"
          @click="(d) => $refs.sgUpload.triggerUploadFile()"
          >上传大图</el-button
        >
        <el-button
          type="success"
          icon="el-icon-s-promotion"
          @click="uploadTiles"
          v-if="tiles.length"
          >上传瓦片图</el-button
        >
      </div>
      <div class="tip-text">
        <p>{{ loadingText }}</p>
      </div>
    </div>
    <div class="sg-tiles" :view100px="view100px">
      <div
        :style="{ width: `${(view100px ? 100 : tileSize) * colCount}px` }"
        v-if="tiles.length"
      >
        <img
          v-for="(a, i) in tiles"
          :key="i"
          :src="a"
          :width="view100px ? 100 : tileSize"
          :height="view100px ? 100 : tileSize"
        />
      </div>
      <el-alert
        v-else
        style="width: 555px; height: 50px; margin-top: 10px"
        :closable="true"
        :close-text="``"
        :description="``"
        :effect="'light'"
        :show-icon="false"
        :title="`温馨提示:请上传超大图片文件(大于100M)`"
        :type="'success'"
      >
      </el-alert>
    </div>
    <!-- 上传组件 -->
    <sgUpload
      :disabledWhenShowSels="['.v-modal']"
      drag
      ref="sgUpload"
      :data="{
        maxSize: 1000, //最大支持上传图片文件1G
        accept: `*`,
      }"
      @resultBase64Image="resultBase64Image"
      @success="uploadSuccess"
      @error="uploadError"
      hideUploadTray
      @showFakeLoading="showFakeLoading"
    />
  </div>
</template>
<script>
import sgUpload from "@/vue/components/admin/sgUpload";
export default {
  name: "sgUploadTileImage",
  components: {
    sgUpload,
  },
  data() {
    return {
      loading: false,
      changeSizeloading: false,
      view100px: true,
      loadingText: "",
      colCount: 0,
      rowCount: 0,
      src: "",
      fileFormat: "",
      tileSize: 500,
      tiles: [], //瓦片图数组
      base64Image: null,
    };
  },
  watch: {
    tileSize(d) {
      if (this.base64Image) {
        this.changeSizeloading = true;
        this.resultBase64Image(this.base64Image, { isTrigger: true });
      } else {
        this.tiles = [];
        this.loadingText = "";
        this.loading = false;
      }
    },
  },
  methods: {
    uploadTiles(d) {
      let r = [];
      let format = this.fileFormat.toLocaleLowerCase().split("/")[1];
      this.tiles.forEach((base64, i) => {
        let fileName = `${i}.${format}`;
        let file = this.$g.image.getFileFromBase64(base64, fileName);
        r.push(file);
      });
      this.$emit(`uploadTiles`, r);
    },
    getRowColIndex(itemIndex = 0, colCount = 3) {
      //必选参数:itemIndex是当前元素的索引值,从0开始计算(如果是从1开始,则需将传入值-1,即itemIndex--)
      //必选参数:colCount是每一行显示多少个元素
      return {
        colIndex: itemIndex % colCount, //计算列索引(从0开始)
        rowIndex: Math.floor(itemIndex / colCount), //计算行索引(从0开始)
      };
    },
    // 获取瓦片图
    getTiles({ img, format = "image/png", cb } = {}) {
      this.fileFormat = format;
      let canvas = document.createElement("canvas"),
        ctx = canvas.getContext("2d");
      (canvas.width = this.tileSize), (canvas.height = this.tileSize);
      let tiles = [];
      let colCount = Math.ceil(img.width / this.tileSize); //列数量
      let rowCount = Math.ceil(img.height / this.tileSize); //行数量
      let len = colCount * rowCount; //瓦片图总数
      this.loadingText = `已经为您生成${len}个瓦片图(尺寸:${this.tileSize}像素×${this.tileSize}像素)。`;
      this.colCount = colCount;
      this.rowCount = rowCount;
      for (let i = 0; i < len; i++) {
        let { colIndex, rowIndex } = this.getRowColIndex(i, colCount);
        let drawImageWidth = this.tileSize;
        //   colIndex === colCount - 1 ? img.width % this.tileSize : this.tileSize;
        let drawImageHeight = this.tileSize;
        //   rowIndex === rowCount - 1 ? img.height % this.tileSize : this.tileSize;
        (canvas.width = drawImageWidth), (canvas.height = drawImageHeight);
        console.log(`drawImageWidth`, drawImageWidth);
        ctx.drawImage(
          img,
          this.tileSize * colIndex,
          this.tileSize * rowIndex, //绘制图片起始点的横纵坐标
          drawImageWidth,
          drawImageHeight, //切割图片的宽高
          0,
          0, //绘制canvas的起始点横纵坐标
          drawImageWidth,
          drawImageHeight
        ); //绘制canvas的宽高
        tiles.push(canvas.toDataURL(format));
        if (i === len - 1) {
          let scale = 100 / this.tileSize;
          this.$el.style.setProperty("--last-img-width", `${drawImageWidth}px`); //js往css传递局部参数
          this.$el.style.setProperty("--last-img-height", `${drawImageHeight}px`); //js往css传递局部参数
          this.$el.style.setProperty(
            "--last-img-width-view100px",
            `${drawImageWidth * scale}px`
          ); //js往css传递局部参数
          this.$el.style.setProperty(
            "--last-img-height-view100px",
            `${drawImageHeight * scale}px`
          ); //js往css传递局部参数
        }
      }
      cb && cb(tiles);
    },
    /* 将图片(路径)转换为Base64 */
    resultBase64Image(base64, { isTrigger } = {}) {
      this.base64Image = base64;
      isTrigger || (this.tiles = []);
      this.loadingText = "正在为您分解图片,请稍候!";
      let img = new Image();
      img.crossOrigin = "Anonymous";
      img.onload = (d) => {
        this.getTiles({
          img,
          cb: (tiles) => {
            this.tiles = tiles;
            this.changeSizeloading = false;
            this.loading = false;
          },
        });
      };
      img.src = base64;
    },
    uploadSuccess(d, f) {},
    uploadError(d, f) {},
    showFakeLoading(d) {
      this.loadingText = "图片上传中,请稍候!";
      this.loading = true;
    },
  },
};
</script>
<style lang="scss" scoped>
.sgUploadTileImage {
  display: flex;
  flex-direction: column;
  .sg-ctrl {
    display: flex;
    flex-shrink: 0;
    flex-wrap: nowrap;
    align-items: center;
    .px {
      margin-right: 10px;
      span {
        margin-left: 5px;
      }
    }
    .tip-text {
      margin-left: 10px;
      display: flex;
      align-items: center;
    }
  }
  .sg-tiles {
    overflow: auto;
    // max-height: calc(100vh - 100px);
    flex-grow: 1;
    div {
      display: flex;
      flex-wrap: wrap;
      img {
        box-sizing: border-box;
        border: 0.5px solid white;
        object-position: top left;
        object-fit: scale-down;
        &:last-of-type {
          border-right: none;
          border-bottom: none;
          object-fit: fill;
          width: var(--last-img-width);
          height: var(--last-img-height);
        }
      }
    }
    &[view100px] {
      div {
        img {
          &:last-of-type {
            width: var(--last-img-width-view100px);
            height: var(--last-img-height-view100px);
          }
        }
      }
    }
  }
}
</style>

用例

<template>
  <div>
    <el-button type="primary" @click="dialogVisible = true">上传超大文件</el-button>
    <el-dialog :custom-class="'sgUploadTileImage-el-dialog'" :append-to-body="true" :close-on-click-modal="true"
      :close-on-press-escape="true" :destroy-on-close="true" :fullscreen="true" :show-close="true" :title="`瓦片图上传`"
      :width="'100%'" :visible.sync="dialogVisible">
      <div style="width: 100%;height: 100%;">
        <sgUploadTileImage @uploadTiles="uploadTiles" />
      </div>
    </el-dialog>
  </div>
</template>
<script>
import sgUploadTileImage from "@/vue/components/admin/sgUploadTileImage";
export default {
  components: { sgUploadTileImage, },
  data() {
    return { dialogVisible: false, }
  },
  methods: {
    uploadTiles(files) {
      console.log(`瓦片图files:`, files);
    },
  },
};
</script> 
<style lang="scss">
.sgUploadTileImage-el-dialog {
  .el-dialog__body {
    padding: 0;
    .sg-tiles {
      max-height: calc(100vh - 100px);
    }
  }
}
</style>


相关文章
|
7月前
|
JavaScript
浏览器插件crx文件--JS混淆与解密
浏览器插件crx文件--JS混淆与解密
193 0
|
7月前
|
Web App开发 前端开发
Chrome 浏览器插件 V3 版本 Manifest.json 文件中 Action 的类型(Types)、方法(Methods)和事件(Events)的属性和参数解析
Chrome 浏览器插件 V3 版本 Manifest.json 文件中 Action 的类型(Types)、方法(Methods)和事件(Events)的属性和参数解析
238 0
|
4月前
|
XML 缓存 JSON
为什么浏览器中有些图片、PDF等文件点击后有些是预览,有些是下载
为什么浏览器中有些图片、PDF等文件点击后有些是预览,有些是下载
299 0
|
1月前
|
Web App开发 缓存 安全
WIN11 Chrome 双击打不开闪退及Chrome浏览器不能拖拽文件crx
【11月更文挑战第6天】本文介绍了 WIN11 系统中 Chrome 浏览器双击打不开闪退及不能拖拽文件 crx 的原因和解决方法。包括浏览器版本过旧、扩展程序冲突、硬件加速问题、缓存过多、安全软件冲突、系统文件损坏、用户配置文件损坏等问题的解决方案,以及 crx 文件的屏蔽、权限问题和文件格式问题的处理方法。
140 2
|
3月前
|
JSON 前端开发 JavaScript
java中post请求调用下载文件接口浏览器未弹窗而是返回一堆json,为啥
客户端调接口需要返回另存为弹窗,下载文件,但是遇到的问题是接口调用成功且不报错,浏览器F12查看居然返回一堆json,而没有另存为弹窗; > 正确的效果应该是:接口调用成功且浏览器F12不返回任何json,而是弹窗另存为窗口,直接保存文件即可。
168 2
|
4月前
|
Web App开发 JSON 数据格式
【Azure Developer】浏览器查看本地数据文件时遇见跨域问题(CORS)
【Azure Developer】浏览器查看本地数据文件时遇见跨域问题(CORS)
【Azure Developer】浏览器查看本地数据文件时遇见跨域问题(CORS)
|
5月前
|
JavaScript
js中readAsDataURL的意思,可以用于浏览器预览图像文件或者转成base64字符串
js中readAsDataURL的意思,可以用于浏览器预览图像文件或者转成base64字符串
js中readAsDataURL的意思,可以用于浏览器预览图像文件或者转成base64字符串
|
5月前
|
JavaScript
vue 组件封装 | 随鼠标移动的信息框 (含监听鼠标移动、移出事件,获取元素的宽高、获取浏览器的宽高)
vue 组件封装 | 随鼠标移动的信息框 (含监听鼠标移动、移出事件,获取元素的宽高、获取浏览器的宽高)
57 1
|
6月前
|
Web App开发 JSON 数据格式
【Azure Developer】浏览器查看本地数据文件时遇见跨域问题(CORS)
Access to XMLHttpRequest at 'file:///C:/Users/.../failedrequests.json' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrome-extension, chrome-untrusted, https, edge. reportdata/failedrequests.json:1 Fail
|
7月前
|
JavaScript
vue自定义浏览器滚动条样式
vue自定义浏览器滚动条样式
60 0