特性:
- 支持自定义瓦片图尺寸
- 支持显示预览最小尺寸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"><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,//最大支持上传图片文件1Gaccept: `*`,}" @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,view100px: true,loadingText: '',colCount: 0,rowCount: 0,src: '',fileFormat: '',tileSize: 500,tiles: [],//瓦片图数组}},watch: {tileSize(d) {this.tiles = [];this.loading = false;this.loadingText = '';},},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 = colIndex === colCount - 1 ? (img.width % this.tileSize) : this.tileSize;let drawImageHeight = rowIndex === rowCount - 1 ? (img.height % this.tileSize) : this.tileSize;canvas.width = drawImageWidth, canvas.height = drawImageHeight;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(url, f) {this.tiles = [];this.loadingText = '正在为您分解图片,请稍候!';let img = new Image(); img.crossOrigin = 'Anonymous';img.onload = d => {this.getTiles({img,cb: tiles => {this.tiles = tiles;this.loading = false;}})}img.src = url;},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>