人生一世,也不过是一个又一个二十四小时的叠加,在这样宝贵的光阴里,我必须明白自己的选择。–三毛
上一篇我们讲了如何实现首页布局,在这一篇中我们会讲如何实现小程序的测绘列表(也就是点击测绘进入展现的那个页面)。
因为程序列表的实现需要用到数据绑定和列表渲染,所以我们先要讲基础知识便于后续理解。
什么是数据绑定?
数据绑定必须要使用使用 Mustache 语法(双大括号)将变量包起来,可以作用于任意的元素上:
简单内容绑定
<view> {{ message }} view>
//这里绑定后就会显示Hello 三叶雨
Page({
data: {
message: 'Hello 三叶雨'
}
})
组件属性(需要在双引号之内)
<view id="item-{{id}}"> view>
//绑定后的id为item-0
Page({
data: {
id: 0
}
})
控制属性(需要在双引号之内)
<view wx:if="{{condition}}"> view>
//为true时显示该元素,为false时移除该元素,true和false切换时会重新渲染
//如果页面需要切换的频繁用建议用hidden
Page({
data: {
condition: true
}
})
关键字(需要在双引号之内)
true:boolean
类型的 true
,代表真值。
false:boolean
类型的 false
,代表假值。
<checkbox checked="{{false}}"> checkbox>
特别注意:不要直接写 checked="false"
,其计算结果是一个字符串,转成 boolean 类型后代表真值。
更多内容请看微信小程序开发文档
什么是列表渲染?
指令:wx:for
在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item
<view wx:for="{{array}}">
{{index}}: {{item.message}}//
//0 foo
//1 bar
view>
Page({
data: {
array: [{
message: 'foo',
}, {
message: 'bar'
}]
}
})
使用wx:for-item
可以指定数组当前元素的变量名,
使用 wx:for-index
可以指定数组当前下标的变量名:
<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">
{{idx}}: {{itemName.message}}
view>
这个改变变量名在多个嵌套的时候很有用
wx:for 也可以嵌套,下边是一个九九乘法表
<view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="i">
<view wx:for="{{[1, 2, 3, 4, 5, 6, 7, 8, 9]}}" wx:for-item="j">
<view wx:if="{{i <= j}}">
{{i}} * {{j}} = {{i * j}}
view>
view>
view>
block wx:for
类似 block wx:if,也可以将 wx:for 用在
标签上,以渲染一个包含多节点的结构块。例如:
<block wx:for="{{[1, 2, 3]}}">
<view> {{index}}: view>
<view> {{item}} view>
block>
这个block
特别有用,因为元素本身是不会被渲染的。
wx:key
如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 input 中的输入内容,switch 的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符。
wx:key
的值以两种形式提供
字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
保留关键字 *this
代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字。
当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。如不提供 wx:key,会报一个 warning, 如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略。
我们打开微信开发者工具在之前的文件根目录下新建component,具体目录形式如下。
pages
|—pageBar
|——component//首页
|——index.wxml
|——index.wxss
|——index.json
|——index.js
|—component
|——pages
|——mapping//测绘计算页面
|——mapping.wxml
| ——mapping.wxss
|——mapping.json
| ——mapping.js
|——resources
|——globalstyle//公共样式存放
|——globalstyle.wxss
又到了最高兴的时刻撸代码,其实主要就是页面的编写,该页面只要掌握了HTML、CSS和JS编写起来较为简单,你可以把页面想成是一个一个盒子组装起来的,也就是根据UI设计图把页面还原出来,根据UI设计图的的标注将盒子装到页面上。
编写结构
mapping.wxml
这里主要用到的知识点就是数据的绑定和列表渲染
例如下边的
wx:for="{{list}}"
我们绑定的数据是下边mapping.js中的list数组,用for循环的形式,将list里面的数据将其渲染出来,然后将其绑定到需要显示的元素,如果要将list数组里面的 name:‘坐标反算’ 内容绑定到view元素上,就可以用{{item.name}}在页面中,就可以将list同级下的name值全部渲染出来。如果要渲染其他内容也是同样的方法。
<mynavbar back="true" titleImg="../../../icon/mapping.png" imgWidth="25" imgHeight="25" color="rgba(0,0,0,.5)" titleText="测绘工程计算">mynavbar>
<view class="header-text">{{headerText}}view>
<view class="mapp-list">
<block wx:for="{{list}}" wx:key="id">
<view id="{{item.id}}" class="mapp-list-inner {{item.open ? 'mapp-list-inner-selected' : ''}}" bindtap="switchList">
<view class="left-color">view>
<view class="right-content">
<view class="left-content">
<view class="left-text">{{item.name}}
view>
<view class="left-dwon">
<block wx:for="{{item.pages}}" wx:for-item="textname" wx:key="id">
<text class="left-dwon-text">{{textname.name}}text>
block>
view>
view>
<view class="right-icon">
<image class="right-icon-img" src="{{item.url}}">image>
view>
view>
view>
<view class="kind-list-item-bd">
<view class="kind-list-item-hide {{item.open ? 'kind-list-item-show' : ''}}">
<block wx:for-items="{{item.pages}}" wx:for-item="page" wx:key="id">
<navigator class="navigator-box {{item.open ? 'navigator-box-show' : ''}}" url="../pageslist/coord/{{page.url}}/{{page.url}}">
<view class="navigator-box-text {{page.borderstyle ? 'hideborder' : ''}}">{{page.name}}
<view class="navigator-box-subtitle">{{page.subtitle}}view>
view>
<view class="navigator-box-icon">
<image src="../../../icon/right.png" style="width: 40rpx;height: 40rpx;">image>
view>
navigator>
block>
view>
view>
block>
view>
<view class="footer-text">
{{footerText}}
view>
以上结构中主要用到的知识点有数据的绑定、列表渲染等知识点。其中以上的组件为头部导航栏自定义组件,该组件是自定义的组件它适配了各种机型的兼容性,在该篇不讲该组件后续会在“微信小程序头部导航栏自定义(完美适配)”中会讲解。
<mynavbar back="true" titleImg="../../../icon/mapping.png" imgWidth="25" imgHeight="25" color="rgba(0,0,0,.5)" titleText="测绘工程计算">mynavbar>
编写
mapping.wxss
@import '../../resources/globalstyle/globalstyle.wxss';
这里是引入的样式是公共样式,这样便于代码复用和管理,也就是引入了下边的这个全局样式。
编写样式
globalstyle.wxss
/* 头部文字 */
.header-text {
width: 630rpx;
height: 100%;
margin: 33rpx auto 33rpx;
box-sizing: border-box;
text-align: center;
opacity: .3;
font-size: 28rpx;
}
/* 测绘程序列表 */
.mapp-list {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
padding-bottom: 66rpx;
box-sizing: border-box;
}
.mapp-list-inner {
width: 630rpx;
height: 235rpx;
background: linear-gradient(to top right, #ffffff, #f7f7f7);
box-shadow: 0rpx 0rpx 14rpx 0rpx rgba(126, 114, 114, 0.25);
border-radius: 20rpx;
margin-top: 33rpx;
display: flex;
justify-content: center;
align-items: center;
transition: opacity .3s;
}
/* 选中当前列表设置透明度 */
.mapp-list-inner-selected {
opacity: .3;
}
/* 坐标的小背景 */
.left-color {
height: 100%;
flex: 0 0 20rpx;
background-color: #00da00;/*/0bda6f*/
border-radius: 20rpx 0rpx 0rpx 20rpx;
}
/* 右边内容 */
.right-content {
width: 100%;
height: 235rpx;
flex: 1;
font-family: KaiTi;
font-size: 40rpx;
display: flex;
justify-content: center;
align-items: center;
padding: 0rpx 26rpx;
box-sizing: border-box;
}
/* 左边文字内容 */
.right-content .left-content {
width: 100%;
height: 100%;
flex: 1;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
border-right: 1px solid #eee;
padding-right: 10rpx;
}
/* 标题文字 */
.left-text {
flex: 0 0 80rpx;
width: 100%;
border-bottom: 1px solid #eee;
box-sizing: border-box;
display: flex;
align-items: center;
font-size: 38rpx;
}
/* 副标题文字 */
.right-content .left-dwon {
width: 460rpx;
height: 100%;
flex: 1;
color: rgb(123, 123, 123);
font-size: 30rpx;
box-sizing: border-box;
background-color: rgba(255, 255, 255, 0.549);
padding: 10rpx 0rpx 10rpx;
overflow: hidden;
}
/* 副标题文字间隔 */
.right-content .left-dwon .left-dwon-text {
height: 40px;
padding-right: 10rpx;
padding-left: 10rpx;
box-sizing: border-box;
border-right: 1px solid rgb(223, 223, 223);
}
/* 消除第一个文字内边距 */
.right-content .left-dwon .left-dwon-text:first-of-type {
padding-left: 0rpx;
}
/* 消除文字最后边框 */
.right-content .left-dwon .left-dwon-text:last-of-type {
border-right: none;
}
/* 右边的图标 */
.right-icon {
height: 100%;
flex: 0 0 100rpx;
display: flex;
justify-content: center;
align-items: center;
padding: 10rpx;
}
.right-icon .right-icon-img {
width: 60rpx;
height: 60rpx;
filter: drop-shadow(3rpx 13rpx 3rpx rgba(126, 114, 114, 0.5));
}
/* 子列表 */
.kind-list-item-bd {
width: 630rpx;
height: 100%;
border-radius: 26rpx 26rpx 26rpx 26rpx;
overflow: hidden;
background-color: #fff;
box-shadow: 0rpx 6rpx 14rpx 0rpx rgba(126, 114, 114, 0.25);
margin-top: 15rpx;
box-sizing: border-box;
opacity: .9;
}
/* 隐藏 */
.kind-list-item-hide {
height: 0rpx;
overflow: hidden;
}
/* 显示菜单 */
.kind-list-item-show {
height: auto;
}
/* 路由跳转 */
.navigator-box {
opacity: 0;
display: flex;
justify-content: center;
align-items: center;
padding: 0rpx 30rpx;
box-sizing: border-box;
overflow: hidden;
transform: translate3d(0, 380%, 0);
transition: .3s;
}
/* 满足条件后显示进行上拉动画 */
.navigator-box-show {
opacity: 1;
transform: translate3d(0, 0, 0);
}
/* 子列表下拉菜单 */
.navigator-box-text {
padding: 36rpx 36rpx 36rpx 10rpx;
flex: 1;
border-bottom: 1px solid #eee;
box-sizing: border-box;
font-size: 36rpx;
}
/* 隐藏子列表的最后一个边框 */
.navigator-box-text.hideborder {
border-bottom: none;
}
/* 副标题文字 */
.navigator-box-subtitle {
width: 480rpx;
opacity: .5;
font-size: 26rpx;
overflow: hidden;
/* 内容超出宽度时隐藏超出部分的内容 */
text-overflow: ellipsis;
/* 文本溢出时显示省略标记(...)需与overflow:hidden;一起使用。*/
word-break: keep-all;
/*不换行*/
white-space: nowrap;
/* 不换行 */
}
/* 子列表图标 */
.navigator-box-icon {
width: 100%;
height: 100%;
padding-top: 30rpx;
flex: 0 0 40rpx;
box-sizing: border-box;
}
/* 底部文字 */
.footer-text {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
color: #00da00;
padding-bottom: 33rpx;
font-size: 28rpx;
opacity: .5;
}
编写
mapping.json
每一个组件的引入必须都要在这里配置组件的名字和路径,然后在页面中通过组件的名字进行引入。
{
"usingComponents": {
"mynavbar":"../../../common/resources/navbar/navbar"
}
}
如果需要将以上组件引入到想引入的页面中去例如mapping.wxml中,就需要用到组件的名字,使用方法如下back="true"
、imgWidth="25"
等都是向组件本身传递参数,这样做的目的在于可以最大程度的提高组件的灵活性和可复用性最终做到代码的优化。
<mynavbar back="true" titleImg="../../../icon/mapping.png" imgWidth="25" imgHeight="25" color="rgba(0,0,0,.5)" titleText="测绘工程计算">mynavbar>
编写脚本
mapping.js
const app = getApp()
Page({
/**
* 页面的初始数据
*/
data: {
headerText: '该程序的产生主要是为了解决工程测绘中的一些计算问题,便于提高工作效率。',
footerText: '小哥哥正在努力开发中,敬请期待...',
list: [{
id: 0,
name: '中央子午线计算',
open: false,
url: '../../../icon/radial.png',
pages: [{
id: 0,
name: '3°带',
url: 'centralmeridianis',
subtitle: '求解中央子午线经度、带号、经度范围'
}, {
id: 1,
name: '6°带',
url: 'centralmeridiancounter',
borderstyle: true,
subtitle: '求解中央子午线经度、带号、经度范围'
}]
},
{
id: 1,
name: '角度⇌弧度',
open: false,
url: '../../../icon/deg.png',
pages: [{
id: 2,
name: '角度→度分秒',
url: 'degtodfm',
subtitle: '六十进制角度化为十进制角度'
}, {
id: 3,
name: '度分秒→角度',
url: 'dfmtodeg',
subtitle: '十进制角度化为六十进制角度'
}, {
id: 4,
name: '弧度→度及度分秒',
url: 'radtodfm',
subtitle: '弧度制化为六十进制角度、十进制角度'
}, {
id: 5,
name: '度分秒→弧度',
url: 'dfmtorad',
subtitle: '十进制角度化为弧度制',
borderstyle: true
}]
},
{
id: 2,
name: '坐标计算',
open: false,
url: '../../../icon/XY.png',
pages: [{
id: 6,
name: '坐标正算',
url: 'coordbeing',
subtitle: '直角坐标转化为极坐标'
},
{
id: 7,
name: '坐标反算',
url: 'coordreverse',
subtitle: '极坐标转化为直角坐标'
},
{
id: 8,
name: '建筑坐标→测量坐标',
url: 'buildtocity',
subtitle: '建筑坐标转换测量坐标'
}, {
id: 9,
name: '测量坐标→建筑坐标',
url: 'citytobuild',
subtitle: '测量坐标转建筑坐标',
borderstyle: true
}
]
},
{
id: 3,
name: '交会定点',
open: false,
url: '../../../icon/rendezvous.png',
pages: [{
id: 10,
name: '前方交会',
url: 'rendezvousAhead',
subtitle: '由相邻两个已知点的坐标和角度交会出未知点',
},
{
id: 11,
name: '侧边交会',
url: 'rendezvousSide',
subtitle: '由相邻两个已知点的坐标和边长交会出未知点',
},
{
id: 12,
name: '后方交会',
url: 'rendezvousRear',
subtitle: '由相邻三个已知点的坐标和角度交会出未知点',
}
]
},
{
id: 4,
name: '圆曲线放样',
open: false,
url: '../../../icon/quxian.png',
pages: [{
id: 13,
name: '单一偏角法放样',
url: 'singleCurve',
subtitle: '由圆曲线起点(ZY)至曲线上任意一点P的弦长与切线之间偏角和弦长C来确定P点的位置',
borderstyle: true
}, {
id: 14,
name: '缓和曲线放样',
url: '',
subtitle: '正在开发中',
borderstyle: true
}]
},
{
id: 5,
name: '高斯投影',
open: false,
url: '../../../icon/ellipsoid.png',
pages: [{
id: 15,
name: '高斯正算',
url: 'gaussCount',
subtitle: '由大地坐标(B,L)转高斯平面坐标(X,Y)'
}, {
id: 16,
name: '高斯反算',
url: 'gaussIan',
subtitle: '由高斯平面坐标(X,Y)转大地坐标(B,L)',
borderstyle: true
}]
}
]
},
// 控制程序列表的选择(当选中到该列表展开,其他的隐藏)
switchList: function (e) {
var id = e.currentTarget.id,
list = this.data.list;
for (var i = 0, len = list.length; i < len; ++i) {
if (list[i].id == id) {
list[i].open = !list[i].open
} else {
list[i].open = false
}
}
//改变初始数据
this.setData({
list: list
});
}
})
我们将以上代码编写完成后点击预览(图3-2 预览),代码上传完成会出现一个二维码你用绑定了小程序的那台手机微信扫一扫就可以在手机上看到你做的小程序了。
今天就说到这里,让我们听着雨声入眠。
-END-
-预告-
以下小程序是本教程最终要开发的产品可以点击体验,下一篇为从0开发《工程测绘大师》小程序之中央子午线计算实现篇(七)
“雨”见美好 - “雨”见你