Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
F
foxgis-server-lite
概览
概览
详情
活动
周期分析
版本库
存储库
文件
提交
分支
标签
贡献者
分支图
比较
统计图
问题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
open
foxgis-server-lite
Commits
8e10ccc8
提交
8e10ccc8
authored
6月 06, 2025
作者:
方治民
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
chore: 添加雪碧图生成工具网页
上级
3fd60460
隐藏空白字符变更
内嵌
并排
正在显示
2 个修改的文件
包含
553 行增加
和
0 行删除
+553
-0
sprite.html
data/assets/sprite.html
+246
-0
sprite@2x.html
data/assets/sprite@2x.html
+307
-0
没有找到文件。
data/assets/sprite.html
0 → 100644
浏览文件 @
8e10ccc8
<!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://www.jianshu.com/p/f477fa2111f7 -->
<!-- import CSS -->
<link
href=
"https://cdn.bootcss.com/element-ui/2.4.5/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%
;
}
.but
{
height
:
100%
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
}
</style>
</head>
<body>
<div
id=
"app"
>
<el-row
style=
"height: 100%"
>
<el-col
class=
"upload"
:span=
"11"
>
<el-upload
action=
"https://jsonplaceholder.typicode.com/posts/"
list-type=
"picture-card"
accept=
"image/*"
:on-preview=
"handlePreview"
:on-success=
"handleSuccess"
:on-remove=
"handleRemove"
multiple
>
<i
class=
"el-icon-plus"
></i>
<div
slot=
"tip"
class=
"el-upload__tip"
>
只能上传图片格式文件,且不超过500kb
</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>
</el-col>
<el-col
class=
"creat"
:span=
"11"
>
<el-row
style=
"height: 45%"
>
<img
:src=
"editURL"
style=
"border:1px solid #6f6f6f"
>
</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>
<el-row
style=
"height: 45%"
>
<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.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>
new
Vue
({
el
:
'#app'
,
data
()
{
return
{
dialogImageUrl
:
''
,
dialogVisible
:
false
,
disabled
:
false
,
fileList
:
[],
canvas
:
[],
paramList
:
[],
//List
<
Param
>
rowparams
:
[],
//List
<
List
<
Param
>>
editURL
:
''
,
spritejson
:
''
}
},
mounted
()
{
},
methods
:
{
handleRemove
(
file
,
fileList
)
{
this
.
fileList
=
fileList
;
},
handlePreview
(
file
)
{
this
.
dialogImageUrl
=
file
.
url
;
this
.
dialogVisible
=
true
;
},
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
);
},
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
=
[];
}
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
>
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
);
}
}
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"
);
// 获取画布上的图像像素矩阵
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'
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.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
data/assets/sprite@2x.html
0 → 100644
浏览文件 @
8e10ccc8
<!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
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论