特性:
- 支持自定义瓦片图尺寸
- 支持显示预览最小尺寸100x100像素大小,切换为实际切割尺寸
- 支持获取切割后的文件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>