前几天@天下雪 给了我一张高铁的路线图,问我能不能用canvas画出来,所以我就试了试,我的思路可能比较复杂;如果有更简单的思路可以留言回复;

关注微信公众号,获取源码和教程

下面说一下我的实现思路:  
1、首先是每个站点圆角矩形的绘制,一开始想着用canvas把圆角矩形绘制出来,但发现小程序暂时还没有绘制圆角的arcTo方法,所以用canvas绘制就相对比较麻烦,最后为了方便决定用图片代替; 
2、将整个路线图分为四个小图片,(1)站点圆角矩形(2)站点之间的直连线(3)站点之间右侧弯曲连线(4)站点之间左侧弯曲连线; 
3、通过观察分析,将绘制过程分为两步,(1)奇数行圆角矩形、连线的绘制点x坐标是从左至右递增,y坐标值是行数乘以某个固定值(2)偶数行圆角矩形、连线的绘制点x坐标是从左至右递减,y坐标值是行数乘以某个固定值 
4、奇数行,偶数行的圆角矩形的下标index+1是3的倍数的话,奇数行当前下标右侧绘制右弯曲连线图片,偶数行当前下标左侧绘制左弯曲连线图片; 
5、整个canvas绘制区域在不同手机上的适配 
6、具体的一些细节请参照代码注释 
7、开发工具上使用drawImage重复绘制同一张图片只显示第一次绘制的位置,暂时不知道什么原因,所以请在真机上测试; 
8、有什么不足之处,望大家多多指点!感激!

wxml代码:

  1. <!--pages/Gline/index.wxml-->
  2. <viewclass="g-title">(G23)选择出发站点<textclass="chooseStation">{{chooseStation}}</text></view>
  3. <canvasbindtouchstart="touchS"canvas-id="map"style='width:{{canvWidth}}rpx;height:{{canvHeight}}px;background-color:#eee'/>

wxss代码:

  1. /* pages/Gline/index.wxss */
  2. page{ background-color: #eeeeee }
  3. .g-title{font-size: 36rpx;font-weight: 600;color: #768da4;padding: 36rpx 0;padding-left: 20rpx; background-color: #fff}
  4. .chooseStation{color: #32b16c}

js代码:

[javascript] view plaincopy
  1. js代码:
  2. // pages/Gline/index.js
  3. Page({
  4. data:{
  5. canvWidth:750,
  6. canvHeight:750,
  7. stations:['北京南','天津南','济南西','泰安','滕州东','徐州东','南京南','镇江南','苏州北','上海虹桥','北京南','天津南','济南西','泰安','滕州东','徐州东','南京南','镇江南','苏州北','上海虹桥','北京南','天津南','济南西','泰安','滕州东','徐州东','南京南','镇江南','苏州北','上海虹桥'],
  8. chooseStation:'',//页面显示选中的车站名字
  9. prevChooseIdx:null,//上一次选中车站的下标
  10. // stations:['北京南','天津南','济南西','泰安'],
  11. },
  12. onLoad:function(options){
  13. // 页面初始化 options为页面跳转所带来的参数
  14. // this.setData({canvHeight:502});
  15. const ctx = wx.createCanvasContext('map');//路线图绘制的画布上下文对象
  16. this.ctx = ctx;//将ctx对象绑定到当前页面中
  17. this.column = 3;//每行显示车站数量
  18. this.offsetTop = 30;//绘制起始坐标的top值,也就是距离canvas顶部的距离
  19. this.rect={//圆角矩形对象
  20. img_b:'/images/rect-b.png',//初始时图片
  21. img_g:'/images/rect-g.png',//选中时图片
  22. height:32,
  23. width:68
  24. }
  25. this.line = {//站与站之间的连线对象
  26. img:'/images/line.png',
  27. height:6,
  28. width:30
  29. },
  30. this.bendLine = {//站与站之间弯曲的连线
  31. img_l:'/images/line_l.png',//左侧连线
  32. img_r:'/images/line_r.png',//右侧连线
  33. height:70,
  34. width:20
  35. },
  36. this.rectArr=[];//记录所有车站的绘制起始点的坐标的数组
  37. this.oddRowIndexArr=[];//记录奇数行的车站的下标数组,如[0,1,2,6,.....]
  38. this.evenRowIndexArr=[];//记录偶数行的车站的下标数组,如[3,4,5,9,.....]
  39. this.initMap();
  40. },
  41. onReady:function(){
  42. },
  43. onShow:function(){
  44. // 页面显示
  45. },
  46. onHide:function(){
  47. // 页面隐藏
  48. },
  49. onUnload:function(){
  50. // 页面关闭
  51. },
  52. //对不同设备下图片大小的适配
  53. adaptiveScreenSize:function(o){
  54. let ww = this.data.winWidth;
  55. let zoom = ww/375;//375这里是按iPhone6的宽度做等比缩放
  56. this.setData({zoom:zoom});
  57. let rectW = o.width*zoom;
  58. let rectH = o.height*zoom;
  59. o.width = rectW;
  60. o.height = rectH;
  61. },
  62. //初始化路线图的方法
  63. initMap:function(){
  64. const that = this;
  65. wx.getSystemInfo({
  66. success: function(res){
  67. const ww = res.windowWidth;
  68. const pr = res.pixelRatio;
  69. that.setData({ winWidth:ww,pixelRatio:pr});//将设备的信息存入data中,供后面使用
  70. that.drawMap();
  71. }
  72. })
  73. },
  74. drawTxtAtPos:function(idx){
  75. const rectArr = this.rectArr;
  76. const w = this.rect.width;
  77. const h = this.rect.height;
  78. let txt = this.data.stations[idx];
  79. let len = txt.length;
  80. //当站点文本文字超过3个字,将缩小字号
  81. let fontSize = len>3?12:14;
  82. let x = rectArr[idx].x;
  83. let y = rectArr[idx].y;
  84. //计算文本在圆角矩形中的绘制点,使文字居中显示
  85. let txt_x = Math.floor((w - len*fontSize)/2)+x;
  86. let txt_y = Math.floor(h/2+fontSize/2)+y-2;//这里额外-2,文本才能更接近垂直居中
  87. this.ctx.setFontSize(fontSize);
  88. this.ctx.setFillStyle('#ffffff')
  89. this.ctx.fillText(txt, txt_x, txt_y);
  90. },
  91. //在下标为idx处绘制圆角矩形
  92. initRect:function(idx){
  93. const rectArr = this.rectArr;
  94. let x = rectArr[idx].x;
  95. let y = rectArr[idx].y;
  96. this.ctx.drawImage(this.rect.img_b,x, y, this.rect.width, this.rect.height);
  97. },
  98. //动态计算不同屏幕大小canvas的高度
  99. initCanvHeight:function(){
  100. let len = this.data.stations.length;
  101. let pr = this.data.pixelRatio;
  102. let z = this.data.zoom;
  103. let row = Math.ceil(len/this.column);
  104. let h = 0;
  105. if(row <= 1){
  106. console.log(this.rect.height);
  107. h = (this.offsetTop*2 + this.rect.height)*2;
  108. }else{
  109. h = this.offsetTop*2+(row-1)*(this.bendLine.height-this.line.height)+this.rect.height;
  110. }
  111. this.setData({canvHeight:h});
  112. },
  113. //绘制线路这逻辑比较乱,我是把路线分为奇数段和偶数段进行绘制
  114. drawLine:function(){
  115. const rectArr = this.rectArr;
  116. let x=0,y=0;
  117. if(rectArr.length==2){//首先当车站数量为2个的时候,只需绘制一条线段
  118. x = rectArr[0].x+this.rect.width;//计算绘制线段起始点的x坐标
  119. y = rectArr[0].y+Math.floor((this.rect.height-this.line.height)/2);//计算绘制线段起始点的y坐标
  120. this.ctx.drawImage(this.line.img, x, y, this.line.width, this.line.height);
  121. }else{
  122. const odd = this.oddRowIndexArr;
  123. const even = this.evenRowIndexArr;
  124. if(odd.length>0){
  125. for(let i=0;i<odd.length;i++){
  126. if((odd+1)!=rectArr.length){//判断当前下标绘制点后面是否还有绘制点
  127. x = rectArr[odd].x+this.rect.width;
  128. y = rectArr[odd].y+Math.floor((this.rect.height-this.line.height)/2);
  129. if((odd+1)%this.column!=0){//判断奇数行绘制点的下标如果不是3的整数倍将绘制一条直线,反之绘制右曲线
  130. this.ctx.drawImage(this.line.img, x, y, this.line.width, this.line.height);
  131. }else{
  132. this.ctx.drawImage(this.bendLine.img_r, x, y, this.bendLine.width, this.bendLine.height);
  133. }
  134. }
  135. }
  136. }
  137. //下面逻辑同奇数行的逻辑,不同的是绘制直线和弯曲线时x的坐标会有变化
  138. if(even.length>0){
  139. for(let i=0;i<even.length;i++){
  140. if((even+1)!=rectArr.length){
  141. y = rectArr[even].y+Math.floor((this.rect.height-this.line.height)/2);
  142. if((even+1)%this.column!=0){
  143. x = rectArr[even].x-this.line.width;//绘制直线时的计算公式
  144. this.ctx.drawImage(this.line.img, x, y, this.line.width, this.line.height);
  145. }else{
  146. x = rectArr[even].x-this.bendLine.width;//绘制弯曲线时的计算公式
  147. this.ctx.drawImage(this.bendLine.img_l, x, y, this.bendLine.width, this.bendLine.height);
  148. }
  149. }
  150. }
  151. }
  152. }
  153. },
  154. drawMap:function(){
  155. this.adaptiveScreenSize(this.rect);
  156. this.adaptiveScreenSize(this.line);
  157. this.adaptiveScreenSize(this.bendLine);
  158. this.initCanvHeight();
  159. this.createRectTopPoints();
  160. // setTimeout(()=>{
  161. const rectArr = this.rectArr;
  162. for(let i=0;i<rectArr.length;i++){
  163. this.initRect(i);
  164. this.drawTxtAtPos(i);
  165. }
  166. this.ctx.draw(true);
  167. // },500);
  168. this.drawLine();
  169. this.ctx.draw(true);
  170. },
  171. //计算后,每行的所有绘制点的起始坐标x值是一个固定数组
  172. //如:奇数行[10,20,30],偶数行:[30,20,10]
  173. getDisXArr:function(){
  174. let arr = [];
  175. let ww = this.data.winWidth;
  176. let disX = Math.floor((ww-(this.column*this.rect.width+(this.column-1)*this.line.width))/2);
  177. for(let i=0;i<this.column;i++){
  178. let x = disX+i%this.column*(this.rect.width+this.line.width);
  179. arr = x;
  180. }
  181. return arr;
  182. },
  183. //根据给出的车站数量,将每个车站的绘制顶点计算出来存入数组rectArr中
  184. createRectTopPoints:function(){
  185. let rectArr = [];
  186. let disXArr = this.getDisXArr();
  187. let disXArrRev = this.getDisXArr().reverse();
  188. let disY = this.offsetTop;//绘制初始点距离canvas顶部的高度
  189. let len = this.data.stations.length;
  190. let row = Math.ceil(len/this.column);//根据车站数量计算需要绘制的行数
  191. let n=0,x=0,y=0;
  192. for(let j = 1;j<=row;j++){
  193. for(let i=0;i<this.column;i++){
  194. ++n;
  195. if(n<=len){
  196. if(j%2!=0){
  197. this.oddRowIndexArr.push(n-1);
  198. //console.log("奇数行:"+n);
  199. x = disXArr;
  200. }else{
  201. this.evenRowIndexArr.push(n-1);
  202. //console.log("偶数行:"+n);
  203. x = disXArrRev;
  204. }
  205. y = disY + (j-1)*(this.bendLine.height-this.line.height);
  206. this.rectArr[n-1] = {x:x,y:y};
  207. }
  208. }
  209. }
  210. },
  211. //判断手指触摸点是否在圆角矩形中
  212. pointInRectPolygon : function (point, vs) {
  213. let x = point[0], y = point[1],inside = false;
  214. for (let i = 0, j = vs.length - 1; i < vs.length; j = i++) {
  215. let xi = vs[0], yi = vs[1];
  216. let xj = vs[j][0], yj = vs[j][1];
  217. let intersect = ((yi > y) != (yj > y))
  218. && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
  219. if (intersect) inside = !inside;
  220. }
  221. return inside;
  222. },
  223. //根据某个圆角矩形的绘制点和宽高,计算出圆角矩形4个顶点的坐标值
  224. //顺序为左上,右上,右下,左下,也就是顺时针方向
  225. getRectPolygon:function(x,y,w,h){
  226. let vs = new Array() ;
  227. vs[0] = [x,y];
  228. vs[1] = [x+w,y];
  229. vs[2] = [x+w,y+h];
  230. vs[3] = [x,y+h];
  231. return vs;
  232. } ,
  233. //点击车站调取的事件,事件中需要处理:
  234. //1、需要获取到当前点击的车站文本
  235. //2、判断是否有过选取,如果之前有选取,需要将之前选取过的区块颜色改为默认色
  236. //3、改变当前区块的颜色
  237. //4、记录当前点击的下标
  238. chooseStation:function(currIdx){
  239. let txt = this.data.stations[currIdx];
  240. let prevIdx = this.data.prevChooseIdx;
  241. if(prevIdx!=null){
  242. let x = this.rectArr[prevIdx].x;
  243. let y = this.rectArr[prevIdx].y;
  244. this.ctx.drawImage(this.rect.img_b,x, y, this.rect.width, this.rect.height);
  245. this.drawTxtAtPos(prevIdx);
  246. }
  247. let x = this.rectArr[currIdx].x;
  248. let y = this.rectArr[currIdx].y;
  249. this.ctx.drawImage(this.rect.img_g,x, y, this.rect.width, this.rect.height);
  250. this.drawTxtAtPos(currIdx);
  251. this.ctx.draw(true);
  252. this.setData({chooseStation:txt,prevChooseIdx:currIdx});
  253. },
  254. //点击事件
  255. touchS:function(e){
  256. console.log(e);
  257. let touch = e.changedTouches;//这里一定要用changedTouches,如果用touches,安卓机会有问题
  258. if(touch.length==1){
  259. let tapPoint = [touch[0].x,touch[0].y];
  260. let rectArr = this.rectArr;
  261. for(let i=0;i<rectArr.length;i++){
  262. let vs = this.getRectPolygon(rectArr.x,rectArr.y,this.rect.width,this.rect.height);
  263. let inside = this.pointInRectPolygon(tapPoint,vs);
  264. if(inside){
  265. this.chooseStation(i);
  266. break;
  267. }
  268. }
  269. }
  270. }
  271. })

真机测试图:

wxapp-Gline.zip 
 
 

转载于http://www.wxapp-union.com/article-1419-1.html

小程序之基于canvas绘制高铁线路图相关推荐

  1. 微信小程序之基于canvas绘制高铁线路图

    前几天@天下雪 给了我一张高铁的路线图,问我能不能用canvas画出来,所以我就试了试,我的思路可能比较复杂:如果有更简单的思路可以留言回复: 关注微信公众号,获取源码和教程 下面说一下我的实现思路: ...

  2. 在H5、微信小程序中使用canvas绘制二维码、分享海报

    在H5.微信小程序中使用canvas绘制二维码.分享海报 文章目录 在H5.微信小程序中使用canvas绘制二维码.分享海报 前言 一.canvas绘制二维码 1.H5中使用canvas 2.微信小程 ...

  3. 小程序 mpvue 使用canvas绘制环形图表

    本来想用css3来实现,发现轮廓边上残影严重,所以直接用小程序的canvas使用来. 最终效果如下: 我的整页代码如下,里面已经写出备注来. <template><div class ...

  4. 用微信小程序开发的Canvas绘制可配置的转盘抽奖

    使用https://github.com/givebest/GB-canvas-turntable代码移植过而来. 其它 微信小程序感觉是个半成品,代码移植过程比较繁琐麻烦.canvas API 部分 ...

  5. 小程序中使用canvas绘制海报

    最近项目需求使用canvas绘制朋友圈可分享的海报,中间遇到很多问题,于是上网搜索,完美解决后,在此总结一下. 先来看一下效果图,点击按钮生成带二维码的图片. 1.关于canvas画布的宽度和高度 w ...

  6. 微信小程序开发—(八)canvas绘制图形

    一.小知识 (1).API接口 (2).context 对象的方法列表 二.步骤 wxml中: <canvas canvas-id="myCanvas" class=&quo ...

  7. 小程序水印照片canvas绘制网络图和本地图加载空白的解决方案

    功德+1 功德+1 功德+1 效果图 遇到问题 在图片onload中,绘制水印,水印含网络图logo和本地图的图片.在使用**wx.canvasToTempFilePath()**生成图片时,logo ...

  8. java高铁购票程序代码教学_基于jsp的高铁订票-JavaEE实现高铁订票 - java项目源码...

    基于jsp+servlet+pojo+mysql实现一个javaee/javaweb的高铁订票, 该项目可用各类java课程设计大作业中, 高铁订票的系统架构分为前后台两部分, 最终实现在线上进行高铁 ...

  9. 微信小程序:wx-charts动态绘制折线图

    微信小程序:wx-charts动态绘制折线图 wx-charts是基于 Canvas的微信小程序主流图表工具,体积小易操作,支持多种图表的绘制,这里主要就动态绘制折线图做出详解,所谓动态,指的是表格的 ...

最新文章

  1. 018_SpringBoot异常处理方式-ExceptionHandle注解处理异常
  2. java同一包protect_Java基础知识 - 欢迎来到夜故事,一个人的故事 - OSCHINA - 中文开源技术交流社区...
  3. Python索引index常用的8种操作
  4. springboot单例模式注入对象_Spring 中经典的 9 种设计模式,打死也要记住啊!
  5. Confluence 6 管理协同编辑 - 关于 Synchrony
  6. 来吧,1分钟带你玩转Kafka
  7. C++新特性探究(八):初始化列表(Initialization List)再探究
  8. matlab ofdmmodulator,那位高手指点一下OFDM的基本仿真,用MATLAB,谢谢了
  9. 100.Day12反射机制_qq_38205875的博客
  10. jquery grid 显示隐藏列
  11. 你应该首先保护哪些应用程序?这个问题本身问错了!
  12. 可做fft分析吗_做数据分析的你,真的会5W2H分析法吗?
  13. BP神经网络算法基本原理,bp神经网络的算法步骤
  14. IDEA安装Vue插件
  15. 绕过BIOS/UEFI固件写保护写入SPI闪存
  16. gammatone 滤波器详解及其MATLAB代码实现
  17. 数独输出Java_java – 使用回溯的数独求解器
  18. Auto.js学习笔记——快速入门:软件安装
  19. 深演智能数智化 “三角魔方”浮出水面,企业征战数字化转型沙场的最新秘密武器?...
  20. FLink聚合性能优化--MiniBatch分析

热门文章

  1. vue项目开发的目录结构
  2. Win2000 蓝屏画面错误代码说明
  3. 系统维护盘Windwos PE的使用
  4. 栈应用—判断FILO序列
  5. Adobe Photoshop 2020 21.2.2.289 中文版 — 图像处理工具
  6. 我们需要什么样的导航网站?
  7. 离线安装wxpython4.0.6_离线安装wxpython
  8. 复旦FM1208 CPU卡调试
  9. 如何将一个Jsp网站打包发布(发布为War文件)
  10. 进程间通信——剪切板