写在前面
如果某一天,你突然要开发一个小程序项目,然而到目前为止你只是一个无辜的Android程序员,此时该怎么做呢?
下载开发环境
在微信小程序官方网站下载官方IDE,然后安装,就完成啦。
新建一个项目
填入项目目录和名称,如果注册了AppID可以填入,然后点击确定进入。
新建项目开始写Bug代码
一个小程序页面包含4个类型的文件:.js,.json,.wxml,.wxss。js负责管理逻辑,json是配置文件,wxml和wxss就是界面实现文件了,对应html和css,具体描述见官方文档,如果有过网页前端的编写经验理解起来会比较容易。ctrl+s保存代码,保存后左边预览会立即更新。ctrl+/ 可以快速注释行代码或代码块。
配个图更好看一点那么,接下来就要开始我们的HelloWorld了。
首先在app.json里配置全局属性:
{
"pages": [
"pages/index/index"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#333333",
"navigationBarTitleText": "我的小程序",
"navigationBarTextStyle": "white"
}
}
效果如下图:
配置小程序窗口和标题
page字段里是所有页面的地址,当你用IDE新建页面的时候就会自动填入,最上面一个页面即为小程序首页。window字段是小程序窗口相关的配置。这里还可以配置小程序下方的tabbar:
"tabBar": {
"selectedColor": "#FCA13B",
"backgroundColor": "#4A4A4A",
"color": "#B6B6B6",
"list": [
{
"pagePath": "pages/main/main",
"text": "首页",
"iconPath": "images/home@2x.png",
"selectedIconPath": "images/home_selected@2x.png"
},
/*...下略*/
]
}
小程序原生支持tabbar,不过最多配置5个,最少两个,今天这篇文章对应的程序没有用到这个功能,所以只是拿其它地方的做个示意,效果可以像这样:
tabbar效果图
图标右上角红点和数字是调用api实现的,代码如下:
wx.setTabBarBadge({
index: 3,/*在第几个item上显示*/
text: '99+',/*显示的内容*/
});
wx.showTabBarRedDot({
index: 0,/*第几个item显示红点*/
});
接下来开始编写我们的页面。打开index.wxml。
常用的标签有:
<view> 类似于html里的<div>,是块元素(block element),应用于各种地方。
<scroll-view>滚动视图,和Android的ScrollView不同的是它里面可以包含多个并列的标签。分为水平滚动和垂直滚动。如果你设置的scroll view没有滚动或者和你想的不太一样,试试把它的大小写成固定值。
<text>文本标签。
<image>图片标签。
<radio-group>、<radio>radio-group里嵌套radio,构成一组单选按钮。
<checkbox-group>、<checkbox>和radio类似,可构成一组多选按钮。
<label>用于配合radio、checkbox做自定义样式的单选、多选按钮组,具体可以参考这个官方例子。
<input>输入框,支持密码模式。
暂时用过这些,所以来画界面吧。
目标效果图长这样:
首先是外层布局,写个.container:
.container{
display: flex;
flex-direction: column;
padding: 20rpx;
background: #f5f5f5;
}
flex布局参考。微信小程序常用的尺寸单位是rpx。
接下来是第一行日期和省份选择器。设置渐变色,css画下箭头。微信提供了picker组件,里面自带了日期和省份选择器的功能,布局代码如下:
<view class='container'>
<view class='picker-row'>
<view class='picker-btn'>
<picker mode="date" value="{{date}}" start="1970-01-01" end="2030-12-31" bindchange="onDatePickerChange">
{{date}}
</picker>
<label class='down-arrow' />
</view>
<view class='picker-btn'>
<picker mode="region" value="{{region}}" bindchange="onRegionPickerChange" custom-item='全部'>
{{region[0]}}
</picker>
<label class='down-arrow' />
</view>
</view>
</view>
对应css如下:
.picker-row{
display: flex;
flex-direction: row;
justify-content: flex-start;
}
.picker-btn{
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-around;
padding: 0 20rpx;
margin-right: 20rpx;
border: 1rpx solid #cccccc;
border-radius: 8rpx;
background:linear-gradient(#f4f4f4,#e6e6e6);
}
.down-arrow{/*画下箭头,通过叠放两个三角形实现。*/
width: 0rpx;
height: 0rpx;
border: 20rpx solid transparent;
border-top: 20rpx solid #9d9d9d;/*底层,箭头的颜色*/
border-radius: 10rpx;
margin-top: 30rpx;
margin-left: 10rpx;
}
.down-arrow::after{
content: "";
width: 0rpx;
height: 0rpx;
border: 20rpx solid transparent;
border-top: 20rpx solid #ededed;/*覆盖层,颜色和背景色相同*/
position: absolute;
margin-top: -25rpx;
margin-left: -20rpx;
}
/*另一种实现下箭头的方式,相互垂直放置两条短线,再整体旋转*/
.down-arrow1{
width: 20rpx;
height: 5rpx;
background: #9d9d9d;
transform: rotate(135deg);
margin-left: 20rpx;
margin-top: 5rpx;
}
.down-arrow1::after{
position: absolute;
content: "";
width: 20rpx;
height: 5rpx;
margin-left: 10rpx;/*width的一半*/
margin-top: 10rpx;
transform: rotate(90deg);
background: #9d9d9d;
}
效果图:
picker效果图
尝试了两种下箭头实现方式。虽然布局看起来还不错,然而右边的下箭头被放在了picker组件外面,点击无效果,而放进picker里面布局又调不好。。。只好暂时先这样了。
接下来是重要指标这个box。思路是整体一个纵向布局,第一行横向布局,包含一个image加上一个text,第二行横向布局,包含三个纵向布局元素,每个元素包含一个canvas和一个text。
<view class='box'>
<view class='box-row'>
<image src='../../images/icon1.png' class='icon' />
<text class='box-text-title'>重要指标</text>
</view>
<view class='box-row between'>
<view class='box-col' bindtap='onClaimExpenseTap'>
<canvas class='canvas-ring' canvas-id='ring1'>
<view class='important-standard-rate'>{{important.fundRate}}</view>
</canvas>
<text class='box-text-lightblue'>资金报账比例</text>
</view>
<view class='box-col'>
<canvas class='canvas-ring' canvas-id='ring2'>
<view class='important-standard-rate'>{{important.checkAccount}}</view>
</canvas>
<text class='box-text-lightblue'>银行对账上传及时率</text>
</view>
<view class='box-col' bindtap='onInconformityTap'>
<canvas class='canvas-ring' canvas-id='ring3'>
<view class='important-standard-rate'>{{important.inconformity}}</view></canvas>
<text class='box-text-lightblue'>账交不符率</text>
</view>
</view>
</view>
css如下
.box{
margin-top: 30rpx;
display: flex;
flex-direction: column;
background: white;
border-radius: 16rpx;
padding: 20rpx;
box-shadow: 0 0 8rpx 2rpx rgba(0,0,0,0.2);
}
.box-row{
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
overflow: scroll;
padding: 5rpx;
}
.between{
justify-content: space-between;
}
.box-col{
display: flex;
flex-direction: column;
align-items: center;
}
.canvas-ring{
width: 200rpx;
height: 200rpx;
}
.icon{
width: 50rpx;
height: 50rpx;
margin-right: 20rpx;
}
.box-text-title{
font-size: 30rpx;
color:#526066;
}
.box-text-lightblue{
font-size: 25rpx;
color: #a3bfcc;
margin: 5rpx;
white-space: nowrap;
text-align: center;
}
.important-standard-rate{
font-size: 34rpx;
line-height: 34rpx;
margin-top: 83rpx;
color: #647176;
text-align: center;
}
这里的一个难点就是canvas绘制那个环形图了,绘制步骤如下:
1、获取canvas宽和高,计算出圆心坐标。
2、定义圆环线宽,计算外环和内环的半径:外环半径+外环线宽<=canvas宽高中较小的那个,内环半径则在外环半径基础上减去线宽。
3、设置线性渐变,画出外环(设置透明度为0.1)和内环(设置透明度为0.3)。
4、画表示百分比的圆弧。两个要点:圆弧左右两边对称,圆弧有动画效果。
①用待表示的百分比乘以2π得出圆弧弧度。
②微信小程序arc函数的起点,也就是0°位置在3点钟处(看看墙上的钟,3点钟的
位置在哪?),默认顺时针旋转。所以要使圆弧左右对称,绘制起点度数为 (π-圆弧弧度)/2
③实现动画效果的方式是:设置一个动画时长和每帧间隔时间,它们之间的比值称为动画的步数。圆弧弧度除以动画步数就是每帧绘制的弧度。setInterval反复执行直到动画结束。
实现代码如下:
function drawRingGraph(canvasId, canvasWidth, canvasHeight, percent, lineWidth,grdColors,animTime){
const context=wx.createCanvasContext(canvasId);
const x=canvasWidth/2;
const y=canvasHeight/2;
const lw =lineWidth?lineWidth:6;
const radiusOut=Math.min(x,y)-lw;//外环半径为宽高较小一个减去线宽
const radiusIn=radiusOut-lw;//内环比外环小一个线宽
const rate=(percent>=0&&percent<=1)?percent:0;//百分比为[0,1]之间的数
const colors = grdColors ? grdColors : ['#0ecbfe', '#aabdfa'];
const time=animTime?animTime:500;//动画时长,ms
const frameTimeInterval=20;//帧间隔,ms
const grd = context.createLinearGradient(0, 2 * y, 2 * x, 0);//设置渐变从画布左下角到右上角
for(let i=0;i<colors.length;i++){
grd.addColorStop(i/(colors.length-1),colors[i]);//均分多个颜色的位置,addColorStop的颜色位置取值范围[0,1]
}
const interval= setInterval((()=>{
let cnt = time / frameTimeInterval;//帧间隔时间
let start = Math.PI / 2 - Math.PI * rate;//百分比环起点弧度
const step=2*Math.PI*rate/cnt;//步长弧度
let end=start+step;//终止弧度
function drawStep(){
//画外圆
context.setLineWidth(lw);
context.setStrokeStyle(grd);
context.setGlobalAlpha(0.1);
context.beginPath();
context.arc(x, y, radiusOut, 0, 2 * Math.PI, false);
context.stroke();//在调用新的beginPath之前必须调用stroke或fill,否则之前的路径无效
//画内圆
context.setGlobalAlpha(0.3);
context.beginPath();
context.arc(x, y, radiusIn, 0, 2 * Math.PI, false);
context.stroke();
//画百分比弧
context.setGlobalAlpha(1);
context.setLineCap('round');
context.beginPath();
context.arc(x, y, radiusIn, start, end, false);
context.stroke();
context.draw();//注意!每次调用draw会把整个画布清空!所以只在画完一帧之后才调用该函数,每一帧都要重画背景。
end+=step;
cnt--;
if(cnt==0)
clearInterval(interval);
}
return drawStep;
})(), frameTimeInterval);
}
来个效果图,可以设置动画时长和多个渐变色,还不错的样子对吧:
接下来是业务推广这个框,和刚完成的这个框类似,当然难点就是在中间那个漂亮的线形图上了。所以说该怎么办呢?
盯着图大概理一下思路。首先计算出坐标原点,然后画y轴,确定一个y轴最大值,根据y轴高度和预设文字高度确定y轴刻度个数,画y轴刻度,然后画x轴,根据x轴长度确定x轴刻度个数,画x轴刻度。画点的话,y轴依据点的值与y轴最大值的比例来确定位置,x轴则根据轴长度与总点数计算出每个点所占空间,依次排布即可,然后用弧线连接点,再画上阴影。听起来可行的样子,那来画画看:
———————————————跨越时空的分割线———————————————
(此文初写于2018年5月份,后面就。。忘记了,今天权且收个尾。)
关于图表,后来我们做了个初版的插件放在了github上
线形图表后面的开发考虑了这样一些问题:
1、绘制数值型坐标轴(经常是Y轴),根据传入数据Array确定坐标上下限,根据坐标轴长度和设定的字体大小来计算刻度个数,然后据此计算刻度间隔,再将刻度间隔最高位以下的值向上舍入到5或者0(如最大值是123,刻度是9,则计算的刻度间隔是13.67,然后最终刻度间隔会取15,若最大0.123刻度依然为9,则最终刻度间隔取0.015),最后根据刻度间隔绘出每一个刻度值,数值特别大或者是很小的小数,导致过长无法完全显示,则转化为指数形式表示。
2、绘制文字型坐标轴(经常是X轴),这种相对容易些,主要考虑了文字过长换行显示(可设定最大行数),这样都还无法显示完整则截取能显示的长度并在最后加上一个…字符。数组长度大于最大可绘制刻度数时做抽稀,即增大取数据的步长。
3、如何绘制曲线?当然是贝赛尔曲线了,但是控制点感觉取得不是很好,画的效果没有那么理想的样子,想知道别人怎么画曲线图的。
4、tooltip。一开始想在canvas里画十字和数值提示,后面发现不太现实,然后发现在canvas上可以覆盖<cover-view>,然后就弄了三个view分别表示水平线,垂直线和tooltip框,然后根据坐标移动线和计算应该显示的点就可以了。
5、支持同时绘制多条线。感觉比较麻烦我一开始是拒绝的后来被连续追击还是实现了一个版本。多线要考虑数据长度不一的时候以最大的一组为基础,求所有数据的最大值和最小值,然后分别绘制。
6、其它各种debug,比如设计了一堆参数,字体大小,文字行距,离坐标轴的距离,最大行数,这些参数设的大坐标轴的可绘长度就会减小,比如画短线表示刻度,然后文本中间要正对刻度短线。还有一些后面注释掉了的feature,比如可以设置一个颜色组给线条和背景,然后设置成渐变,这样就能画出五彩斑斓
线形图
关于后面的内容:
后面的实现主要就是图上是啥就摆上啥。最后一个手机样子的progressbar是在蓝色进度条上覆盖图片实现的所以就很简单。
这个页面主要的难点是环形图和线形图的绘制,并且由于要加入图表框架并且要提取出Axis封装给其它图表用就扩展了很多内容所以做了很多
网友评论