地图骨架屏

GitHub仓库:源代码
土地管理管理相关部门一般会有地图开发的相关业务,开屏一般会展示某个区域的全局图,如果加载过慢的话,可以考虑添加一个骨架屏来提升体验,特别是一些需要离线加载的场景,在线场景可以使用,或者使用web开发一套。
此项目通过地图边界数据来绘制骨架,提供了两种动画,线条动画如下,另一种为透明度的闪烁
image.png

数据来源

从地图平台上下载或者是动态请求行政区边界数据,如高德地图(底部有测试功能,可直接获取),天地图也有对应的接口,但是调用发现没有数据

使用

添加仓库

// 添加仓库
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:porpertyNamemap_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>

三连!!!

  • 13
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

百忧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值