svg 地图 及path的渲染

最近完成了一个web的项目:要求在地图上描绘两个省市(假设:A省和B省)的path,并动态的显示运动方向。
svg china地图,地图只是大致的示意图,不代表中国就完全这个样子。图上没有显示出中国版图最南端的群岛。
难点:
1.在web上渲染地图,省市坐标定位。效果如上图:A省画黄色圈,B省画蓝色圈,
2.path计算描绘、颜色渐变及path的动态效果。效果如上图:路径由黄色渐变成蓝色,路径上有个白色箭头由A省向B省移动。

关于地图这部分的demo链接:https://github.com/BokeChen/ChinaMap.git


一 地图渲染
svg地图的解决方案:
1.用Echarts的map库,但开发过程中涉及到底层的东西就很难找到相关的资料,刚开始也是用echarts来做,但echarts 提供的接口不是很灵活,很难实现自己的自定义的样式。后面弃用了。
2.在网上下载svg地图做背景或者直接用echarts的地图做背景,echarts的map 比较精确,自己描绘的地图不能保证精确。
这里用网上共享的svg地图,部分地方我微调了下(地图大致相似,不保证和现实地图等比缩放关系),具体见上面链接的demo的chinaMap .svg 。

chinaMap.svg 地图 ,可以直接用网页iframe标签引入,引入时注意设置width 和height。width 和height必须和svg里面用的尺寸一致。

svg设置地图长宽的地方:
<svg  width="500" heigth="425" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" viewBox="0 0 1000 850">

width为500,height为425. viewBox 的意思是:把地图的宽等比例分成1000 份,高分成850份。

二、各省定位及路径生成
地图引入到网页后,做为背景使用。这里不打算直接chinaMap.svg 里面的内容,所以打算把地图做为底层,在其上面添加一个等大的透明的svg覆盖,再根据地图各省的位置,在这个svg上用坐标标定出来。svg的默认参考点都是左上角。
最后定位出来的各省坐标如下:

// 各省市在地图上的坐标
 var province_config = [
   {name:'xinjiang',nameCH:'新疆',location:{x:"110",y:"170"}},    
   {name:'Tibet',nameCH:'西藏',location:{x:"130",y:"270"}},
   {name:'qinghai',nameCH:'青海',location:{x:"200",y:"236"}},
   {name:'jilin',nameCH:'吉林',location:{x:"454",y:"129"}},
   {name:'liaoning',nameCH:'辽宁',location:{x:"430",y:"154"}},
   {name:'hebei',nameCH:'河北',location:{x:"370",y:"197"}},
   {name:'shandong',nameCH:'山东',location:{x:"394",y:"222"}},
   {name:'jiangsu',nameCH:'江苏',location:{x:"420",y:"248"} },
   {name:'zhejiang',nameCH:'浙江',location:{x:"430",y:"292"} },
   {name:'anhui',nameCH:'安徽',location:{x:"400",y:"274"} },
   {name:'henan',nameCH:'河南',location:{x:"359",y:"251"} },
   {name:'shanxi',nameCH:'山西',location:{x:"345",y:"219"} },
   {name:'shaanxi',nameCH:'陕西',location:{x:"318",y:"255"} },
   {name:'gansu',nameCH:'甘肃',location:{x:"210",y:"182"} },
   {name:'hubei',nameCH:'湖北',location:{x:"354",y:"280"} },
   {name:'jiangxi',nameCH:'江西',location:{x:"388",y:"317"} },
   {name:'fujian',nameCH:'福建',location:{x:"414",y:"325"} },
   {name:'hunan',nameCH:'湖南',location:{x:"349",y:"320"} },
   {name:'guizhou',nameCH:'贵州',location:{x:"300",y:"334"} },
   {name:'sichuan',nameCH:'四川',location:{x:"270",y:"294"} },
   {name:'yunnan',nameCH:'云南',location:{x:"250",y:"358"} },
   {name:'hainan',nameCH:'海南',location:{x:"338",y:"415"} },
   {name:'shanghai',nameCH:'上海',location:{x:"433",y:"262"} },
   {name:'chongqing',nameCH:'重庆',location:{x:"309",y:"298"} },
   {name:'tianjin',nameCH:'天津',location:{x:"387",y:"185"} },
   {name:'beijing',nameCH:'北京',location:{x:"377",y:"175"} },
   {name:'ningxia',nameCH:'宁夏',location:{x:"290",y:"215"} },
   {name:'neimongol',nameCH:'内蒙古',location:{x:"320",y:"175"} },
   {name:'guangxi',nameCH:'广西',location:{x:"325",y:"371"} },   
   {name:'guangdong',nameCH:'广东',location:{x:"372",y:"360"} },
   {name:'hongkong',nameCH:'香港',location:{x:"383",y:"372"} },
   {name:'taiwan',nameCH:'台湾',location:{x:"448",y:"351"} },
   {name:'macau',nameCH:'澳门',location:{x:"350",y:"387"} },
   {name:'heilongjiang',nameCH:'黑龙江',location:{x:"456",y:"80"} }  

];

坐标定出来后,就考虑实现目标省市的特效效果,A省市: 土黄色圈 ,B省市:蓝色圈。至于实际项目中的要实现的psd设计图的外放光及高斯模糊效果,这里不讲。
svg 的动态生产dom节点必须使用 document.createElementNS(‘http://www.w3.org/2000/svg‘,”g”);
g是要生成的标签名,可使用svg circle path等svg特有的标签名。

下面函数里面使用了fragment来减少对dom的操作次数。(frogment是手误,这里把frogment 当成fragment )
var frogment=document.createDocumentFragment();

/**
* showPathFlowInMap 在地图上描绘出 A点(from)B点(to)的路线,A点画棕色(三色)圈,B点画蓝色(双色)圈。
* @ from Oject 类型,格式:{name:'xinjiang',nameCH:'新疆',location:{x:"110",y:"170"}} ,location是该点在地图上的坐标,路径起点(A点)
* @ to Oject 类型,格式:{name:'xinjiang',nameCH:'新疆',location:{x:"110",y:"170"}},locationlocation是该点在地图上的坐标,路径终点(B点)
*/

function showPathFlowInMap(from,to,frog){
var html=document.createElementNS('http://www.w3.org/2000/svg',"g");
html.innerHTML +=`
<circle cx="${to.location.x}" cy="${to.location.y}" r="6"  stroke="#476F8E"  stroke-width="2" fill="transparent" />
<circle cx="${to.location.x}" cy="${to.location.y}" r="2"  fill="#3C9CEF" />  

<circle cx="${from.location.x}" cy="${from.location.y}" r="7" stroke="#99834C"  stroke-width="2" fill="transparent" />        
<circle cx="${from.location.x}" cy="${from.location.y}" r="4" stroke="#58513C"  stroke-width="1.5" fill="transparent" />
<circle cx="${from.location.x}" cy="${from.location.y}" r="2" fill="#A97631" />
`;
frog.appendChild(html);
var frogment=document.createDocumentFragment();
frog.appendChild(createPath(from,to,frog));
console.log(frog);
return frog;

}

路径生成函数,要求路径由黄到蓝色渐变及路径上的箭头效果。
svg渐变有线性渐变和径向渐变,线性渐变又分为水平方向的渐变和垂直方向的渐变。下面的代码只是单独使用了水平渐变,在代码调试的时候,发现实际效果中并不完美,所以实际项目是都使用了水平和垂直渐变。
路径的描述:这里用了svg path d: A 的标签,具体的用法 下面函数注释里面有。

这里需要计算出两个椭圆的半径,当大半径和小半径相等时椭圆将接近于圆。运用这个原理,我把两个半径的值设成一样的值。如果要在两个省之间画圆,圆心到两个省的距离相等。
这个圆的半径:r=Math.sqrt((Math.pow(Math.abs(from.location.x-to.location.x),2)+Math.pow(Math.abs(from.location.y-to.location.y),2)));
椭圆的半径等于圆的半径*0.9 (0.9这个系数可以确认弧度的弯曲程度)

svg 的animateMotion 标签, svg同样拥有自己的动画效果。可以设计动画的持续时间,延迟动作时间,重复次数,运动路径,移动标志物。
这里提下调试的时候碰到的问题:
1.标志物(白色箭头)的中心点必须是(svg的原点)
2.重复次数设为1时,即使path改变,标志物也只会跑一次。要解决这个问题必须每次路径变化的同时删除svg再重新添加一个svg。
3.如果重复次数设置为:indefinite, 整个svg的动画持续时间是公用的。当路径变化后,标志物是接着上一个动画停止相对点继续运动。要处理这个问题,也需要先删除svg再添加svg。

/**
* createPath: showPathFlowInMap 的子函数,作用:描绘Path,添加渐变,添加箭头动态效果
* @ from Oject 类型,格式:{name:'xinjiang',nameCH:'新疆',location:{x:"110",y:"170"}} ,location是该点在地图上的坐标,路径起点(A点)
* @ to Oject 类型,格式:{name:'xinjiang',nameCH:'新疆',location:{x:"110",y:"170"}},locationlocation是该点在地图上的坐标,路径终点(B点)
* svg path A 指令参数 d="M ${from.location.x} ${from.location.y}(前两个参数起点坐标)     A${r},${r} (椭圆半径)   0 (与x轴的夹角) 0 (大弧度,小弧度选择),1(圆弧方向,顺时针为0,逆时针为1) ${to.location.x},${to.location.y}(中点坐标)"
*/

function createPath(from,to,frog){
var r= Math.sqrt((Math.pow(Math.abs(from.location.x-to.location.x),2)+Math.pow(Math.abs(from.location.y-to.location.y),2)))*0.9;  //算出两点间的距离,然后乘以0.9
var linearColor= from.location.x<to.location.x?"grad1":"grad3";
var radianOrientation= from.location.x<to.location.x?"1":"1";//调整圆弧方向,这里用不上。所以都赋值为1.
let html=document.createElementNS('http://www.w3.org/2000/svg',"g");
html.innerHTML+=`
<path d="M${from.location.x} ${from.location.y} A${r},${r} 0 0,${radianOrientation} ${to.location.x},${to.location.y}" stroke-width="2" stroke="url(#${linearColor})" fill="transparent" id="route${from.location.x}${from.location.y}_${to.location.x}${to.location.y}"/>
<image  x="-17" y="-10.7" width="25px" xlink:href="img/arrows.png"> 
   <animateMotion    id="#map${from.location.x}${from.location.y}_${to.location.x}${to.location.y}"        
          dur="5s"
          begin="0s"
          fill="freeze"
          repeatCount="1"
           rotate="auto"> 
           <mpath xlink:href="#route${from.location.x}${from.location.y}_${to.location.x}${to.location.y}" />         
   </animateMotion>
  </image>
`;

return html;//indefinite
}

最后就是 定时切换地图上的path:

setInterval(function(){  
  var s1=  document.getElementById("s1");

   s1.parentNode.removeChild(s1);

  var svg=document.createElementNS('http://www.w3.org/2000/svg','svg');//width="500px" height="425" id="s1" viewBox="0 0 500 425"
  console.log(document.body.childNodes[0]);
   document.getElementsByTagName("body")[0].insertBefore(svg,document.body.childNodes[0]).setAttribute("id","s1");
   document.getElementById("s1").setAttribute("width",500);
   document.getElementById("s1").setAttribute("height",425);
   document.getElementById("s1").setAttribute("viewBox","0 0 500 425");

  document.getElementById("s1").innerHTML=frog1;

    var frog=document.createDocumentFragment(); 
    var frogment=showPathFlowInMap(province_config[i],province_config[5],frog);

    document.getElementById("s1").appendChild(frog)    
    i++;

},6800);

实际项目地图最终实现的效果:
这里写图片描述

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值