GitHub仓库:源代码
土地管理管理相关部门一般会有地图开发的相关业务,开屏一般会展示某个区域的全局图,如果加载过慢的话,可以考虑添加一个骨架屏来提升体验,特别是一些需要离线加载的场景,在线场景可以使用,或者使用web开发一套。
此项目通过地图边界数据来绘制骨架,提供了两种动画,线条动画如下,另一种为透明度的闪烁
数据来源
从地图平台上下载或者是动态请求行政区边界数据,如高德地图(底部有测试功能,可直接获取),天地图也有对应的接口,但是调用发现没有数据
使用
添加仓库
// 添加仓库
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
// 添加依赖
dependencies {
implementation 'com.github.wangyou1024:skeleton:Tag'
}
xml调用
<com.wangyou.skeleton.map.MapSkeleton
android:id="@+id/mapSkeleton"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
默认参数,或者在xml中设置app:porpertyName
,map_skeleton_city_source
可以是省级拼音(库内有少量下载的行政边界案例,可以直接使用),或者assets
下的文件名,map_skeleton_animation_type
可以设置为line
或者alpha
<style name="DefaultMapSkeleton">
<item name="map_skeleton_background">#f2f3f5</item>
<item name="map_skeleton_stroke_color">#BABABA</item>
<item name="map_skeleton_stroke_width">2dp</item>
<item name="map_skeleton_duration">600</item>
<item name="map_skeleton_city_source">china_chongqing_liangping.txt</item>
<item name="map_skeleton_city_name">梁平</item>
<item name="map_skeleton_city_name_color">#BABABA</item>
<item name="map_skeleton_city_name_size">25dp</item>
<item name="map_skeleton_animation_type">line</item>
<item name="map_skeleton_light_angle">30</item>
<item name="map_skeleton_light_color">#FFFFFFFF</item>
<item name="map_skeleton_alpha_max">1</item>
<item name="map_skeleton_alpha_min">0.4</item>
</style>
也可以自定义获取行政区数据的方法
class MainActivity {
public void onCreate(){
mapSkeleton.setCreatePath(new MapSkeleton.CreatePath() {
@Override
public List<float[][]> getPath() {
// List代表行政区下的多个地图块,很多行政区并不是一个整块,例如广州,float[][]代表某个区块的坐标经纬度
return new ArrayList<>();
}
});
}
}
Web版本
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>测试</title>
</head>
<body>
<div id="canvas_container" style="width: 100%; height: 100%;background: white;position: absolute;"></div>
<script>
class MapViewSkeleton {
/**
*
* @param element div元素
* @param pathData 三维小数数组,一个省市可能有多个区块,第一维为地图块,第二维为区块的经纬度[[[12,33],[13,43]][[23,43],[34,43]]]
* @param background 背景颜色
* @param strokeColor 边界颜色
* @param strokeSize 边界大小
* @param cityName 城市名称
* @param cityNameSize 城市名称字体大小
* @param cityNameColor 城市名称字体颜色
* @param duration 动画循环时长
*/
constructor(element, pathData, background, strokeColor, strokeSize, cityName, cityNameSize, cityNameColor, duration) {
this.element = element;
this.path = pathData;
this.background = background;
this.strokeColor = strokeColor;
this.strokeSize = strokeSize;
this.cityName = cityName;
this.cityNameSize = cityNameSize;
this.cityNameColor = cityNameColor;
this.duration = duration;
this.startTime = Date.now();
this.initCanvas();
this.draw();
}
initCanvas(){
this.canvas = document.createElement("canvas");
this.canvas.style = "width:100%; height:100%;border:1px solid #d3d3d3;position:absolute";
this.element.appendChild(this.canvas);
let rectangle = this.element.getBoundingClientRect();
// 不设置画布的宽高会导致画布被拉伸到element的宽高,默认canvas的大小width:300,height:150
this.canvas.width=rectangle.width;
this.canvas.height = rectangle.height;
// 处理缩放问题,计算左右上下边界
let left = Number.MAX_VALUE;
let right = 0;
let top = Number.MAX_VALUE;
let bottom = 0;
for (let i = 0; i < this.path.length; i++) {
left = Math.min(left, this.path[i].reduce((a1, a2)=>{return a1[0]>a2[0]?a2:a1})[0]);
right = Math.max(right, this.path[i].reduce((a1, a2)=>{return a1[0]>a2[0]?a1:a2})[0])
top = Math.min(top, this.path[i].reduce((a1, a2)=>{return a1[1]>a2[1]?a2:a1})[1])
bottom = Math.max(bottom, this.path[i].reduce((a1, a2)=>{return a1[1]>a2[1]?a1:a2})[1])
}
// 根据边界来移动路径,使其居中显示
let mapWidth = right-left;
let mapHeight = bottom-top;
let scaleX = this.canvas.width/mapWidth;
let scaleY = this.canvas.height/mapHeight;
let scale = Math.min(scaleX, scaleY);
for (let i = 0; i < this.path.length; i++){
for (let j = 0; j < this.path[i].length; j++) {
this.path[i][j][0] = (this.path[i][j][0]-left)*scale+(this.canvas.width-mapWidth*scale)/2;
this.path[i][j][1] = (this.path[i][j][1]-top)*scale+(this.canvas.height-mapHeight*scale)/2;
}
}
}
draw(){
let ctx = this.canvas.getContext("2d");
let round = (Date.now()-this.startTime)%this.duration;
// 用透明度来实现动画
ctx.globalAlpha = 0.7+0.3*Math.cos(Math.PI*2*(round/this.duration))
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
for (let j = 0; j < this.path.length; j++) {
ctx.beginPath();
ctx.moveTo(this.path[j][0][0], this.path[j][0][1]);
for (let i = 1; i < this.path[j].length; i++) {
ctx.lineTo(this.path[j][i][0], this.path[j][i][1]);
}
ctx.fillStyle = this.background
ctx.fill();
ctx.strokeStyle = this.strokeColor;
ctx.lineWidth = this.strokeSize
ctx.stroke()
}
ctx.font= this.cityNameSize+"px Arial";
ctx.textAlign="center"
ctx.fillStyle = this.cityNameColor
ctx.fillText(this.cityName,this.canvas.width/2, this.canvas.height/2);
}
startAnimation(){
this.draw();
this.animationId = requestAnimationFrame(()=>this.startAnimation());
}
cancelAnimation(){
cancelAnimationFrame(this.animationId);
this.element.removeChild(this.canvas);
}
// 高德的行政区数据解析
static addSkeleton(element, pathData, background, strokeColor, strokeSize, cityName, cityNameSize, cityNameColor, duration){
let pathDataStrArray = pathData.split("|");
let paths = [];
let top = Number.MAX_VALUE;
let bottom = 0;
for (let j = 0; j < pathDataStrArray.length; j++) {
let path = [];
let pathStr = pathDataStrArray[j].split(";");
for (let i = 0; i < pathStr.length; i++) {
let positionStr = pathStr[i].split(',');
let positions = [];
positions.push(parseFloat(positionStr[0]));
positions.push(parseFloat(positionStr[1]));
path.push(positions);
top = Math.min(top, positions[1]);
bottom = Math.max(bottom, positions[1]);
}
paths.push(path)
}
for (let j = 0; j < paths.length; j++) {
for (let i = 0; i < paths[j].length; i++) {
// 地图坐标系与画板坐标系的Y轴方向相反,需要反转
paths[j][i][1] = top+bottom-paths[j][i][1];
}
}
let skeleton = new MapViewSkeleton(element, paths, background, strokeColor, strokeSize, cityName, cityNameSize, cityNameColor, duration)
skeleton.startAnimation()
}
}
// pathData为地图原始数据
MapViewSkeleton.addSkeleton(document.getElementById("canvas_container"), pathData, "#f2f3f5", '#d7d6d6', 4, "南京", 50, "#d7d6d6", 2400)
</script>
</body>
</html>
三连!!!