[关闭]
@coder-pig 2019-07-04T15:52:43.000000Z 字数 23238 阅读 1718

我写小程序像菜虚鲲——3、你能17张牌把我秒杀,我当场把电脑屏幕吃掉

小程序


标题和内容无关,只是前几天看卢姥爷的鬼畜: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、布局实战:仿写每日优鲜首页。

0x1、微信小程序特有单位——rpx


px(pixel)像素,相信大家都不陌生吧,但是有三个名词要说下:

看到这里,读者可能会有疑问:为何像素还要分两种,有区别吗

答:很久以前是没区别的,在CSS里写1px,屏幕就渲染一个物理像素,即DPR=1。随着苹果Retina技术的出现,这种局面被打破,使用Retina技术可以使用多个物理像素来渲染一个逻辑像素,屏幕尺寸没变,分辨率变高了,而人的视网膜无法分辨出屏幕上的像素点,这也是感觉Retina屏却比非Retina屏细腻的原因。

在Retina屏上DPR不再是1,而是大于1,比如iPhone 6DPR=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物理像素


0x2、WXSS样式导入


关于CSS样式上节课就谈了,微信小程序中的WXSS稍微有点不一样。除了在目录下创建同名的.wxss文件会自动引用外。还可以使用@import语句 导入外部样式,相对路径,示例如下:

  1. /* app.wxss */
  2. @import './wxss/base.wxss';

除此之外,可以使用 style属性 设置内联样式,一般是接收 动态样式 用,而把 静态样式 统一写到class中,示例如下:

  1. <view style="color:{{color}};" />

0x3、选择器定位元素


为元素设置样式,那你也得先定位到元素是吧!有如下三类最基础的选择器:

接着是具体定位到元素的各种操作示例:

  1. /* 标签选择器*/
  2. p{color: red;}
  3. /* id选择器 */
  4. #id-choose {color: green;}
  5. /* class选择器 */
  6. .class-choose {color: blue;}
  7. /* 对选择器进行分组,共享同一个样式,逗号隔开 */
  8. text, button, checkbox { color: green; }
  9. .text-1, .text-2 { color: gold }
  10. /* x元素内所有的y元素,选择作为x元素后代的y元素,称后代选择器或包含选择器 */
  11. view text{ color: purple }
  12. /* 还可使用*通配符选择所有元素 */
  13. view *{ color: purple }
  14. /* 父元素为x元素中的所有y元素,又称:子元素选择器 */
  15. view > text{ color: red }
  16. /* x元素后的所有y元素,又称:相邻兄弟选择器 */
  17. view + text{ color: red }
  18. /* 选择前面有x元素的每个y元素 */
  19. view ~ text{ color: red }
  20. /* 还可以通过属性来定位元素 */
  21. <view aria-role="button" aria-label="submit-label">提交</view>
  22. [aria-role]{ color: purple } /* 带有某属性 */
  23. [aria-role="button"]{ color: purple } /* 带某属性且等于xxx */
  24. [aria-label~="label"]{color: purple} /* 带某属性且包含XXX单词 */
  25. [aria-label|="submit"]{color: purple} /* 带某属性且XXX单词开头 */
  26. [aria-label^="su"]{color: purple} /* 带某属性且xx开头,不需要单词 */
  27. [aria-label$="el"]{color: purple} /* 带某属性且xx结尾,不需要单词 */
  28. [aria-label*="el"]{color: purple} /* 带某属性且包含xxx */
  29. /* 还可以搭配元素选择器玩耍 */
  30. view[aria-role]{ color: purple }
  31. /* 伪类,根据顺序定位 */
  32. .content-1 text:first-child{ color: pink } /* 父元素首个x元素 */
  33. .content-1 text:last-child{ color: pink } /* 父元素最后一个x元素 */
  34. .content-1 text:nth-child(n){ color: pink } /* 父元素第n个x元素 */
  35. .content-1 text:nth-last-child(n){ color: pink } /* 父元素倒数第n个x元素 */
  36. /* 伪元素 */
  37. <view class="content">中间元素</view>
  38. /* 元素前添加内容 */
  39. .content:before{
  40. content: "插在前面的文字";
  41. color: red;
  42. }
  43. /* 元素后添加内容 */
  44. .content:after {
  45. content:url("http://codingboy.xyz/avator.png");
  46. }

注意一点!!!

wxss无法获取本地图片资源,可使用 网络图片base64后的图片image标签

再注意一点!!!

class属性值多个空格分隔,比如:<view class="font small blue">,其实就是指定多个class。这样写是为了CSS模块化设计减少CSS重复代码,提高复用性。比如小程序中文本有几种,样式是基本一样的,可能只是字体大小或颜色不同,你就可以这样玩,代码示例如下:

  1. <!-- wxml -->
  2. <view class="font">
  3. <view class="font small">
  4. <view class="font small blue">
  5. <!-- wxss -->
  6. .font{ text-align: center; }
  7. .font.small{ text-size: 18rpx; } /* 小号字体 */
  8. .font.big{ text-size: 24rpx; } /* 大号字体 */
  9. .font.small.blue{ text-color: blue; } /* 蓝色小号字体 */
  10. <!-- 写了多个,如果有重复属性定义,那么后面的会覆盖前面的! -->

关于选择器更多内容可移步至:http://www.w3school.com.cn/cssref/css_selectors.asp


0x4、文档流

文档内元素的流动方向,内联元素从左往右,块级元素从上往下

简单点说:元素在页面出现的先后顺序

可能有些模糊,举个例子

  1. <view style="background-color: #FFBBFF; height: 96rpx; line-height: 96rpx; text-align:center">块元素①</view>
  2. <text style="background-color: #CAFF70; ">行内元素①</text>
  3. <text style="background-color: #EED8AE; ">行内元素②</text>
  4. <text style="background-color: #FFA500; ">行内元素③</text>
  5. <view style="background-color: #F08080; height: 96rpx; line-height: 96rpx; text-align:center">块元素②</view>
  6. <view style="background-color: #EEEE00; height: 96rpx; line-height: 96rpx; text-align:center">块元素③</view>

运行结果如下

按照:内联元素从左往右,块级元素从上往下(独占一行),这样的规则就是「正常文档流」,如果我们通过一些手段,使得元素不按照这个规则进行排布,就叫「脱离文档流」。比如为行内元素②设置一个向右的浮动:

  1. <text style="background-color: #EED8AE; float:right">行内元素②</text>

就变成了这样:

行内元素②没有跟在①后,而③也没有跟在②后,这就是 脱离文档流


0x5、块级元素(block)与行内(inline)元素

块级元素

  • 独占一行,且宽度会占满父元素宽度,即容器的100%;
  • 可设置width和height,不过即使设置了width还是会独占一行
  • 可设置margin和padding;
  • 可容纳内联元素和其他块元素;
  • 比如:<view>标签

行内(内联)元素

  • 不独占一行,相邻行内元素可以排在同一行;
  • 宽高为文字或图片的宽高不可变,即设置width/height无效
  • 设置margin和padding 水平方向有效垂直方向无效
  • 只能容纳文本或者其他内联元素;
  • 比如:<text>标签

可以通过 display 属性来完成行内元素和块级元素的切换,有三个可选值:

  • block:设置为块元素。
  • inline:设置为行内元素。
  • inline-block行内块元素,让元素具有块级元素和行内元素的特性,即能
    设置宽高,margin和padding生效,还可以和其他行内元素并排。

举个例子

  1. <!-- test.wxml -->
  2. <view class="container">
  3. <view class="block-1">块元素-1</view>
  4. <view class="block-2">块元素-2</view>
  5. <view class="block-3">块元素-3</view>
  6. <view class="block-3">块元素-4</view>
  7. <text class="inline-1">行内元素-1</text>
  8. <text class="inline-2">行内元素-2</text>
  9. <text class="inline-3">行内元素-3</text>
  10. <view>
  11. <view class="display-inline">行内块元素-1</view>
  12. <view class="display-inline">行内块元素-2</view>
  13. </view>
  14. <view>
  15. <text class="display-block">行内块元素-3</text>
  16. <text class="display-block">行内块元素-4</text>
  17. </view>
  18. <view>
  19. <view class="display-inline-block">行内块元素-5</view>
  20. <view class="display-inline-block">行内块元素-6</view>
  21. </view>
  22. </view>
  1. /* test.wxss */
  2. /* 块元素 */
  3. .block-1{
  4. background: red
  5. }
  6. /* 块元素可以直接设置margin和padding */
  7. .block-2 {
  8. background: greenyellow;
  9. margin-right: 50rpx;
  10. margin-top: 50rpx;
  11. padding-bottom: 25rpx;
  12. padding-left: 25rpx;
  13. }
  14. /* 块元素设置宽高,但是依旧是占一行 */
  15. .block-3 {
  16. background: paleturquoise;
  17. height: 96rpx;
  18. width: 200rpx;
  19. }
  20. /* 行内元素 */
  21. .inline-1 {
  22. background: red
  23. }
  24. /* 行内元素设置margin和padding,只有水平方向生效 */
  25. .inline-2 {
  26. background: greenyellow;
  27. margin-right: 50rpx;
  28. margin-top: 50rpx;
  29. padding-bottom: 25rpx;
  30. padding-left: 25rpx;
  31. }
  32. /* 行内元素设置宽高不生效 */
  33. .inline-3 {
  34. background: paleturquoise;
  35. height: 96rpx;
  36. width: 200rpx;
  37. }
  38. /* 块元素转换为行内元素 */
  39. .display-inline {
  40. display: inline;
  41. background: orange;
  42. margin-right: 50rpx;
  43. padding-bottom: 25rpx;
  44. padding-left: 25rpx;
  45. }
  46. /* 行内元素转换为块元素 */
  47. .display-block {
  48. display: block;
  49. background: pink;
  50. margin-right: 50rpx;
  51. padding-bottom: 25rpx;
  52. padding-left: 25rpx;
  53. margin-top: 40rpx;
  54. height: 96rpx;
  55. width: 200rpx;
  56. }
  57. /* 行内块元素,同时拥有块级元素和行内元素的特性 */
  58. .display-inline-block {
  59. display: inline-block;
  60. width: 300rpx;
  61. height: 100rpx;
  62. background: gold;
  63. margin-left: 50rpx;
  64. margin-top: 20rpx;
  65. }

运行结果如下


0x6、盒子模型

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

如图,盒子模型通过下述四个边界来描述:

暂且把这个大盒子称为「元素框」,设置width和height指的是内容部分宽高设置内外边距和边框不会影响内容区域的尺寸,但是会增加元素框的总尺寸。举个例子,你定义了一个48rpx*48rpx的view,但是如果你还设置了margin或padding,那么这个元素的元素框尺寸就不止48rpx*48rpx了!


① box-sizing属性

如果你想设置「元素框」的宽高固定,不会因为设置了边距和边框而改变宽高,可以使用「box-sizing」来实现,该属性有下述两个可选值:

  • content-box:宽高仅是内容宽高,加上padding和border,模型宽高会变大.
  • border-box以border为边界,宽高是包括边框和内边距的,设置padding模型宽高也不会变。

使用代码示例如下

  1. <!-- test.wxml -->
  2. <view class="view-wrapper">
  3. <view class="view-1">元素1</view>
  4. <view class="view-2">元素2</view>
  5. </view>
  1. /* test.wxss */
  2. page {
  3. background: gray;
  4. }
  5. view {
  6. text-align: center;
  7. width: 240rpx;
  8. height: 240rpx;
  9. line-height: 240rpx;
  10. border: 10rpx solid white;
  11. }
  12. .view-wrapper {
  13. width: 75%;
  14. background: gold;
  15. padding: 50rpx;
  16. overflow: hidden;
  17. border: none;
  18. }
  19. .view-1 {
  20. background: greenyellow;
  21. box-sizing: content-box;
  22. float: left;
  23. }
  24. .view-2 {
  25. background: blueviolet;
  26. box-sizing: border-box;
  27. float: right;
  28. }

运行结果如下


② 外边距合并问题

当两个或更多垂直外边距相遇时,它们将形成一个外边距,合并后的外间距高度等于两个元素中外边距高度中的较大者

单看概念有点含糊,写个简单的例子来帮助理解(相邻元素):

  1. <!-- test.wxml -->
  2. <view class="container">
  3. <view class="view-1">元素1</view>
  4. <view class="view-2">元素2</view>
  5. </view>

接着设置两个样式

  1. .view-1 { background: gold; }
  2. .view-2 { background: red; }

接着按照下述步骤修改样式:

  • ① view-1设置:margin-bottom: 50rpx
  • ② 注释掉view-1的样式,view-2设置:margin-top: 10rpx
  • ③ 去掉view-1的注释。

每一步的结果如下:

如图,两个元素最后的边距是50rpx,而不是50rpx + 10rpx = 60rpx
接着我们再来试试 负值 的情况

  1. .view-1 { background: gold; margin-bottom: -10rpx}
  2. .view-2 { background: red; margin-top: 30rpx}

运行结果如下

不难发现此时的外边距是20rpx,再试试负数比整数大的情况:

  1. .view-1 { background: gold; margin-bottom: 10rpx}
  2. .view-2 { background: red; margin-top: -20rpx}

同样不难发现此时的外边距是-10rpx,再试试两个都是负数的情况:

  1. .view-1 { background: gold; margin-bottom: -10rpx}
  2. .view-2 { background: red; margin-top: -20rpx}

此时的外边距是-20rpx,分析计算下规律:

  • 一正一负,先求绝对值差(绝对值大-绝对值小),再设置正负;
  • 同正同负:去绝对值大的那个,在设置正负。

如果不想面对外边距合并问题,有下述几种规避方法:

  • ① 下面的元素设置绝对定位:position:absolute;
  • ② 下面的元素设置下浮动:float:left;
  • ③ 任意一个盒子设置为为行内块元素:dispaly:inline-block

除了上面这种「相邻元素」会出现外边距合并问题外「父子元素」也可能会,
没有内边距和边框隔开。写个简单的测试例子体验下:

  1. <view class="view-1">
  2. <view class="view-2">元素</view>
  3. </view>

设置两个样式,灰色背景方便对比,设置一个左边的间距方便看。

  1. page { background: gray; }
  2. .view-1 { background: gold; }
  3. .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,块级格式化上下文),目前还不知道具体是啥,后面研究了再另外开一片介绍吧。


0x7、定位

让元素脱离文档流的办法是:定位,浮动或者多列布局,这里先讲解一波定位。
通过一个例子来帮助理解,先定义一个没有使用定位的页面。

  1. <!-- test.wxml -->
  2. <view class="view-wrapper">
  3. <view class="view-1">元素1</view>
  4. <view class="view-2">元素2</view>
  5. <view class="view-3">元素3</view>
  6. <view class="view-4">元素4</view>
  7. </view>
  1. /* test.wxss */
  2. view {
  3. display: inline-block;
  4. padding: 10px
  5. }
  6. .view-wrapper { background: gold; }
  7. .view-1 { background: greenyellow; }
  8. .view-2 { background: blueviolet; }
  9. .view-3 { background: orange; }
  10. .view-4 { background: pink; }

运行结果如下


① 相对定位

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

  1. position: relative;
  2. left: 50rpx;
  3. top: 50rpx;

运行结果如下

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


② 绝对定位

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

可以看到,偏移前的空间已被删除!绝对布局是这样的定位的:

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

购物车那种数字小红点一般就是用绝对定位实现的。另外还可以通过 z-index 属性来控制重叠排列顺序,值大的在上面。改下样式:

  1. /* index.wxss */
  2. .view-1 {
  3. background: greenyellow;
  4. position: absolute;
  5. left: 0rpx;
  6. }
  7. .view-2 {
  8. background: blueviolet;
  9. position: absolute;
  10. left: 108rpx;
  11. top: 20rpx;
  12. }
  13. .view-3 {
  14. background: orange;
  15. position: absolute;
  16. top: 100rpx;
  17. left: 20rpx
  18. }
  19. .view-4 {
  20. background: pink;
  21. position: absolute;
  22. left: 100rpx;
  23. top: 80rpx;
  24. }

运行结果如下

加入index-z属性,控制重叠排列顺序:

  1. /* index.wxss */
  2. .view-1 {
  3. background: greenyellow;
  4. position: absolute;
  5. left: 0rpx;
  6. z-index: 50;
  7. }
  8. .view-2 {
  9. background: blueviolet;
  10. position: absolute;
  11. left: 108rpx;
  12. top: 20rpx;
  13. z-index: 30;
  14. }
  15. .view-3 {
  16. background: orange;
  17. position: absolute;
  18. top: 100rpx;
  19. left: 20rpx;
  20. z-index: 20;
  21. }
  22. .view-4 {
  23. background: pink;
  24. position: absolute;
  25. left: 100rpx;
  26. top: 80rpx;
  27. z-index: 10;
  28. }

运行结果如下


③ 固定定位

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


0x8、浮动与清除浮动

使元素脱离文档流,按照指定方向(左或右)移动直到外边缘碰到包含框或另一个浮动框的边框为止。浮动前竖向排列,浮动后横向排列;float属性,可选值left左,right右。写个例子体验下,复用上面的wxml,然后设置新的样式:

  1. /* test.wxss */
  2. view > view {
  3. line-height: 100rpx;
  4. width: 140rpx;
  5. text-align: center;
  6. }
  7. .view-1 {
  8. background: greenyellow;
  9. }
  10. .view-2 {
  11. background: blueviolet;
  12. }
  13. .view-3 {
  14. background: orange;
  15. }
  16. .view-4 {
  17. background: pink;
  18. }

运行效果如下

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

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

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

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

另外有一种情形要注意一下,把样式文件修改为:

  1. /**index.wxss**/
  2. view > view {
  3. line-height: 100rpx;
  4. width: 240rpx;
  5. float: left;
  6. text-align: center;
  7. }
  8. .view-1 {
  9. background: greenyellow;
  10. height: 140rpx;
  11. }
  12. .view-2 {
  13. background: blueviolet;
  14. }
  15. .view-3 {
  16. background: orange;
  17. }
  18. .view-4 {
  19. background: pink;
  20. }

包含框太窄,无法容纳水平排列的三个浮动元素,那么其它浮动块向下移动,直到有足够的空间。如果浮动元素的高度不同,那么当它们向下移动时可能被其它浮动元素“卡住”.

接着说下「清除浮动」,示例代码如下:

  1. <!-- test.wxml -->
  2. <view class="view-wrapper">
  3. <view class="view-1">元素1</view>
  4. <view class="view-2">元素2</view>
  5. </view>
  1. /* test.wxss */
  2. page {
  3. background: gray;
  4. }
  5. .view-wrapper {
  6. width: 75%;
  7. background: gold;
  8. padding: 10rpx;
  9. }
  10. view > view {
  11. width: 240rpx;
  12. text-align: center;
  13. }
  14. .view-1 {
  15. background: greenyellow;
  16. }
  17. .view-2 {
  18. background: blueviolet;
  19. }
  20. .view-3 {
  21. background: orange;
  22. }
  23. .view-4 {
  24. background: pink;
  25. }

运行结果如下

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

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

除此之外还可以添加一个组件,然后设置clear:both实现相同的效果。

  1. <view style="clear:both"/>

还有一种玩法:通过伪元素:after直接添加

  1. .view-2:after {
  2. content: "";
  3. display: block;
  4. clear: both;
  5. }

当然,你也可以直接写死容器元素的高度~


0x9、多列布局multi-column

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:指定如何填充列;

文本多列的代码示例如下

  1. <!-- test.wxml -->
  2. <view class="view-wrapper">
  3. <view class="view-1">大家好,我是练习时长两年半的个人练习生菜虚鲲,我喜欢唱,跳,rap,篮球,Music!</view>
  4. </view>
  1. /* test.wxss */
  2. .view-1 {
  3. columns:auto 5;
  4. column-rule: 5rpx solid lightblue;
  5. }

运行结果如下

实现一个简易图片瀑布流示例如下

  1. //test.js,新增一堆图片URL
  2. Page({
  3. data: {
  4. pics: [
  5. "http://img4.imgtn.bdimg.com/it/u=529905932,1803087578&fm=26&gp=0.jpg",
  6. "http://img5.imgtn.bdimg.com/it/u=823139735,3514716232&fm=26&gp=0.jpg",
  7. "http://img4.imgtn.bdimg.com/it/u=529905932,1803087578&fm=26&gp=0.jpg",
  8. "http://img5.imgtn.bdimg.com/it/u=2958310391,871610286&fm=26&gp=0.jpg",
  9. "http://img5.imgtn.bdimg.com/it/u=2958310391,871610286&fm=26&gp=0.jpg",
  10. "http://img5.imgtn.bdimg.com/it/u=2958310391,871610286&fm=26&gp=0.jpg",
  11. "http://img2.imgtn.bdimg.com/it/u=3924656228,1474918552&fm=26&gp=0.jpg",
  12. "http://img4.imgtn.bdimg.com/it/u=529905932,1803087578&fm=26&gp=0.jpg",
  13. "http://img2.imgtn.bdimg.com/it/u=3924656228,1474918552&fm=26&gp=0.jpg",
  14. "http://img5.imgtn.bdimg.com/it/u=823139735,3514716232&fm=26&gp=0.jpg",
  15. "http://img5.imgtn.bdimg.com/it/u=2958310391,871610286&fm=26&gp=0.jpg",
  16. "http://img5.imgtn.bdimg.com/it/u=2958310391,871610286&fm=26&gp=0.jpg",
  17. "http://img5.imgtn.bdimg.com/it/u=823139735,3514716232&fm=26&gp=0.jpg",
  18. "http://img2.imgtn.bdimg.com/it/u=3924656228,1474918552&fm=26&gp=0.jpg",
  19. "http://img5.imgtn.bdimg.com/it/u=823139735,3514716232&fm=26&gp=0.jpg",
  20. "http://img4.imgtn.bdimg.com/it/u=529905932,1803087578&fm=26&gp=0.jpg",
  21. "http://img2.imgtn.bdimg.com/it/u=3924656228,1474918552&fm=26&gp=0.jpg",
  22. ]
  23. },
  24. })
  1. <!-- test.wxml,利用wx:for生成控件 -->
  2. <view class="content">
  3. <block wx:for="{{pics}}">
  4. <image src="{{item}}" mode="widthFix"></image>
  5. </block>
  6. </view>
  1. /* test.wxss */
  2. page {
  3. background: gray;
  4. }
  5. .content {
  6. columns: auto 3;
  7. width: 100%;
  8. column-gap: 5rpx;
  9. }
  10. image {
  11. width: 100%;
  12. display: block;
  13. box-sizing: border-box;
  14. padding: 5rpx;
  15. }

运行结果如下

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

  1. break-inside: avoid;

运行后结果如下


0x10、flex弹性布局

学习完前面的内容,我们可以通过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;即可。


0x0、布局实战:写个抠腚优鲜的首页

看那么多,总得练下手,随手找个小程序参(chao)考(xi)下吧,这里选择的是每日优鲜,利用上一节学到的姿势反编译一波,拿下图片素材。如果不知道如何反编译微信小程序,可自行查阅上一节:《我写小程序像菜虚鲲——2、鸡你太美》,反编译后的文件如下:

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

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

2333,这已经不算是开卷考试了,而是拿着参考答案来做题了,行吧,开始干活,实现下这个页面~


① 设置标题与底部tab选项卡

把标题设置为抠腚优鲜,打开app.json 进行如下配置:

  1. "navigationBarTitleText": "抠腚优鲜",

接着是底部tab选项卡,关于tabBar的详细介绍可自行查阅官方文档:
https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#tabBar
把反编译后的项目里的images目录直接拷贝到项目中,打开app.json,添加下述配置:

  1. "tabBar": {
  2. "color": "#969696",
  3. "selectedColor": "#ff4891",
  4. "borderStyle": "white",
  5. "backgroundColor": "#ffffff",
  6. "list": [
  7. {
  8. "pagePath": "pages/index/index",
  9. "text": "首页",
  10. "iconPath": "images/tab-bar-home.png",
  11. "selectedIconPath": "images/tab-bar-home-active.png"
  12. },
  13. {
  14. "pagePath": "pages/index/index",
  15. "text": "赚钱",
  16. "iconPath": "images/tab-bar-group-sign.png",
  17. "selectedIconPath": "images/tab-bar-group-sign-active.png"
  18. },
  19. {
  20. "pagePath": "pages/index/index",
  21. "text": "分类",
  22. "iconPath": "images/tab-bar-category.png",
  23. "selectedIconPath": "images/tab-bar-category-active.png"
  24. },
  25. {
  26. "pagePath": "pages/index/index",
  27. "text": "购物车",
  28. "iconPath": "images/tab-bar-cart.png",
  29. "selectedIconPath": "images/tab-bar-cart-active.png"
  30. },
  31. {
  32. "pagePath": "pages/index/index",
  33. "text": "我的",
  34. "iconPath": "images/tab-bar-mine.png",
  35. "selectedIconPath": "images/tab-bar-mine-active.png"
  36. }
  37. ]
  38. },

接着运行看下效果:


② 页面区域划分

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

划分完,接着一个个来实现~


③ 顶部栏

页面结构如下

  1. <!-- index.wxml -->
  2. <view class="container">
  3. <!-- 顶部栏 -->
  4. <view class="top-wrapper">
  5. <!-- 定位部分 -->
  6. <view class="location_box">
  7. <image class="location_icon" src="{{yx.location_icon_url}}"></image>
  8. <image class="express_icon" src="{{yx.express_icon_url}}"></image>
  9. </view>
  10. <!-- 搜索部分 -->
  11. <view class="search-wrapper">
  12. <image class="search_icon" src="{{yx.search_icon_url}}"></image>
  13. <text class="search_text">搜索</text>
  14. </view>
  15. </view>
  16. </view>

样式调整顺序如下

  • 定位图片宽高44rpx * 44rpx,快速图片宽高120rpx * 30rpx;
  • 搜索字体大小28rxp,字体颜色#969696;
  • 顶部栏宽度占满100%,高度88rpx,flex布局,space-between两端占满,垂直居中;
  • 定位部分左侧偏移16rpx;
  • 搜索部分,flex布局,宽高534rpx * 60rpx,圆角12rpx,背景颜色#f5f5f5,
    Item水平居中,垂直居中;
  • 搜索部分右侧偏移16rpx;
  • 搜索文字右侧偏移16rpx;

对应样式文件如下

  1. .top-wrapper {
  2. display: flex;
  3. width: 100%;
  4. height: 88rpx;
  5. align-items: center;
  6. justify-content: space-between;
  7. }
  8. .location_box {
  9. margin-left: 16rpx;
  10. }
  11. .location_icon {
  12. width: 44rpx;
  13. height: 44rpx;
  14. }
  15. .express_icon {
  16. width: 120rpx;
  17. height: 30rpx;
  18. }
  19. .search-wrapper {
  20. display: flex;
  21. width: 534rpx;
  22. height: 60rpx;
  23. border-radius: 12rpx;
  24. background-color: #f5f5f5;
  25. align-items: center;
  26. justify-content: center;
  27. margin-right: 16rpx;
  28. }
  29. .search_icon {
  30. width: 26rpx;
  31. height: 26rpx;
  32. margin-right: 16rpx;
  33. }
  34. .search_text {
  35. font-size: 28rpx;
  36. color: #969696;
  37. }

运行结果如下


④ Banner轮播图

小程序提供了一个滑块视图容器swiper组件,利用它可以实现简单的轮播图。详细官方文档:
https://developers.weixin.qq.com/miniprogram/dev/component/swiper.html

页面结构如下

  1. <!-- Banner -->
  2. <view class="banner-wrapper">
  3. <!-- 轮播图部分 -->
  4. <swiper class="banner-swiper">
  5. <block wx:for="{{yx.banner_urls}}">
  6. <swiper-item>
  7. <image class="banner_image" src="{{item}}" mode="widthFix"></image>
  8. </swiper-item>
  9. </block>
  10. </swiper>
  11. <!-- 当前页数 -->
  12. <view class="pagination">1/5</view>
  13. </view>

样式调整顺序如下:

对应样式文件如下

  1. .banner-wrapper {
  2. width: 100%;
  3. position: relative;
  4. }
  5. .banner-swiper {
  6. height: 280rpx;
  7. }
  8. .banner-item {
  9. overflow: hidden;
  10. display: block
  11. }
  12. .banner_image {
  13. width: 100%;
  14. margin-top: -168rpx;
  15. }
  16. .pagination {
  17. position: absolute;
  18. bottom: 15rpx;
  19. right: 20rpx;
  20. background: rgba(0, 0, 0, 0.3);
  21. line-height: 1;
  22. padding: 8rpx 15rpx;
  23. border-radius: 24rpx;
  24. color: #fff;
  25. font-size: 24rpx;
  26. }

运行结果如下

不过现在只能手动滑切换图片,接着添加定时自动滑动和页数变化,swiper提供下面三个属性:

  • autoplay:是否自动切换。
  • interval:自动切换的时间间隔。
  • duration:滑动动画时长。

添加上述属性到代码中:

  1. <swiper class="banner-swiper" autoplay="true" interval="5000" duration="5000">

编译后可以看到图片已经能自动滚动了,接着绑定下页面切换的事件,当页面改变时切换右下角那个页数的显示,添加下述代码:

  1. <!-- index.js -->
  2. current: 1,
  3. onPageChange: function(e) {
  4. this.setData({
  5. current: e.detail.current + 1
  6. })
  7. },
  8. <!-- index.wxml -->
  9. <swiper ..bindchange="onPageChange">
  10. <view class="pagination">{{current}}/5</view>

运行结果如下


⑤ 新人福利

页面结构如下

  1. <!-- 新人福利 -->
  2. <view class="welfare">
  3. <view class="welfare-container">
  4. <!-- 顶部图片 -->
  5. <image class="welfare-top-image" src="{{yx.welfare_top_url}}"></image>
  6. <!-- 商品部分 -->
  7. <view class="welfare-goods-container">
  8. <block wx:for="{{yx.welfare_goods}}">
  9. <view class="goods-wrapper">
  10. <image class="goods-image" src="{{item.icon}}"></image>
  11. <image class="goods-price" src="{{item.price}}"></image>
  12. </view>
  13. </block>
  14. </view>
  15. <!-- 底部图片 -->
  16. <image class="welfare-bottom-image" src="{{yx.welfare_bottom_url}}"></image>
  17. </view>
  18. </view>

先把商品部分的标签注释掉,样式调整顺序如下

  • 最外层设置flex布局,主轴从上往下,item居中,背景白色,上下内间距24rpx;
  • 外层设置flex布局,主轴从上往下,item居中,宽690rpx,高434rpx,圆角12rpx,背景颜色#d4545a;
  • 顶部图片100%,高度110rpx,左上和右上圆角12rpx;
  • 商品部分最外层设置flex布局,主轴从上往下,item居中;
  • 商品部分外层宽度660rpx,高度212rpx,圆角12rpx,上下内间距12rpx,左右内间距20rpx;
  • 底部图片宽660rpx,高96rpx,圆角12rpx;

对应样式文件如下

  1. .welfare {
  2. display: flex;
  3. flex-direction: column;
  4. align-items: center;
  5. background: #fff;
  6. padding: 24rpx 0;
  7. }
  8. .welfare-container {
  9. display: flex;
  10. flex-direction:column;
  11. align-items:center;
  12. width: 690rpx;
  13. height: 434rpx;
  14. border-radius: 12rpx;
  15. background: #d4545a;
  16. }
  17. .welfare-top-image {
  18. width: 100%;
  19. height: 110rpx;
  20. border-radius: 12rpx 12rpx 0 0;
  21. }
  22. .welfare-goods {
  23. display: flex;
  24. flex-direction: column;
  25. align-items: center;
  26. }
  27. .welfare-goods-container {
  28. width: 660rpx;
  29. height: 212rpx;
  30. box-sizing: border-box;
  31. border-radius: 12rpx;
  32. background: #fff;
  33. padding: 12rpx 20rpx;
  34. }
  35. .welfare-bottom-image{
  36. width: 660rpx;
  37. height: 96rpx;
  38. border-radius: 12rpx;
  39. }

运行结果如下

接着去掉商品部分的标签注释,调整样式

  • 商品外层设置flex布局,主轴从上往下,item居中,宽度134rpx,高度100%;
  • 商品图片宽高130rpx;
  • 商品价格宽100%,高56rpx;
  1. .goods-wrapper {
  2. display: flex;
  3. flex-direction: column;
  4. align-items: center;
  5. width: 134rpx;
  6. height: 100%;
  7. }
  8. .goods-image {
  9. width: 130rpx;
  10. height: 130rpx;
  11. }
  12. .goods-price {
  13. width: 100%;
  14. height: 56rpx;
  15. }

em...商品直接穿出来了,修改下商品外层布局flex布局,从左网友,两端占满:

  1. .welfare-goods-container {
  2. width: 660rpx;
  3. height: 212rpx;
  4. box-sizing: border-box;
  5. border-radius: 12rpx;
  6. background: #fff;
  7. padding: 12rpx 20rpx;
  8. display: flex;
  9. flex-direction: row;
  10. justify-content: space-between;
  11. }

运行结果如下


⑥ 信息标签

页面结构如下

  1. <!-- 信息标签 -->
  2. <view class="info-tag-container">
  3. <block wx:for="{{yx.tag_info_list}}">
  4. <view class="tag-wrapper">
  5. <image class="tag-image" src="{{item.icon}}"></image>
  6. <view class="tag-text">{{item.text}}</view>
  7. </view>
  8. </block>
  9. </view>

样式调整顺序如下

  • 最外层高度48rpx,flex布局,两端对齐留白;
  • 外层flex布局,内容居中;
  • 标签图片宽高24rpx,右侧偏离8rpx;
  • 标签字体颜色#ff4891,大小22rpx;

对应样式文件如下

  1. .info-tag-container {
  2. height: 48rpx;
  3. display: flex;
  4. justify-content: space-around;
  5. }
  6. .tag-wrapper {
  7. align-items: center;
  8. display: flex;
  9. }
  10. .tag-image {
  11. width: 24rpx;
  12. height: 24rpx;
  13. margin-right: 8rpx;
  14. }
  15. .tag-text {
  16. font-size: 22rpx;
  17. color: #ff4891;
  18. }

运行结果如下


⑥ 商品分类

这里的商品分类不止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。看了下每日优鲜的玩法,是把这里拆分成了两个部分,这里照葫芦画瓢实现一波。

页面结构如下

  1. <!-- 商品类别 -->
  2. <view class="good_category_container">
  3. <scroll-view scroll-x class="scroll-view">
  4. <view class="category_first">
  5. <block wx:for="{{yx.category_list_first}}" wx:key="key">
  6. <view class="good_category">
  7. <image class="category-image" src="{{item.icon}}"></image>
  8. <view class="category-text">{{item.text}}</view>
  9. </view>
  10. </block>
  11. </view>
  12. <view class="category_second">
  13. <block wx:for="{{yx.category_list_second}}" wx:key="key">
  14. <view class="good_category">
  15. <image class="category-image" src="{{item.icon}}"></image>
  16. <view class="category-text">{{item.text}}</view>
  17. </view>
  18. </block>
  19. </view>
  20. </scroll-view>
  21. </view>

样式调整顺序如下

  • 最外层宽度100%,设置white-space: nowrap;
  • scroll-view设置最大高度360rpx;
  • 左侧部分,宽度100%,inline-flex,自动换行;
  • 右侧部分,高度384rpx,inline-flex,主轴从上往下;
  • 分类外层,flex布局,水平竖直居中,主轴从上往下,宽150rpx,顶部偏移24rpx;
  • 分类图标,宽高104rpx;
  • 分类文本,高34rpx,行高34rpx让文本垂直居中,字体颜色#474245,大小24rpx,顶部偏移10rpx;

对应样式文件如下

  1. .good_category_container {
  2. width: 100%;
  3. white-space: nowrap;
  4. }
  5. .scroll-view {
  6. max-height: 360rpx;
  7. }
  8. .category_first {
  9. width: 100%;
  10. display: inline-flex;
  11. flex-wrap: wrap;
  12. }
  13. .category_second {
  14. height: 384rpx;
  15. display: inline-flex;
  16. flex-direction: column;
  17. flex-wrap: wrap;
  18. }
  19. .good_category {
  20. display: flex;
  21. flex-direction: column;
  22. justify-content: center;
  23. align-items: center;
  24. width: 150rpx;
  25. margin-top: 24rpx;
  26. }
  27. .category-image {
  28. width: 104rpx;
  29. height: 104rpx;
  30. }
  31. .category-text {
  32. font-size: 24rpx;
  33. color: #474245;
  34. height: 34rpx;
  35. line-height: 34rpx;
  36. margin-top: 10rpx;
  37. }

运行结果如下


⑦ 悬浮提醒

这种悬浮在页面上,不会因为页面滚动而滚动的部分,可以使用固定定位来实现。

页面结构如下

  1. <!-- 浮动提醒 -->
  2. <view class="tip-container">
  3. <image class="tip-image" src="{{yx.tip_image_url}}"></image>
  4. </view>

样式调整顺序如下

  • 外层容器设置绝对定位,宽98rpx,高130rpx,离右边28rpx,离底部40rpx;
  • 图片设置宽高100%;

对应样式文件如下

  1. .tip-container {
  2. position: fixed;
  3. right: 28rpx;
  4. bottom: 40rpx;
  5. width: 98rpx;
  6. height: 130rpx;
  7. }
  8. .tip-image{
  9. width: 100%;
  10. height: 100%;
  11. }

运行结果如下


⑧ 最终效果展示

Tips:真机预览时发现分类那里滑动时有滚动条,可以在wxss加入下述样式隐藏滚动条~

  1. ::-webkit-scrollbar {
  2. width: 0;
  3. height: 0;
  4. color: transparent;
  5. }

小结

相信读者学完本节,基本可以应付日常小程序页面的堆砌了。这种实操性比较强的东西,切忌死记,建议自己找些小程序仿写下,熟能生巧,别说没有设计稿,没有图片没有尺寸,上节学的反编译技能呢???

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


参考文献

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注