果玩软件园:为用户提供海量热门软件、游戏等手机资源下载服务!

装机必备热门标签玩游戏装软件BT游戏H5游戏看教程专题游戏盒子手机版

果玩软件园

所在位置:首页 > 资讯教程 > 软件教程 >  > 详情

如何在微信小程序中使用canvas绘制高铁线路图?

文章来源:网络作者:陌流兮发布时间:2026-05-29 00:42:08

GM盒子
GM盒子(高返利版)
GM手游福利平台,免费送首充,上线送VIP,免费领元宝和代金券。
Ready

  大家知道如何在微信小程序中使用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代码:

  

  (G23)选择出发站点 {{chooseStation}} 

  

  复制代码

  wxss代码:

  /* pages/Gline/index.wxss */

  page{ background-color: #eeeeee }

  .g-title{font-size: 36rpx;font-weight: 600;color: #768da4;padding: 36rpx 0;padding-left: 20rpx; background-color: #fff}

  .chooseStation{color: #32b16c}

  复制代码

  js代码:

  // pages/Gline/index.js

  Page({

  data:{

  canvWidth:750,

  canvHeight:750,

  stations:[\'北京南\',\'天津南\',\'济南西\',\'泰安\',\'滕州东\',\'徐州东\',\'南京南\',\'镇江南\',\'苏州北\',\'上海虹桥\',\'北京南\',\'天津南\',\'济南西\',\'泰安\',\'滕州东\',\'徐州东\',\'南京南\',\'镇江南\',\'苏州北\',\'上海虹桥\',\'北京南\',\'天津南\',\'济南西\',\'泰安\',\'滕州东\',\'徐州东\',\'南京南\',\'镇江南\',\'苏州北\',\'上海虹桥\'],

  chooseStation:\'\',//页面显示选中的车站名字

  prevChooseIdx:null,//上一次选中车站的下标

  // stations:[\'北京南\',\'天津南\',\'济南西\',\'泰安\'],

  },

  onLoad:function(options){

  // 页面初始化 options为页面跳转所带来的参数

  // this.setData({canvHeight:502});

  const ctx = wx.createCanvasContext(\'map\');//路线图绘制的画布上下文对象

  this.ctx = ctx;//将ctx对象绑定到当前页面中

  this.column = 3;//每行显示车站数量

  this.offsetTop = 30;//绘制起始坐标的top值,也就是距离canvas顶部的距离

  this.rect={//圆角矩形对象

  img_b:\'/images/rect-b.png\',//初始时图片

  img_g:\'/images/rect-g.png\',//选中时图片

  height:32,

  width:68

  }

  this.line = {//站与站之间的连线对象

  img:\'/images/line.png\',

  height:6,

  width:30

  },

  this.bendLine = {//站与站之间弯曲的连线

  img_l:\'/images/line_l.png\',//左侧连线

  img_r:\'/images/line_r.png\',//右侧连线

  height:70,

  width:20

  },

  this.rectArr=[];//记录所有车站的绘制起始点的坐标的数组

  this.oddRowIndexArr=[];//记录奇数行的车站的下标数组,如[0,1,2,6,.....]

  this.evenRowIndexArr=[];//记录偶数行的车站的下标数组,如[3,4,5,9,.....]

  this.initMap();

  },

  onReady:function(){

  },

  onShow:function(){

  // 页面显示

  },

  onHide:function(){

  // 页面隐藏

  },

  onUnload:function(){

  // 页面关闭

  },

  //对不同设备下图片大小的适配

  adaptiveScreenSize:function(o){

  let ww = this.data.winWidth;

  let zoom = ww/375;//375这里是按iPhone6的宽度做等比缩放

  this.setData({zoom:zoom});

  let rectW = o.width*zoom;

  let rectH = o.height*zoom;

  o.width = rectW;

  o.height = rectH;

  },

  //初始化路线图的方法

  initMap:function(){

  const that = this;

  wx.getSystemInfo({

  success: function(res){

  const ww = res.windowWidth;

  const pr = res.pixelRatio;

  that.setData({ winWidth:ww,pixelRatio:pr});//将设备的信息存入data中,供后面使用

  that.drawMap();

  }

  })

  },

  drawTxtAtPos:function(idx){

  const rectArr = this.rectArr;

  const w = this.rect.width;

  const h = this.rect.height;

  let txt = this.data.stations[idx];

  let len = txt.length;

  //当站点文本文字超过3个字,将缩小字号

  let fontSize = len>3?12:14;

  let x = rectArr[idx].x;

  let y = rectArr[idx].y;

  //计算文本在圆角矩形中的绘制点,使文字居中显示

  let txt_x = Math.floor((w - len*fontSize)/2)+x;

  let txt_y = Math.floor(h/2+fontSize/2)+y-2;//这里额外-2,文本才能更接近垂直居中

  this.ctx.setFontSize(fontSize);

  this.ctx.setFillStyle(\'#ffffff\')

  this.ctx.fillText(txt, txt_x, txt_y);

  },

  //在下标为idx处绘制圆角矩形

  initRect:function(idx){

  const rectArr = this.rectArr;

  let x = rectArr[idx].x;

  let y = rectArr[idx].y;

  this.ctx.drawImage(this.rect.img_b,x, y, this.rect.width, this.rect.height);

  },

  //动态计算不同屏幕大小canvas的高度

  initCanvHeight:function(){

  let len = this.data.stations.length;

  let pr = this.data.pixelRatio;

  let z = this.data.zoom;

  let row = Math.ceil(len/this.column);

  let h = 0;

  if(row <= 1){

  console.log(this.rect.height);

  h = (this.offsetTop*2 + this.rect.height)*2;

  }else{

  h = this.offsetTop*2+(row-1)*(this.bendLine.height-this.line.height)+this.rect.height;

  }

  this.setData({canvHeight:h});

  },

  //绘制线路这逻辑比较乱,我是把路线分为奇数段和偶数段进行绘制

  drawLine:function(){

  const rectArr = this.rectArr;

  let x=0,y=0;

  if(rectArr.length==2){//首先当车站数量为2个的时候,只需绘制一条线段

  x = rectArr[0].x+this.rect.width;//计算绘制线段起始点的x坐标

  y = rectArr[0].y+Math.floor((this.rect.height-this.line.height)/2);//计算绘制线段起始点的y坐标

  this.ctx.drawImage(this.line.img, x, y, this.line.width, this.line.height);

  }else{

  const odd = this.oddRowIndexArr;

  const even = this.evenRowIndexArr;

  if(odd.length>0){

  for(let i=0;i

  if((odd+1)!=rectArr.length){//判断当前下标绘制点后面是否还有绘制点

  x = rectArr[odd].x+this.rect.width;

  y = rectArr[odd].y+Math.floor((this.rect.height-this.line.height)/2);

  if((odd+1)%this.column!=0){//判断奇数行绘制点的下标如果不是3的整数倍将绘制一条直线,反之绘制右曲线

  this.ctx.drawImage(this.line.img, x, y, this.line.width, this.line.height);

  }else{

  this.ctx.drawImage(this.bendLine.img_r, x, y, this.bendLine.width, this.bendLine.height);

  }

  }

  }

  }

  //下面逻辑同奇数行的逻辑,不同的是绘制直线和弯曲线时x的坐标会有变化

  if(even.length>0){

  for(let i=0;i

  if((even+1)!=rectArr.length){

  y = rectArr[even].y+Math.floor((this.rect.height-this.line.height)/2);

  if((even+1)%this.column!=0){

  x = rectArr[even].x-this.line.width;//绘制直线时的计算公式

  this.ctx.drawImage(this.line.img, x, y, this.line.width, this.line.height);

  }else{

  x = rectArr[even].x-this.bendLine.width;//绘制弯曲线时的计算公式

  this.ctx.drawImage(this.bendLine.img_l, x, y, this.bendLine.width, this.bendLine.height);

  }

  }

  }

  }

  }

  },

  drawMap:function(){

  this.adaptiveScreenSize(this.rect);

  this.adaptiveScreenSize(this.line);

  this.adaptiveScreenSize(this.bendLine);

  this.initCanvHeight();

  this.createRectTopPoints();

  // setTimeout(()=>{

  const rectArr = this.rectArr;

  for(let i=0;i

  this.initRect(i);

  this.drawTxtAtPos(i);

  }

  this.ctx.draw(true);

  // },500);

  this.drawLine();

  this.ctx.draw(true);

  },

  //计算后,每行的所有绘制点的起始坐标x值是一个固定数组

  //如:奇数行[10,20,30],偶数行:[30,20,10]

  getDisXArr:function(){

  let arr = [];

  let ww = this.data.winWidth;

  let disX = Math.floor((ww-(this.column*this.rect.width+(this.column-1)*this.line.width))/2);

  for(let i=0;i

  let x = disX+i%this.column*(this.rect.width+this.line.width);

  arr = x;

  }

  return arr;

  },

  //根据给出的车站数量,将每个车站的绘制顶点计算出来存入数组rectArr中

  createRectTopPoints:function(){

  let rectArr = [];

  let disXArr = this.getDisXArr();

  let disXArrRev = this.getDisXArr().reverse();

  let disY = this.offsetTop;//绘制初始点距离canvas顶部的高度

  let len = this.data.stations.length;

  let row = Math.ceil(len/this.column);//根据车站数量计算需要绘制的行数

  let n=0,x=0,y=0;

  for(let j = 1;j<=row;j++){

  for(let i=0;i

  ++n;

  if(n<=len){

  if(j%2!=0){

  this.oddRowIndexArr.push(n-1);

  //console.log("奇数行:"+n);

  x = disXArr;

  }else{

  this.evenRowIndexArr.push(n-1);

  //console.log("偶数行:"+n);

  x = disXArrRev;

  }

  y = disY + (j-1)*(this.bendLine.height-this.line.height);

  this.rectArr[n-1] = {x:x,y:y};

  }

  }

  }

  },

  //判断手指触摸点是否在圆角矩形中

  pointInRectPolygon : function (point, vs) {

  let x = point[0], y = point[1],inside = false;

  for (let i = 0, j = vs.length - 1; i < vs.length; j = i++) {

  let xi = vs[0], yi = vs[1];

  let xj = vs[j][0], yj = vs[j][1];

  let intersect = ((yi > y) != (yj > y))

  && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);

  if (intersect) inside = !inside;

  }

  return inside;

  },

  //根据某个圆角矩形的绘制点和宽高,计算出圆角矩形4个顶点的坐标值

  //顺序为左上,右上,右下,左下,也就是顺时针方向

  getRectPolygon:function(x,y,w,h){

  let vs = new Array() ;

  vs[0] = [x,y];

  vs[1] = [x+w,y];

  vs[2] = [x+w,y+h];

  vs[3] = [x,y+h];

  return vs;

  } ,

  //点击车站调取的事件,事件中需要处理:

  //1、需要获取到当前点击的车站文本

  //2、判断是否有过选取,如果之前有选取,需要将之前选取过的区块颜色改为默认色

  //3、改变当前区块的颜色

  //4、记录当前点击的下标

  chooseStation:function(currIdx){

  let txt = this.data.stations[currIdx];

  let prevIdx = this.data.prevChooseIdx;

  if(prevIdx!=null){

  let x = this.rectArr[prevIdx].x;

  let y = this.rectArr[prevIdx].y;

  this.ctx.drawImage(this.rect.img_b,x, y, this.rect.width, this.rect.height);

  this.drawTxtAtPos(prevIdx);

  }

  let x = this.rectArr[currIdx].x;

  let y = this.rectArr[currIdx].y;

  this.ctx.drawImage(this.rect.img_g,x, y, this.rect.width, this.rect.height);

  this.drawTxtAtPos(currIdx);

  this.ctx.draw(true);

  this.setData({chooseStation:txt,prevChooseIdx:currIdx});

  },

  //点击事件

  touchS:function(e){

  console.log(e);

  let touch = e.changedTouches;//这里一定要用changedTouches,如果用touches,安卓机会有问题

  if(touch.length==1){

  let tapPoint = [touch[0].x,touch[0].y];

  let rectArr = this.rectArr;

  for(let i=0;i

  let vs = this.getRectPolygon(rectArr.x,rectArr.y,this.rect.width,this.rect.height);

  let inside = this.pointInRectPolygon(tapPoint,vs);

  if(inside){

  this.chooseStation(i);

  break;

  }

  }

  }

  }

  })

  复制代码

  真机测试图:

  以上就是如何在微信小程序中使用canvas绘制高铁线路图的全部内容了,大家都学会了吗?

End
复制本文链接资讯文章为果玩软件园所有,未经允许不得转载。
热门游戏MORE+
相关资讯MORE+
最新录入
热门资讯
新游新品榜
手机游戏
休闲益智
赛车竞速
棋牌桌游
角色扮演
动作射击
体育竞技
经营养成
策略塔防
冒险解谜
音乐游戏
手游辅助
H5游戏
BT游戏
手机软件
社交聊天
系统工具
时尚购物
旅游出行
影音播放
生活实用
办公学习
资讯阅读
拍摄美化
游戏辅助
健康医疗
地图导航
小说漫画
安全防护
育儿亲子
手游下载
梦想三国之勇往直前0....
炼仙传说0.1折
不可思议的刀剑与魔法...
逍遥浪人
奇幻梦旅人
玄影0.1折
点击冒险之旅(0.1折特...
天神赵子龙0.1折
九州异兽记0.1折
龙魂魔法0.1折
装机软件
爱奇艺电脑版
Steam下载管家 2026最...
360游戏大厅
GoLink加速器
3DM驱动大师
夸克
豆包电脑版
360C盘扩容大师
360录屏
360极速浏览器
精选专题
手机游戏专题
手机软件专题
电脑软件专题
电脑游戏专题
游戏排行榜
手游排行榜
软件排行榜
BT排行榜
电脑软件排行榜
电脑游戏排行榜