图文详解如何在微信小程序中使用canvas绘制高铁线路图。

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

<!--pages/Gline/index.wxml-->

<view class=\"g-title\">(G23)选择出发站点 <text class=\"chooseStation\">{{chooseStation}}</text> </view>

<canvas bindtouchstart=\"touchS\" canvas-id=\"map\" style=\\\'width:{{canvWidth}}rpx;height:{{canvHeight}}px;background-color: #eee\\\' />

复制代码

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<odd.length;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<even.length;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<rectArr.length;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<this.column;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<this.column;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<rectArr.length;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绘制高铁线路图呢?

在微信小程序中怎么用canvas绘制高铁线路图呢?

在微信小程序中怎么用canvas绘制高铁线路图呢?

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

本文来自投稿,不代表酷牛教程立场,如若转载,请注明出处:https://www.xukn.com/97680.html

(0)
上一篇 2021-04-10 10:25
下一篇 2021-04-10 10:43

相关推荐