@coder-pig
2019-07-04T07:52:43.000000Z
字数 23238
阅读 2262
小程序

标题和内容无关,只是前几天看卢姥爷的鬼畜:https://www.bilibili.com/video/av12611527,顺带消费下,上柱香~

看上一节《我写小程序像菜虚鲲——2、鸡你太美》的人比预想中少很多哇...

可能是我不够骚了(或者发的时间不对),不过还是建议阅读下「逆向微信小程序」那部分内容。本节来肝下
「微信小程序中布局」相关姿势点。希望你学完本章后,可以:根据设计尸给的设计稿,堆砌控件。
本节内容较多,建议点赞收藏以后有时间再看,毕竟 收藏了≈我会了,本文姿势点安排如下:
- 1、物理像素,设备独立像素,DPR,微信小程序特有尺寸rpx,设计稿尺寸;
- 2、WXSS样式导入的几种方式;
- 3、如何通过选择器定位到元素;
- 4、文档流与脱离文档流;
- 5、块级元素与行内元素,通过display属性转换;
- 6、盒子模型,box-sizing,外边距合并问题;
- 7、定位:相对定位,绝对定位,固定定位;
- 8、浮动与清除浮动;
- 9、多列布局multi-column,实现多列文本与简单图片瀑布流;
- 10、flex弹性布局;
- 11、布局实战:仿写每日优鲜首页。
px(pixel)像素,相信大家都不陌生吧,但是有三个名词要说下:
看到这里,读者可能会有疑问:为何像素还要分两种,有区别吗?
答:很久以前是没区别的,在CSS里写1px,屏幕就渲染一个物理像素,即DPR=1。随着苹果Retina技术的出现,这种局面被打破,使用Retina技术可以使用多个物理像素来渲染一个逻辑像素,屏幕尺寸没变,分辨率变高了,而人的视网膜无法分辨出屏幕上的像素点,这也是感觉Retina屏却比非Retina屏细腻的原因。
在Retina屏上DPR不再是1,而是大于1,比如iPhone 6的DPR=2,物理像素是:750x1334,对应的逻辑像素: (750x1334)/2 = 375x667。
名词科普到这里,接着说回rpx(responsive pixel),微信小程序特有尺寸单位,可以根据「屏幕宽度进行自适应」,规定:小程序屏幕宽度为750rpx。可以简单地理解为:
把页面按比例分割为750份,而每一份的大小是1rpx。
然后iPhone 6的物理像素刚好为750*1334,所以在iPhone 6中:
1rpx = 1个物理像素(1px)
所以,如果设计尸以 iPhone 6 为标准画设计稿的话,标注是多少px,小程序就直接多少rpx,不用换算,而且还不用担心在各个平台上的适配情况,卧槽,美滋滋啊!!!最后总结下结论,不难得出这样的等式:
在iPhone 6中:1rpx = 0.5px逻辑像素 = 1物理像素
关于CSS样式上节课就谈了,微信小程序中的WXSS稍微有点不一样。除了在目录下创建同名的.wxss文件会自动引用外。还可以使用@import语句 导入外部样式,相对路径,示例如下:
/* app.wxss */@import './wxss/base.wxss';
除此之外,可以使用 style属性 设置内联样式,一般是接收 动态样式 用,而把 静态样式 统一写到class中,示例如下:
<view style="color:{{color}};" />
为元素设置样式,那你也得先定位到元素是吧!有如下三类最基础的选择器:
接着是具体定位到元素的各种操作示例:
/* 标签选择器*/p{color: red;}/* id选择器 */#id-choose {color: green;}/* class选择器 */.class-choose {color: blue;}/* 对选择器进行分组,共享同一个样式,逗号隔开 */text, button, checkbox { color: green; }.text-1, .text-2 { color: gold }/* x元素内所有的y元素,选择作为x元素后代的y元素,称后代选择器或包含选择器 */view text{ color: purple }/* 还可使用*通配符选择所有元素 */view *{ color: purple }/* 父元素为x元素中的所有y元素,又称:子元素选择器 */view > text{ color: red }/* x元素后的所有y元素,又称:相邻兄弟选择器 */view + text{ color: red }/* 选择前面有x元素的每个y元素 */view ~ text{ color: red }/* 还可以通过属性来定位元素 */<view aria-role="button" aria-label="submit-label">提交</view>[aria-role]{ color: purple } /* 带有某属性 */[aria-role="button"]{ color: purple } /* 带某属性且等于xxx */[aria-label~="label"]{color: purple} /* 带某属性且包含XXX单词 */[aria-label|="submit"]{color: purple} /* 带某属性且XXX单词开头 */[aria-label^="su"]{color: purple} /* 带某属性且xx开头,不需要单词 */[aria-label$="el"]{color: purple} /* 带某属性且xx结尾,不需要单词 */[aria-label*="el"]{color: purple} /* 带某属性且包含xxx *//* 还可以搭配元素选择器玩耍 */view[aria-role]{ color: purple }/* 伪类,根据顺序定位 */.content-1 text:first-child{ color: pink } /* 父元素首个x元素 */.content-1 text:last-child{ color: pink } /* 父元素最后一个x元素 */.content-1 text:nth-child(n){ color: pink } /* 父元素第n个x元素 */.content-1 text:nth-last-child(n){ color: pink } /* 父元素倒数第n个x元素 *//* 伪元素 */<view class="content">中间元素</view>/* 元素前添加内容 */.content:before{content: "插在前面的文字";color: red;}/* 元素后添加内容 */.content:after {content:url("http://codingboy.xyz/avator.png");}
注意一点!!!
wxss无法获取本地图片资源,可使用 网络图片,base64后的图片 或 image标签!
再注意一点!!!
class属性值多个空格分隔,比如:<view class="font small blue">,其实就是指定多个class。这样写是为了CSS模块化设计,减少CSS重复代码,提高复用性。比如小程序中文本有几种,样式是基本一样的,可能只是字体大小或颜色不同,你就可以这样玩,代码示例如下:
<!-- wxml --><view class="font"><view class="font small"><view class="font small blue"><!-- wxss -->.font{ text-align: center; }.font.small{ text-size: 18rpx; } /* 小号字体 */.font.big{ text-size: 24rpx; } /* 大号字体 */.font.small.blue{ text-color: blue; } /* 蓝色小号字体 */<!-- 写了多个,如果有重复属性定义,那么后面的会覆盖前面的! -->
关于选择器更多内容可移步至:http://www.w3school.com.cn/cssref/css_selectors.asp
文档内元素的流动方向,内联元素从左往右,块级元素从上往下。
简单点说:元素在页面出现的先后顺序。
可能有些模糊,举个例子:
<view style="background-color: #FFBBFF; height: 96rpx; line-height: 96rpx; text-align:center">块元素①</view><text style="background-color: #CAFF70; ">行内元素①</text><text style="background-color: #EED8AE; ">行内元素②</text><text style="background-color: #FFA500; ">行内元素③</text><view style="background-color: #F08080; height: 96rpx; line-height: 96rpx; text-align:center">块元素②</view><view style="background-color: #EEEE00; height: 96rpx; line-height: 96rpx; text-align:center">块元素③</view>
运行结果如下:

按照:内联元素从左往右,块级元素从上往下(独占一行),这样的规则就是「正常文档流」,如果我们通过一些手段,使得元素不按照这个规则进行排布,就叫「脱离文档流」。比如为行内元素②设置一个向右的浮动:
<text style="background-color: #EED8AE; float:right">行内元素②</text>
就变成了这样:

行内元素②没有跟在①后,而③也没有跟在②后,这就是 脱离文档流。
块级元素:
- 独占一行,且宽度会占满父元素宽度,即容器的100%;
- 可设置width和height,不过即使设置了width还是会独占一行;
- 可设置margin和padding;
- 可容纳内联元素和其他块元素;
- 比如:<view>标签
行内(内联)元素:
- 不独占一行,相邻行内元素可以排在同一行;
- 宽高为文字或图片的宽高,不可变,即设置width/height无效;
- 设置margin和padding 水平方向有效,垂直方向无效;
- 只能容纳文本或者其他内联元素;
- 比如:<text>标签
可以通过 display 属性来完成行内元素和块级元素的切换,有三个可选值:
- block:设置为块元素。
- inline:设置为行内元素。
- inline-block:行内块元素,让元素具有块级元素和行内元素的特性,即能
设置宽高,margin和padding生效,还可以和其他行内元素并排。
举个例子:
<!-- test.wxml --><view class="container"><view class="block-1">块元素-1</view><view class="block-2">块元素-2</view><view class="block-3">块元素-3</view><view class="block-3">块元素-4</view><text class="inline-1">行内元素-1</text><text class="inline-2">行内元素-2</text><text class="inline-3">行内元素-3</text><view><view class="display-inline">行内块元素-1</view><view class="display-inline">行内块元素-2</view></view><view><text class="display-block">行内块元素-3</text><text class="display-block">行内块元素-4</text></view><view><view class="display-inline-block">行内块元素-5</view><view class="display-inline-block">行内块元素-6</view></view></view>
/* test.wxss *//* 块元素 */.block-1{background: red}/* 块元素可以直接设置margin和padding */.block-2 {background: greenyellow;margin-right: 50rpx;margin-top: 50rpx;padding-bottom: 25rpx;padding-left: 25rpx;}/* 块元素设置宽高,但是依旧是占一行 */.block-3 {background: paleturquoise;height: 96rpx;width: 200rpx;}/* 行内元素 */.inline-1 {background: red}/* 行内元素设置margin和padding,只有水平方向生效 */.inline-2 {background: greenyellow;margin-right: 50rpx;margin-top: 50rpx;padding-bottom: 25rpx;padding-left: 25rpx;}/* 行内元素设置宽高不生效 */.inline-3 {background: paleturquoise;height: 96rpx;width: 200rpx;}/* 块元素转换为行内元素 */.display-inline {display: inline;background: orange;margin-right: 50rpx;padding-bottom: 25rpx;padding-left: 25rpx;}/* 行内元素转换为块元素 */.display-block {display: block;background: pink;margin-right: 50rpx;padding-bottom: 25rpx;padding-left: 25rpx;margin-top: 40rpx;height: 96rpx;width: 200rpx;}/* 行内块元素,同时拥有块级元素和行内元素的特性 */.display-inline-block {display: inline-block;width: 300rpx;height: 100rpx;background: gold;margin-left: 50rpx;margin-top: 20rpx;}
运行结果如下:

元素被描绘成「矩形盒子」,这些盒子通过一个模型来描绘它的占用空间,即「盒子模型」。

如图,盒子模型通过下述四个边界来描述:
暂且把这个大盒子称为「元素框」,设置width和height指的是内容部分宽高,设置内外边距和边框不会影响内容区域的尺寸,但是会增加元素框的总尺寸。举个例子,你定义了一个48rpx*48rpx的view,但是如果你还设置了margin或padding,那么这个元素的元素框尺寸就不止48rpx*48rpx了!
如果你想设置「元素框」的宽高固定,不会因为设置了边距和边框而改变宽高,可以使用「box-sizing」来实现,该属性有下述两个可选值:
- content-box:宽高仅是内容宽高,加上padding和border,模型宽高会变大.
- border-box:以border为边界,宽高是包括边框和内边距的,设置padding模型宽高也不会变。
使用代码示例如下:
<!-- test.wxml --><view class="view-wrapper"><view class="view-1">元素1</view><view class="view-2">元素2</view></view>
/* test.wxss */page {background: gray;}view {text-align: center;width: 240rpx;height: 240rpx;line-height: 240rpx;border: 10rpx solid white;}.view-wrapper {width: 75%;background: gold;padding: 50rpx;overflow: hidden;border: none;}.view-1 {background: greenyellow;box-sizing: content-box;float: left;}.view-2 {background: blueviolet;box-sizing: border-box;float: right;}
运行结果如下:

当两个或更多垂直外边距相遇时,它们将形成一个外边距,合并后的外间距高度等于两个元素中外边距高度中的较大者。
单看概念有点含糊,写个简单的例子来帮助理解(相邻元素):
<!-- test.wxml --><view class="container"><view class="view-1">元素1</view><view class="view-2">元素2</view></view>
接着设置两个样式
.view-1 { background: gold; }.view-2 { background: red; }
接着按照下述步骤修改样式:
- ① view-1设置:margin-bottom: 50rpx。
- ② 注释掉view-1的样式,view-2设置:margin-top: 10rpx。
- ③ 去掉view-1的注释。
每一步的结果如下:

如图,两个元素最后的边距是50rpx,而不是50rpx + 10rpx = 60rpx;
接着我们再来试试 负值 的情况
.view-1 { background: gold; margin-bottom: -10rpx}.view-2 { background: red; margin-top: 30rpx}
运行结果如下:

不难发现此时的外边距是20rpx,再试试负数比整数大的情况:
.view-1 { background: gold; margin-bottom: 10rpx}.view-2 { background: red; margin-top: -20rpx}

同样不难发现此时的外边距是-10rpx,再试试两个都是负数的情况:
.view-1 { background: gold; margin-bottom: -10rpx}.view-2 { background: red; margin-top: -20rpx}

此时的外边距是-20rpx,分析计算下规律:
- 一正一负,先求绝对值差(绝对值大-绝对值小),再设置正负;
- 同正同负:去绝对值大的那个,在设置正负。
如果不想面对外边距合并问题,有下述几种规避方法:
- ① 下面的元素设置绝对定位:position:absolute;
- ② 下面的元素设置下浮动:float:left;
- ③ 任意一个盒子设置为为行内块元素:dispaly:inline-block;
除了上面这种「相邻元素」会出现外边距合并问题外「父子元素」也可能会,
没有内边距和边框隔开。写个简单的测试例子体验下:
<view class="view-1"><view class="view-2">元素</view></view>
设置两个样式,灰色背景方便对比,设置一个左边的间距方便看。
page { background: gray; }.view-1 { background: gold; }.view-2 { background: red; margin-left: 50rpx;}
接着按照下述步骤修改样式:
- ① view-2设置:margin-top:20rpx。
- ② 注释掉view-2,view-1设置:margin-top: 50rpx。
- ③ 去掉view-2的注释。
每一步的结果如下:

有下述几种方法可以规避父子元素外边距合并问题:
- ① 父元素设置内边距:padding-top:1rpx;
- ② 父元素设置:overflow: hidden;
- ③ 父元素设置边框:border:1rpx solid transparent;
关于外边距合并就说那么多吧,知道怎么规避就好,具体原因涉及到BFC(Block Formatting Context,块级格式化上下文),目前还不知道具体是啥,后面研究了再另外开一片介绍吧。
让元素脱离文档流的办法是:定位,浮动或者多列布局,这里先讲解一波定位。
通过一个例子来帮助理解,先定义一个没有使用定位的页面。
<!-- test.wxml --><view class="view-wrapper"><view class="view-1">元素1</view><view class="view-2">元素2</view><view class="view-3">元素3</view><view class="view-4">元素4</view></view>
/* test.wxss */view {display: inline-block;padding: 10px}.view-wrapper { background: gold; }.view-1 { background: greenyellow; }.view-2 { background: blueviolet; }.view-3 { background: orange; }.view-4 { background: pink; }
运行结果如下:

相对于它在「文档流中的位置的起始点」进行移动,通过例子来体验下,这里我们为元素2添加下述样式:
position: relative;left: 50rpx;top: 50rpx;
运行结果如下:

可以看到元素2从起始点开始,左边和上面都偏移50rpx,此处有个细节:偏移前的空间依旧存在!另外,另外注意 起始点 这个字眼,元素是基于起始点进行偏移的,比如为外层元素设置一个margin-left: 50rpx; 运行后的效果如下:

完全从文档流中抽离出来,可放到页面的任何位置。把上面设置的margin-left:50rpx删掉,接着把relative; 改为 absolute;运行效果如下:

可以看到,偏移前的空间已被删除!绝对布局是这样的定位的:
「相对于它的父元素来定位」,如果父元素没有设置定位,就找父元素的父元素,依次往上,直至遇到设置了定位的父元素未知,如果没找到,就会相对于文档 body进行定位。所以这里是基于body进行定位的,我们可以试下为外层的view设置position: relative,接着运行看下效果:

购物车那种数字小红点一般就是用绝对定位实现的。另外还可以通过 z-index 属性来控制重叠排列顺序,值大的在上面。改下样式:
/* index.wxss */.view-1 {background: greenyellow;position: absolute;left: 0rpx;}.view-2 {background: blueviolet;position: absolute;left: 108rpx;top: 20rpx;}.view-3 {background: orange;position: absolute;top: 100rpx;left: 20rpx}.view-4 {background: pink;position: absolute;left: 100rpx;top: 80rpx;}
运行结果如下:

加入index-z属性,控制重叠排列顺序:
/* index.wxss */.view-1 {background: greenyellow;position: absolute;left: 0rpx;z-index: 50;}.view-2 {background: blueviolet;position: absolute;left: 108rpx;top: 20rpx;z-index: 30;}.view-3 {background: orange;position: absolute;top: 100rpx;left: 20rpx;z-index: 20;}.view-4 {background: pink;position: absolute;left: 100rpx;top: 80rpx;z-index: 10;}
运行结果如下:

fixed:和absolute类似,超出屏幕的时候也是固定,参考的是窗口,常用于需要悬浮固定的场景。比如商品详情页,有个一直固定在底部的购买按钮,页面内容可以正常滚动;还有基于窗口的悬浮框等。

使元素脱离文档流,按照指定方向(左或右)移动,直到外边缘碰到包含框或另一个浮动框的边框为止。浮动前竖向排列,浮动后横向排列;float属性,可选值left左,right右。写个例子体验下,复用上面的wxml,然后设置新的样式:
/* test.wxss */view > view {line-height: 100rpx;width: 140rpx;text-align: center;}.view-1 {background: greenyellow;}.view-2 {background: blueviolet;}.view-3 {background: orange;}.view-4 {background: pink;}
运行效果如下:

接着为元素1设置一个向右的浮动 float:right; 运行结果如下:

如图,元素1脱离了文档流(所占空间被删除),然后浮动到右侧了,如果想调整元素1的位置,可以设置margin,比如这里设置margin-right:20rpx;

接着我们如果为元素2也设置一个向右的浮动:

按顺序排到了右侧,之所以没有像元素1一样贴着右边而是在元素1的左侧,因为碰到元素1浮动框了。接着为元素4页设置一个右浮动:

另外有一种情形要注意一下,把样式文件修改为:
/**index.wxss**/view > view {line-height: 100rpx;width: 240rpx;float: left;text-align: center;}.view-1 {background: greenyellow;height: 140rpx;}.view-2 {background: blueviolet;}.view-3 {background: orange;}.view-4 {background: pink;}
包含框太窄,无法容纳水平排列的三个浮动元素,那么其它浮动块向下移动,直到有足够的空间。如果浮动元素的高度不同,那么当它们向下移动时可能被其它浮动元素“卡住”.

接着说下「清除浮动」,示例代码如下:
<!-- test.wxml --><view class="view-wrapper"><view class="view-1">元素1</view><view class="view-2">元素2</view></view>
/* test.wxss */page {background: gray;}.view-wrapper {width: 75%;background: gold;padding: 10rpx;}view > view {width: 240rpx;text-align: center;}.view-1 {background: greenyellow;}.view-2 {background: blueviolet;}.view-3 {background: orange;}.view-4 {background: pink;}
运行结果如下:

接着为两个元素分别设置左和右的浮动,运行结果如下:

卧槽,怎么就这样了?浮动带来的影响,可以为通过设置属性overflow: hidden; 来清除浮动。

除此之外还可以添加一个组件,然后设置clear:both实现相同的效果。
<view style="clear:both"/>
还有一种玩法:通过伪元素:after直接添加
.view-2:after {content: "";display: block;clear: both;}
当然,你也可以直接写死容器元素的高度~
CSS3新增了一个多栏布局,用来实现「文本多列」和「瀑布流」非常方便,就顺带讲下吧~
相关属性如下:
- column-rule-style:列与列间的边框样式;
- column-rule-width:两列的边框厚度;
- column-rule-color:两列的边框颜色;
- column-rule:上述所有属性的简写,示例: column-rule: 1px solid lightblue;
- column-count:创建多列,指定需要分割的列数;
- column-width:列的宽度;
- columns:column-width 和 column-count 的简写。
- column-gap:列与列间的间隙;
- column-span:是否跨多栏显示;
- column-fill:指定如何填充列;
文本多列的代码示例如下:
<!-- test.wxml --><view class="view-wrapper"><view class="view-1">大家好,我是练习时长两年半的个人练习生菜虚鲲,我喜欢唱,跳,rap,篮球,Music!</view></view>
/* test.wxss */.view-1 {columns:auto 5;column-rule: 5rpx solid lightblue;}
运行结果如下:

实现一个简易图片瀑布流示例如下:
//test.js,新增一堆图片URLPage({data: {pics: ["http://img4.imgtn.bdimg.com/it/u=529905932,1803087578&fm=26&gp=0.jpg","http://img5.imgtn.bdimg.com/it/u=823139735,3514716232&fm=26&gp=0.jpg","http://img4.imgtn.bdimg.com/it/u=529905932,1803087578&fm=26&gp=0.jpg","http://img5.imgtn.bdimg.com/it/u=2958310391,871610286&fm=26&gp=0.jpg","http://img5.imgtn.bdimg.com/it/u=2958310391,871610286&fm=26&gp=0.jpg","http://img5.imgtn.bdimg.com/it/u=2958310391,871610286&fm=26&gp=0.jpg","http://img2.imgtn.bdimg.com/it/u=3924656228,1474918552&fm=26&gp=0.jpg","http://img4.imgtn.bdimg.com/it/u=529905932,1803087578&fm=26&gp=0.jpg","http://img2.imgtn.bdimg.com/it/u=3924656228,1474918552&fm=26&gp=0.jpg","http://img5.imgtn.bdimg.com/it/u=823139735,3514716232&fm=26&gp=0.jpg","http://img5.imgtn.bdimg.com/it/u=2958310391,871610286&fm=26&gp=0.jpg","http://img5.imgtn.bdimg.com/it/u=2958310391,871610286&fm=26&gp=0.jpg","http://img5.imgtn.bdimg.com/it/u=823139735,3514716232&fm=26&gp=0.jpg","http://img2.imgtn.bdimg.com/it/u=3924656228,1474918552&fm=26&gp=0.jpg","http://img5.imgtn.bdimg.com/it/u=823139735,3514716232&fm=26&gp=0.jpg","http://img4.imgtn.bdimg.com/it/u=529905932,1803087578&fm=26&gp=0.jpg","http://img2.imgtn.bdimg.com/it/u=3924656228,1474918552&fm=26&gp=0.jpg",]},})
<!-- test.wxml,利用wx:for生成控件 --><view class="content"><block wx:for="{{pics}}"><image src="{{item}}" mode="widthFix"></image></block></view>
/* test.wxss */page {background: gray;}.content {columns: auto 3;width: 100%;column-gap: 5rpx;}image {width: 100%;display: block;box-sizing: border-box;padding: 5rpx;}
运行结果如下:

瀑布流是实现了,但是左下角的蕾姆酱被切成两半了,如果不想切断,可以为子元素设置「break-inside」属性来防止多列布局,分页媒体和多区域上下文中的意外中断。直接在image的样式里添加:
break-inside: avoid;
运行后结果如下:

学习完前面的内容,我们可以通过display,position,float来布局,但是灵活性较差。2009年,w3c提出了一种新的布局方案:flex弹性布局,可以简便、完整、响应式地实现多种页面布局。任何元素都可以开启弹性布局,采用Flex布局的元素,称为「Flex容器(flex container)」,它里面所有的子元素会自动成为容器成员,称为「Flex 项目(flex item)」。
留意下上面的「主轴」和「侧轴」,其实就是「水平」和「垂直」两个方向。
Flex的属性分为两个部分:「容器属性」和「项目属性」,具体如下图所示:

因为属性较多,限于偏于,也不一一展示具体效果了,Runoob上有对应的效果展示,读者请自行移步至:
https://www.runoob.com/cssref/css3-pr-align-content.html,查看学习:

最后再提下和flex有关的两点:
第一点
设置flex布局后,子元素的float,clear和vertical属性将失效!
第二点:
行内元素也可以使用Flex布局,设置display:inline-flex;即可。
看那么多,总得练下手,随手找个小程序参(chao)考(xi)下吧,这里选择的是每日优鲜,利用上一节学到的姿势反编译一波,拿下图片素材。如果不知道如何反编译微信小程序,可自行查阅上一节:《我写小程序像菜虚鲲——2、鸡你太美》,反编译后的文件如下:

打开images文件夹,可以看到小程序里用到的一些图标:

试下把反编译后的项目导入到微信开发者工具中,设置记得关下域名检验

2333,这已经不算是开卷考试了,而是拿着参考答案来做题了,行吧,开始干活,实现下这个页面~
把标题设置为抠腚优鲜,打开app.json 进行如下配置:
"navigationBarTitleText": "抠腚优鲜",
接着是底部tab选项卡,关于tabBar的详细介绍可自行查阅官方文档:
https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#tabBar
把反编译后的项目里的images目录直接拷贝到项目中,打开app.json,添加下述配置:
"tabBar": {"color": "#969696","selectedColor": "#ff4891","borderStyle": "white","backgroundColor": "#ffffff","list": [{"pagePath": "pages/index/index","text": "首页","iconPath": "images/tab-bar-home.png","selectedIconPath": "images/tab-bar-home-active.png"},{"pagePath": "pages/index/index","text": "赚钱","iconPath": "images/tab-bar-group-sign.png","selectedIconPath": "images/tab-bar-group-sign-active.png"},{"pagePath": "pages/index/index","text": "分类","iconPath": "images/tab-bar-category.png","selectedIconPath": "images/tab-bar-category-active.png"},{"pagePath": "pages/index/index","text": "购物车","iconPath": "images/tab-bar-cart.png","selectedIconPath": "images/tab-bar-cart-active.png"},{"pagePath": "pages/index/index","text": "我的","iconPath": "images/tab-bar-mine.png","selectedIconPath": "images/tab-bar-mine-active.png"}]},
接着运行看下效果:

写界面之前先划分一下区域,如图,划分成六个:

划分完,接着一个个来实现~
页面结构如下:
<!-- index.wxml --><view class="container"><!-- 顶部栏 --><view class="top-wrapper"><!-- 定位部分 --><view class="location_box"><image class="location_icon" src="{{yx.location_icon_url}}"></image><image class="express_icon" src="{{yx.express_icon_url}}"></image></view><!-- 搜索部分 --><view class="search-wrapper"><image class="search_icon" src="{{yx.search_icon_url}}"></image><text class="search_text">搜索</text></view></view></view>
样式调整顺序如下:
- 定位图片宽高44rpx * 44rpx,快速图片宽高120rpx * 30rpx;
- 搜索字体大小28rxp,字体颜色#969696;
- 顶部栏宽度占满100%,高度88rpx,flex布局,space-between两端占满,垂直居中;
- 定位部分左侧偏移16rpx;
- 搜索部分,flex布局,宽高534rpx * 60rpx,圆角12rpx,背景颜色#f5f5f5,
Item水平居中,垂直居中;- 搜索部分右侧偏移16rpx;
- 搜索文字右侧偏移16rpx;
对应样式文件如下:
.top-wrapper {display: flex;width: 100%;height: 88rpx;align-items: center;justify-content: space-between;}.location_box {margin-left: 16rpx;}.location_icon {width: 44rpx;height: 44rpx;}.express_icon {width: 120rpx;height: 30rpx;}.search-wrapper {display: flex;width: 534rpx;height: 60rpx;border-radius: 12rpx;background-color: #f5f5f5;align-items: center;justify-content: center;margin-right: 16rpx;}.search_icon {width: 26rpx;height: 26rpx;margin-right: 16rpx;}.search_text {font-size: 28rpx;color: #969696;}
运行结果如下:

小程序提供了一个滑块视图容器swiper组件,利用它可以实现简单的轮播图。详细官方文档:
https://developers.weixin.qq.com/miniprogram/dev/component/swiper.html
页面结构如下:
<!-- Banner --><view class="banner-wrapper"><!-- 轮播图部分 --><swiper class="banner-swiper"><block wx:for="{{yx.banner_urls}}"><swiper-item><image class="banner_image" src="{{item}}" mode="widthFix"></image></swiper-item></block></swiper><!-- 当前页数 --><view class="pagination">1/5</view></view>
样式调整顺序如下:
对应样式文件如下:
.banner-wrapper {width: 100%;position: relative;}.banner-swiper {height: 280rpx;}.banner-item {overflow: hidden;display: block}.banner_image {width: 100%;margin-top: -168rpx;}.pagination {position: absolute;bottom: 15rpx;right: 20rpx;background: rgba(0, 0, 0, 0.3);line-height: 1;padding: 8rpx 15rpx;border-radius: 24rpx;color: #fff;font-size: 24rpx;}
运行结果如下:

不过现在只能手动滑切换图片,接着添加定时自动滑动和页数变化,swiper提供下面三个属性:
- autoplay:是否自动切换。
- interval:自动切换的时间间隔。
- duration:滑动动画时长。
添加上述属性到代码中:
<swiper class="banner-swiper" autoplay="true" interval="5000" duration="5000">
编译后可以看到图片已经能自动滚动了,接着绑定下页面切换的事件,当页面改变时切换右下角那个页数的显示,添加下述代码:
<!-- index.js -->current: 1,onPageChange: function(e) {this.setData({current: e.detail.current + 1})},<!-- index.wxml --><swiper ..bindchange="onPageChange"><view class="pagination">{{current}}/5</view>
运行结果如下:

页面结构如下:
<!-- 新人福利 --><view class="welfare"><view class="welfare-container"><!-- 顶部图片 --><image class="welfare-top-image" src="{{yx.welfare_top_url}}"></image><!-- 商品部分 --><view class="welfare-goods-container"><block wx:for="{{yx.welfare_goods}}"><view class="goods-wrapper"><image class="goods-image" src="{{item.icon}}"></image><image class="goods-price" src="{{item.price}}"></image></view></block></view><!-- 底部图片 --><image class="welfare-bottom-image" src="{{yx.welfare_bottom_url}}"></image></view></view>
先把商品部分的标签注释掉,样式调整顺序如下:
- 最外层设置flex布局,主轴从上往下,item居中,背景白色,上下内间距24rpx;
- 外层设置flex布局,主轴从上往下,item居中,宽690rpx,高434rpx,圆角12rpx,背景颜色#d4545a;
- 顶部图片100%,高度110rpx,左上和右上圆角12rpx;
- 商品部分最外层设置flex布局,主轴从上往下,item居中;
- 商品部分外层宽度660rpx,高度212rpx,圆角12rpx,上下内间距12rpx,左右内间距20rpx;
- 底部图片宽660rpx,高96rpx,圆角12rpx;
对应样式文件如下:
.welfare {display: flex;flex-direction: column;align-items: center;background: #fff;padding: 24rpx 0;}.welfare-container {display: flex;flex-direction:column;align-items:center;width: 690rpx;height: 434rpx;border-radius: 12rpx;background: #d4545a;}.welfare-top-image {width: 100%;height: 110rpx;border-radius: 12rpx 12rpx 0 0;}.welfare-goods {display: flex;flex-direction: column;align-items: center;}.welfare-goods-container {width: 660rpx;height: 212rpx;box-sizing: border-box;border-radius: 12rpx;background: #fff;padding: 12rpx 20rpx;}.welfare-bottom-image{width: 660rpx;height: 96rpx;border-radius: 12rpx;}
运行结果如下:

接着去掉商品部分的标签注释,调整样式:
- 商品外层设置flex布局,主轴从上往下,item居中,宽度134rpx,高度100%;
- 商品图片宽高130rpx;
- 商品价格宽100%,高56rpx;
.goods-wrapper {display: flex;flex-direction: column;align-items: center;width: 134rpx;height: 100%;}.goods-image {width: 130rpx;height: 130rpx;}.goods-price {width: 100%;height: 56rpx;}

em...商品直接穿出来了,修改下商品外层布局flex布局,从左网友,两端占满:
.welfare-goods-container {width: 660rpx;height: 212rpx;box-sizing: border-box;border-radius: 12rpx;background: #fff;padding: 12rpx 20rpx;display: flex;flex-direction: row;justify-content: space-between;}
运行结果如下:

页面结构如下:
<!-- 信息标签 --><view class="info-tag-container"><block wx:for="{{yx.tag_info_list}}"><view class="tag-wrapper"><image class="tag-image" src="{{item.icon}}"></image><view class="tag-text">{{item.text}}</view></view></block></view>
样式调整顺序如下:
- 最外层高度48rpx,flex布局,两端对齐留白;
- 外层flex布局,内容居中;
- 标签图片宽高24rpx,右侧偏离8rpx;
- 标签字体颜色#ff4891,大小22rpx;
对应样式文件如下:
.info-tag-container {height: 48rpx;display: flex;justify-content: space-around;}.tag-wrapper {align-items: center;display: flex;}.tag-image {width: 24rpx;height: 24rpx;margin-right: 8rpx;}.tag-text {font-size: 22rpx;color: #ff4891;}
运行结果如下:

这里的商品分类不止10个,滑动到右侧还有3个,可以用官方提供的scroll-view组件来实现,详细官方文档:
https://developers.weixin.qq.com/miniprogram/dev/component/scroll-view.html
scroll-view的用法也很简单:scroll-x横向滚动,scroll-y纵向滚动,说个小bug~
scroll-view本身的display:flex不生效,外层元素需要设置属性white-space: nowrap,内层元素需要设置属性display: inline-block,如果内层元素需要用到flex布局,可以设置属性display: inline-flex; 还有内层元素不可以用float浮动。
接着滑动部分的,一个简单的套路是:宽度写死 = 每个商品View的宽度 * 7,不过这种方法有点low。看了下每日优鲜的玩法,是把这里拆分成了两个部分,这里照葫芦画瓢实现一波。
页面结构如下:
<!-- 商品类别 --><view class="good_category_container"><scroll-view scroll-x class="scroll-view"><view class="category_first"><block wx:for="{{yx.category_list_first}}" wx:key="key"><view class="good_category"><image class="category-image" src="{{item.icon}}"></image><view class="category-text">{{item.text}}</view></view></block></view><view class="category_second"><block wx:for="{{yx.category_list_second}}" wx:key="key"><view class="good_category"><image class="category-image" src="{{item.icon}}"></image><view class="category-text">{{item.text}}</view></view></block></view></scroll-view></view>
样式调整顺序如下:
- 最外层宽度100%,设置white-space: nowrap;
- scroll-view设置最大高度360rpx;
- 左侧部分,宽度100%,inline-flex,自动换行;
- 右侧部分,高度384rpx,inline-flex,主轴从上往下;
- 分类外层,flex布局,水平竖直居中,主轴从上往下,宽150rpx,顶部偏移24rpx;
- 分类图标,宽高104rpx;
- 分类文本,高34rpx,行高34rpx让文本垂直居中,字体颜色#474245,大小24rpx,顶部偏移10rpx;
对应样式文件如下:
.good_category_container {width: 100%;white-space: nowrap;}.scroll-view {max-height: 360rpx;}.category_first {width: 100%;display: inline-flex;flex-wrap: wrap;}.category_second {height: 384rpx;display: inline-flex;flex-direction: column;flex-wrap: wrap;}.good_category {display: flex;flex-direction: column;justify-content: center;align-items: center;width: 150rpx;margin-top: 24rpx;}.category-image {width: 104rpx;height: 104rpx;}.category-text {font-size: 24rpx;color: #474245;height: 34rpx;line-height: 34rpx;margin-top: 10rpx;}
运行结果如下:

这种悬浮在页面上,不会因为页面滚动而滚动的部分,可以使用固定定位来实现。
页面结构如下:
<!-- 浮动提醒 --><view class="tip-container"><image class="tip-image" src="{{yx.tip_image_url}}"></image></view>
样式调整顺序如下:
- 外层容器设置绝对定位,宽98rpx,高130rpx,离右边28rpx,离底部40rpx;
- 图片设置宽高100%;
对应样式文件如下:
.tip-container {position: fixed;right: 28rpx;bottom: 40rpx;width: 98rpx;height: 130rpx;}.tip-image{width: 100%;height: 100%;}
运行结果如下:


Tips:真机预览时发现分类那里滑动时有滚动条,可以在wxss加入下述样式隐藏滚动条~
::-webkit-scrollbar {width: 0;height: 0;color: transparent;}
相信读者学完本节,基本可以应付日常小程序页面的堆砌了。这种实操性比较强的东西,切忌死记,建议自己找些小程序仿写下,熟能生巧,别说没有设计稿,没有图片没有尺寸,上节学的反编译技能呢???

笔者不是专业前端,以上内容都是现学现卖,如有纰漏或建议,欢迎评论区指出,谢谢~
源码整理下再丢Gayhub,后面再发个地址哈~(另外,蹲个深圳3年半的Android坑)
参考文献: