提交 6b07a81b 作者: 方治民

feat: 重构雪碧图生成小工具的应用

上级 409055e9
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -6,7 +6,7 @@
<title>生成Mapbox Sprite(精灵图)</title>
<!-- https://www.jianshu.com/p/f477fa2111f7 -->
<!-- import CSS -->
<link href="https://cdn.bootcss.com/element-ui/2.4.5/theme-chalk/index.css" rel="stylesheet">
<link href="./element-ui/2.4.5/theme-chalk/index.css" rel="stylesheet">
<style>
html, body, #app{
width: 100%;
......@@ -25,6 +25,50 @@
display: flex;
align-items:center;
justify-content:center;
flex-direction: column;
}
.option-group {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 20px;
}
.option-item {
margin: 5px 0;
}
.output-controls {
display: flex;
flex-direction: column;
align-items: center;
margin: 10px 0;
}
.download-buttons {
display: flex;
gap: 10px;
flex-wrap: wrap;
justify-content: center;
}
.img-container {
display: flex;
height: 100%;
}
.img-preview {
flex: 1;
padding: 5px;
display: flex;
flex-direction: column;
align-items: center;
}
.preview-label {
text-align: center;
font-size: 12px;
margin-bottom: 5px;
}
.preview-img {
border: 1px solid #6f6f6f;
max-width: 100%;
max-height: calc(100% - 20px);
object-fit: contain;
}
</style>
</head>
......@@ -35,34 +79,87 @@
<el-upload
action="https://jsonplaceholder.typicode.com/posts/"
list-type="picture-card"
accept="image/*"
:before-upload="beforeUpload"
accept="image/*,.svg"
:on-preview="handlePreview"
:on-success="handleSuccess"
:on-remove="handleRemove" multiple>
:on-remove="handleRemove"
:file-list="fileList"
multiple>
<i class="el-icon-plus"></i>
<div slot="tip" class="el-upload__tip">只能上传图片格式文件,且不超过500kb</div>
<div slot="tip" class="el-upload__tip">支持PNG、JPG、SVG等图片格式,建议上传矢量图(SVG)</div>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
</el-col>
<el-col class="but" :span="2">
<el-button type="primary" @click="image">转换</el-button>
<div class="option-group">
<el-button type="primary" @click="image" :loading="processing">转换</el-button>
<div style="margin: 20px 0; text-align: center;">
<span>输出选项:</span>
</div>
<div class="option-item">
<el-checkbox v-model="output1x">标准分辨率 (1x)</el-checkbox>
</div>
<div class="option-item">
<el-checkbox v-model="output2x">高清分辨率 (@2x)</el-checkbox>
</div>
</div>
</el-col>
<el-col class="creat" :span="11">
<el-row style="height: 45%">
<img :src="editURL" style="border:1px solid #6f6f6f">
<el-row style="height: 40%">
<div class="img-container">
<div v-if="editURL" class="img-preview">
<div class="preview-label">标准图 (1x)</div>
<img :src="editURL" class="preview-img">
</div>
<div v-if="editURL2x" class="img-preview">
<div class="preview-label">高清图 (@2x)</div>
<img :src="editURL2x" class="preview-img">
</div>
</div>
</el-row>
<el-row style="height: 10%">
<el-button type="primary" @click="downloadImg">下载图片</el-button>
<el-button type="primary" @click="downloadJSON">下载JSON</el-button>
<el-row style="height: 20%; padding: 10px;">
<div class="output-controls">
<el-alert
v-if="showWarning"
title="提示"
type="warning"
:description="warningMessage"
show-icon
:closable="false"
style="margin-bottom: 10px;">
</el-alert>
<div class="download-buttons">
<el-button type="primary" @click="downloadImg('1x')"
:disabled="!editURL">下载图片 (1x)</el-button>
<el-button type="primary" @click="downloadJSON('1x')"
:disabled="!spritejson">下载JSON (1x)</el-button>
<el-button type="primary" @click="downloadImg('2x')"
:disabled="!editURL2x">下载图片 (@2x)</el-button>
<el-button type="primary" @click="downloadJSON('2x')"
:disabled="!spritejson2x">下载JSON (@2x)</el-button>
</div>
</div>
</el-row>
<el-row style="height: 45%">
<el-input
type="textarea"
:rows="18"
placeholder="JSON内容"
v-model="spritejson"></el-input>
<el-row style="height: 40%">
<el-tabs v-model="activeTab" style="width: 100%; height: 100%;">
<el-tab-pane label="标准JSON (1x)" name="1x">
<el-input
type="textarea"
:rows="8"
placeholder="JSON内容"
v-model="spritejson"></el-input>
</el-tab-pane>
<el-tab-pane label="高清JSON (@2x)" name="2x">
<el-input
type="textarea"
:rows="8"
placeholder="JSON内容"
v-model="spritejson2x"></el-input>
</el-tab-pane>
</el-tabs>
</el-row>
</el-col>
</el-row>
......@@ -71,7 +168,7 @@
<!-- import Vue before Element -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<!-- import JavaScript -->
<script src="https://cdn.bootcss.com/element-ui/2.4.5/index.js"></script>
<script src="./element-ui/2.4.5/index.js"></script>
<script>
new Vue({
el: '#app',
......@@ -81,11 +178,19 @@
dialogVisible: false,
disabled: false,
fileList: [],
canvas: [],
paramList: [],//List<Param>
rowparams: [],//List<List<Param>>
editURL:'',
spritejson:''
paramList: [],
rowparams: [],
editURL: '',
editURL2x: '',
spritejson: '',
spritejson2x: '',
output1x: true,
output2x: true,
activeTab: '1x',
processing: false,
showWarning: false,
warningMessage: '',
loadedImages: []
}
},
mounted() {
......@@ -101,143 +206,306 @@
handleSuccess(response, file, fileList) {
this.fileList = fileList;
},
image() {
let paramlist = [];
if (this.fileList.length === 0) return;
this.fileList.forEach(file => {
let image = new Image();
image.src = file.url;
let canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
canvas.getContext("2d").drawImage(image, 0, 0);
paramlist.push({
name: file.name.split(".")[0],
x: 0,
y: 0,
width: image.width,
height: image.height,
canvas: canvas
})
})
paramlist.sort(function (a, b) {
return a.height - b.height
})
this.paramList = paramlist;
this.creatSprite(paramlist);
beforeUpload(file) {
return true;
},
async image() {
if (this.fileList.length === 0) {
this.$message.warning('请先上传图片');
return;
}
this.processing = true;
this.showWarning = false;
this.editURL = '';
this.editURL2x = '';
this.spritejson = '';
this.spritejson2x = '';
try {
const paramlist = await this.loadImages();
this.processImages(paramlist);
} catch (error) {
console.error('图片处理失败:', error);
this.$message.error('图片处理失败: ' + error.message);
} finally {
this.processing = false;
}
},
loadImages() {
return new Promise((resolve) => {
const paramlist = [];
let loadedCount = 0;
this.fileList.forEach(file => {
const reader = new FileReader();
const fileName = file.name.split(".")[0];
reader.onload = (e) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, img.width, img.height);
paramlist.push({
name: fileName, // 使用原始文件名,不修改
width: img.width,
height: img.height,
canvas: canvas,
img: img,
isSvg: file.type === 'image/svg+xml' || file.name.toLowerCase().endsWith('.svg')
});
loadedCount++;
if (loadedCount === this.fileList.length) {
resolve(paramlist);
}
};
img.onerror = () => {
this.$message.error(`图片 ${file.name} 加载失败`);
loadedCount++;
if (loadedCount === this.fileList.length) {
resolve(paramlist);
}
};
img.src = e.target.result;
};
reader.readAsDataURL(file.raw || file);
});
});
},
creatSprite(paramlist) {
//图片默认宽度为255
let allwidth = 255;
let rowparams = [], paramnowlist = [];
let countnum = 0;
for (let i = 0; i < paramlist.length; i++) {
countnum += paramlist[i].width;
if (countnum > allwidth) {
i = i - 1;
countnum = 0;
rowparams.push(paramnowlist);
paramnowlist = [];
processImages(paramlist) {
if (paramlist.length === 0) {
this.$message.warning('没有可处理的图片');
return;
}
// 按高度排序
paramlist.sort((a, b) => b.height - a.height);
if (this.output1x) {
this.creatSprite(paramlist, 1);
}
if (this.output2x) {
this.creatSprite(paramlist, 2);
}
},
creatSprite(paramlist, scale) {
if (!paramlist || paramlist.length === 0) return;
const layout = this.calculateLayout(paramlist, scale);
const { canvasWidth, canvasHeight, items } = layout;
let spritejson = "{\n";
const editMap = document.createElement('canvas');
editMap.width = canvasWidth;
editMap.height = canvasHeight;
const editCxt = editMap.getContext("2d");
// 设置画布为透明背景
editCxt.fillStyle = 'rgba(0,0,0,0)';
editCxt.fillRect(0, 0, canvasWidth, canvasHeight);
items.forEach((item, index) => {
const { img, x, y, width, height } = item;
// 如果是SVG图片,使用原始图片重新绘制以确保清晰
if (img.isSvg && scale > 1) {
// 创建临时canvas来绘制SVG
const tempCanvas = document.createElement('canvas');
tempCanvas.width = width;
tempCanvas.height = height;
const tempCtx = tempCanvas.getContext("2d");
// 高质量绘制SVG
tempCtx.imageSmoothingEnabled = false;
tempCtx.imageSmoothingQuality = 'high';
// 重新绘制SVG
tempCtx.drawImage(img.img, 0, 0, width, height);
// 绘制到主canvas
editCxt.drawImage(tempCanvas, x, y);
} else {
paramnowlist.push(paramlist[i]);
// 普通图片
editCxt.drawImage(img.canvas, x, y, width, height);
}
if (i === paramlist.length - 1) {
rowparams.push(paramnowlist);
break;
// 生成JSON,使用原始文件名
spritejson += ' "' + img.name + '": ' + JSON.stringify({
width: width,
height: height,
x: x,
y: y,
pixelRatio: scale === 2 ? 2 : 1
}, null, 2).replace(/\n/g, '\n ');
if (index < items.length - 1) {
spritejson += ",\n";
} else {
spritejson += "\n";
}
});
spritejson += "}";
if (scale === 1) {
this.editURL = editMap.toDataURL("image/png");
this.spritejson = spritejson;
} else {
this.editURL2x = editMap.toDataURL("image/png");
this.spritejson2x = spritejson;
}
//计算应有的高度
let allheight = 0;
rowparams.forEach(item => {
allheight += Math.max.apply(Math, item.map(m => m.height));
})
//计算应有的宽度
allwidth = 0
rowparams[0].forEach(item => {
allwidth += item.width;
})
if (allwidth > 200) allwidth = 255;
console.log(allwidth)
let spritejson = "{\n";
//开始画大图
let editMap = document.createElement('canvas');
editMap.width = allwidth;
editMap.height = allheight;
let editCxt = editMap.getContext("2d");
let editImageData = editCxt.getImageData(0, 0, allwidth, allheight);
//保存起始高度
let heighttemp = 0;
for (let i = 0; i < rowparams.length; i++) {
let tempwidthnum = 0;
for (let j = 0; j < rowparams[i].length; j++) {
let map = rowparams[i][j].canvas;
//循环小图片
for (let x = 0; x < map.width; x++) {
for (let y = 0; y < map.height; y++) {
//获取像素
let color = this.getXY(map, x, y);
this.setXY(editImageData, x + tempwidthnum, y + heighttemp, color);
}
},
calculateLayout(paramlist, scale) {
const baseWidth = scale === 1 ? 255 : 510;
const rowSpacing = 2;
const colSpacing = 2;
const items = paramlist.map(img => ({
img: img,
width: img.width * (img.isSvg ? scale : 1), // SVG按比例缩放,位图保持原尺寸
height: img.height * (img.isSvg ? scale : 1)
}));
// 按高度降序排序
items.sort((a, b) => b.height - a.height);
const rows = [];
let currentRow = [];
let currentRowWidth = 0;
let currentRowHeight = 0;
for (const item of items) {
const itemWidth = item.width;
if (currentRowWidth + itemWidth + (currentRow.length * colSpacing) > baseWidth) {
if (currentRow.length > 0) {
rows.push({
items: [...currentRow],
width: currentRowWidth,
height: currentRowHeight
});
}
spritejson += " \"" + rowparams[i][j].name.replace("-", "/").replace("&", ":") + "\":{\"x\":";
spritejson += tempwidthnum + ",\"y\":" + heighttemp + ",\"width\":" + rowparams[i][j].width;
spritejson += ",\"height\":" + rowparams[i][j].height + ",\"pixelRatio\":1,\"sdf\":false},\n";
//增加宽度
tempwidthnum += rowparams[i][j].width;
currentRow = [item];
currentRowWidth = itemWidth;
currentRowHeight = item.height;
} else {
currentRow.push(item);
currentRowWidth += itemWidth;
if (currentRow.length > 1) {
currentRowWidth += colSpacing;
}
currentRowHeight = Math.max(currentRowHeight, item.height);
}
heighttemp += Math.max.apply(Math, rowparams[i].map(m => m.height));
}
//保存大图
editCxt.putImageData(editImageData, 0, 0);
this.editURL = editMap.toDataURL("image/png");//取得图像的数据URI
spritejson = spritejson.substring(0, spritejson.lastIndexOf(','));
spritejson += "\n}";
this.spritejson = spritejson
},
getXY(canvas, x, y) {
let ctx = canvas.getContext("2d");
// 获取画布上的图像像素矩阵
let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
let w = imageData.width
let data = imageData.data
let color = []
color[0] = data[(y * w + x) * 4]
color[1] = data[(y * w + x) * 4 + 1]
color[2] = data[(y * w + x) * 4 + 2]
color[3] = data[(y * w + x) * 4 + 3]
return color
},
setXY(imageData, x, y, color) {
let w = imageData.width
let data = imageData.data
data[(y * w + x) * 4] = color[0]
data[(y * w + x) * 4 + 1] = color[1]
data[(y * w + x) * 4 + 2] = color[2]
data[(y * w + x) * 4 + 3] = color[3]
imageData.data = data;
if (currentRow.length > 0) {
rows.push({
items: currentRow,
width: currentRowWidth,
height: currentRowHeight
});
}
// 计算总尺寸
let canvasWidth = 0;
let canvasHeight = 0;
rows.forEach(row => {
canvasWidth = Math.max(canvasWidth, row.width);
canvasHeight += row.height;
});
if (rows.length > 1) {
canvasHeight += (rows.length - 1) * rowSpacing;
}
// 计算位置
let currentY = 0;
const positionedItems = [];
rows.forEach(row => {
let currentX = 0;
row.items.forEach(item => {
positionedItems.push({
...item,
x: currentX,
y: currentY
});
currentX += item.width + colSpacing;
});
currentY += row.height + rowSpacing;
});
return {
canvasWidth,
canvasHeight,
items: positionedItems
};
},
downloadImg() {
if (this.editURL === null || this.editURL === '') return;
if (this.spritejson === null || this.spritejson === '') return;
// 将图片的src属性作为URL地址
let a = document.createElement('a')
let event = new MouseEvent('click')
a.download = 'sprite'
a.href = this.editURL
a.dispatchEvent(event);
downloadImg(type) {
let url, filename;
if (type === '1x') {
if (!this.editURL) {
this.$message.warning('请先生成1x图片');
return;
}
url = this.editURL;
filename = 'sprite.png';
} else {
if (!this.editURL2x) {
this.$message.warning('请先生成2x图片');
return;
}
url = this.editURL2x;
filename = 'sprite@2x.png';
}
const a = document.createElement('a');
a.download = filename;
a.href = url;
a.style.display = 'none';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
},
downloadJSON(){
let pom = document.createElement('a');
pom.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(this.spritejson));
pom.setAttribute('download', 'sprite.json');
if (document.createEvent) {
let event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
pom.dispatchEvent(event);
downloadJSON(type) {
let content, filename;
if (type === '1x') {
if (!this.spritejson) {
this.$message.warning('请先生成1x JSON');
return;
}
content = this.spritejson;
filename = 'sprite.json';
} else {
pom.click();
if (!this.spritejson2x) {
this.$message.warning('请先生成2x JSON');
return;
}
content = this.spritejson2x;
filename = 'sprite@2x.json';
}
const blob = new Blob([content], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
}
})
......
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>生成Mapbox Sprite(精灵图)</title>
<!-- https://blog.csdn.net/qq_40953393/article/details/120882645 -->
<!-- import CSS -->
<link href="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.14/theme-chalk/index.css" rel="stylesheet">
<style>
html,
body,
#app {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
position: absolute;
}
.upload,
.creat {
border-radius: 4px;
background: #d3dce6;
height: 100%;
}
.upload,
.mbSprite {
overflow-y: auto;
}
.but {
height: 100%;
padding: 80px 0;
/* display: flex; */
/* align-items: center; */
/* justify-content: center; */
}
</style>
</head>
<body>
<div id="app">
<el-row style="height: 100%" v-loading="loading" element-loading-text="精灵图拼命生成中..."
element-loading-spinner="el-icon-loading" element-loading-background="rgba(0, 0, 0, 0.8)">
<el-col class="upload" :span="12" style="padding: 0 25px;">
<el-upload multiple action accept="image/*" :auto-upload="false" list-type="picture-card"
:on-change="handleChange" :on-preview="handlePreview" :on-remove="handleRemove">
<i class="el-icon-plus"></i>
<div slot="tip" class="el-upload__tip">只能上传图片格式文件(png/jpeg/svg/ico等), 且单个图片不超过500kb</div>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
</el-col>
<el-col class="but" :span="1">
<el-row style="height: 5%">图像宽度:</el-row>
<el-row style="height: 75%">
<el-slider v-model="allwidth" :marks="marks" :min="64" :max="1024" vertical height="500px"
show-input></el-slider>
</el-row>
<el-row style="height: 14%"><el-button type="primary" @click="image">转换</el-button></el-row>
</el-col>
<el-col class="creat" :span="11">
<el-row class="mbSprite" style="height: 50%">
<img :src="editURL" style="border:1px solid #6f6f6f">
</el-row>
<el-row style="height: 5%">
<el-button type="primary" @click="downloadImg">下载图片</el-button>
<el-button type="primary" @click="downloadJSON">下载JSON</el-button>
</el-row>
<el-row style="height: 30%">
<el-input type="textarea" :rows="18" placeholder="JSON内容" v-model="spritejson"></el-input>
</el-row>
</el-col>
</el-row>
</div>
</body>
<!-- import Vue before Element -->
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.7.9/vue.min.js"></script>
<!-- import JavaScript -->
<script src="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.14/index.js"></script>
<script>
new Vue({
el: '#app',
data() {
return {
allwidth: 512, //生成精灵图的宽度
marks: {
64: '64',
128: '128',
256: '256',
512: '512',
1024: '1024',
},
loading: false, //加载
dialogImageUrl: '',
dialogVisible: false,
disabled: false,
fileList: [],
canvas: [],
rowparams: [],//List<List<Param>>
editURL: '',
spritejson: ''
}
},
mounted() {
},
methods: {
handleRemove(file, fileList) {
this.fileList = fileList;
},
handlePreview(file) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
},
async handleChange(file, fileList) {
//图片文件转base64
const imgfile = await this.img2base64(file.raw)
if (imgfile) this.fileList.push(imgfile)
},
img2base64(file) {
return new Promise(function (resolve, reject) {
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function (event) {
resolve({
name: file.name,
url: reader.result
})
};
reader.onerror = function () {
reject(null)
};
});
},
getImage(file) {
return new Promise(function (resolve, reject) {
let image = new Image();
image.src = file.url;
image.onload = () => {
resolve({
image,
width: image.width,
height: image.height
});
};
image.onerror = function () {
reject(null)
};
});
},
image() {
this.loading = true
const that = this
let paramlist = [];
if (this.fileList.length === 0) return;
const paramPromises = this.fileList.map(async (file) => {
const { image, width, height } = await this.getImage(file);
let canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
canvas.getContext("2d").drawImage(image, 0, 0);
return {
name: file.name.split(".")[0],
x: 0,
y: 0,
width: width,
height: height,
canvas: canvas
};
});
Promise.all(paramPromises).then(res => {
// 过滤为空数组
paramlist = res.filter(item => item !== null)
paramlist.sort(function (a, b) {
return a.height - b.height;
});
that.creatSprite(paramlist);
}).catch(error => {
console.error("Error processing images: ", error);
}).finally(() => {
that.loading = false
});
},
creatSprite(paramlist) {
//图片默认宽度为1024
let allwidth = this.allwidth;
let rowparams = [], paramnowlist = [];
let countnum = 0;
for (let i = 0; i < paramlist.length; i++) {
countnum += paramlist[i].width;
if (countnum > allwidth) {
i = i - 1;
countnum = 0;
rowparams.push(paramnowlist);
paramnowlist = [];
} else {
paramnowlist.push(paramlist[i]);
}
if (i === paramlist.length - 1) {
rowparams.push(paramnowlist);
break;
}
}
//计算应有的高度
let allheight = 0;
rowparams.forEach(item => {
allheight += Math.max.apply(Math, item.map(m => m.height));
})
//计算应有的宽度
allwidth = 0
rowparams[0].forEach(item => {
allwidth += item.width;
})
if (allwidth > this.allwidth / 2) allwidth = this.allwidth
else allwidth = Math.pow(2, Math.ceil(Math.log2(allwidth)));
let spritejson = "{\n";
//开始画大图
let editMap = document.createElement('canvas');
editMap.width = allwidth;
editMap.height = allheight;
let editCxt = editMap.getContext("2d");
let editImageData = editCxt.getImageData(0, 0, allwidth, allheight);
//保存起始高度
let heighttemp = 0;
for (let i = 0; i < rowparams.length; i++) {
let tempwidthnum = 0;
for (let j = 0; j < rowparams[i].length; j++) {
let map = rowparams[i][j].canvas;
//循环小图片
for (let x = 0; x < map.width; x++) {
for (let y = 0; y < map.height; y++) {
//获取像素
let color = this.getXY(map, x, y);
this.setXY(editImageData, x + tempwidthnum, y + heighttemp, color);
}
}
spritejson += " \"" + rowparams[i][j].name.replace("-", "/").replace("&", ":") + "\":{\"x\":";
spritejson += tempwidthnum + ",\"y\":" + heighttemp + ",\"width\":" + rowparams[i][j].width;
spritejson += ",\"height\":" + rowparams[i][j].height + ",\"pixelRatio\":1,\"sdf\":false},\n";
//增加宽度
tempwidthnum += rowparams[i][j].width;
}
heighttemp += Math.max.apply(Math, rowparams[i].map(m => m.height));
}
//保存大图
editCxt.putImageData(editImageData, 0, 0);
this.editURL = editMap.toDataURL("image/png");//取得图像的数据URI
spritejson = spritejson.substring(0, spritejson.lastIndexOf(','));
spritejson += "\n}";
this.spritejson = spritejson
},
getXY(canvas, x, y) {
let ctx = canvas.getContext("2d");
// 在调用getImageData之前设置willReadFrequently属性
ctx.willReadFrequently = true
// 获取画布上的图像像素矩阵
let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
let w = imageData.width
let data = imageData.data
let color = []
color[0] = data[(y * w + x) * 4]
color[1] = data[(y * w + x) * 4 + 1]
color[2] = data[(y * w + x) * 4 + 2]
color[3] = data[(y * w + x) * 4 + 3]
return color
},
setXY(imageData, x, y, color) {
let w = imageData.width
let data = imageData.data
data[(y * w + x) * 4] = color[0]
data[(y * w + x) * 4 + 1] = color[1]
data[(y * w + x) * 4 + 2] = color[2]
data[(y * w + x) * 4 + 3] = color[3]
imageData.data = data;
},
downloadImg() {
if (this.editURL === null || this.editURL === '') return;
if (this.spritejson === null || this.spritejson === '') return;
// 将图片的src属性作为URL地址
let a = document.createElement('a')
let event = new MouseEvent('click')
a.download = 'sprite@2x'
a.href = this.editURL
a.dispatchEvent(event);
},
downloadJSON() {
let pom = document.createElement('a');
pom.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(this.spritejson));
pom.setAttribute('download', 'sprite@2x.json');
if (document.createEvent) {
let event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
pom.dispatchEvent(event);
} else {
pom.click();
}
}
}
})
</script>
</html>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论