文章来源:网络作者:陌流兮发布时间:2026-05-23 04:00:16
大家知道如何在微信小程序中使用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代码:
复制代码
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绘制高铁线路图的全部内容了,大家都学会了吗?



梦想三国之勇往直前0.1折

炼仙传说0.1折
仙侠01折
充值享永久0.1折福利,登录每日领2000免费代金券;;

不可思议的刀剑与魔法0.1折
卡牌01折
进游立送真充卡,开局自带“钞能力”

逍遥浪人
卡牌
1折送绝版皮肤 全场充值永久1折,创角即送“黑神话”孙悟空绝版皮肤。

奇幻梦旅人
休闲
当个无忧无虑的旅人,尽情享受这个童话世界吧!

玄影0.1折
仙侠01折
全场充值永享0.1折!

点击冒险之旅(0.1折特级行...
卡牌01折
上线福利:登录即送创角大礼包,召唤顶级战力!

天神赵子龙0.1折
卡牌01折
全新0.1折放置卡牌,打破传统三国叙事,解锁新颖剧情,每日送1000代金;

九州异兽记0.1折
开箱01折
1分钱买SSR异兽

龙魂魔法0.1折
185传奇996传奇
上线即送:自动拾取、自动回收、切割+222、老男孩·称号!

魔药杂货铺什么时候出 公测上线时...