在落地页经常需要实现一个可以点击的按钮列表,为用户提供直观的可选产品。
组件使用
- 创建组件,在根目录下创建 components 路径。
- 在 components 下创建组件文件夹,右键文件夹创建组件,文件夹名称和组件名称尽量一致。
- 在需要引用组件的 wxml 中,以标签形式使用组件。
- 在需要引用组件的 json 文件 usingComponents 数组下,添加组件相对路径。
这里新建一个 <Fruits></Fruits> 水果按钮列表组件。
分析界面结构:
- 纵向三层,整体居中。
- 第一层是标题 “请选择水果”,外加两个小手动画。内部是水平布局。
- 第二层是 3 x 3 按钮列表,里面有文字,有点击事件,有按钮样式,整体居中布局。
- 第三层是一个居中布局的文字。
结构很简单,开始动手写 wxml
<view class="title-wrap">
<image class="finger" src="{{fingerImg}}"/>
<text>请选择要购买的水果</text>
<image class="finger" src="{{fingerImg}}"/>
</view>
<view class="fruits-list">
<view class="fruit" wx:for="{{fruitsList}}" wx:key="index" data-index="{{index}}" catchtap="tapFruit">
<text>{{item}}</text>
<button class="fruit-btn" bindtap="getUserProfile" data-index="{{index}}"></button>
</view>
</view>
<view class="tips">{{'*购买成功记得五星好评哦'}}</view>
样式:
.title-wrap {
margin: 40rpx auto;
padding: 0;
display: flex;
flex-direction: row;
justify-content: space-between;
flex-grow: 1;
flex-shrink: 1;
font-size: 34rpx;
color: #000;
font-family: PingFangSC-Semibold,PingFang SC;
}
.finger {
width: 44rpx;
height: 50rpx;
}
.fruits-list {
position: relative;
margin: 0 50rpx;
display: flex;
flex-flow: row wrap;
}
.fruit {
position: relative;
margin: 0 8rpx 24rpx;
width: 200rpx;
background: #fff;
border: 2rpx solid rgba(255, 107, 44, 1);
border-radius: 10rpx;
font-size: 28rpx;
line-height: 72rpx;
font-weight: 600;
color: rgba(255, 98, 3, 1);
text-align: center;
}
.fruit:nth-child(3n+1) {
margin-left: 0;
}
.fruit:nth-child(3n) {
margin-right: 0;
}
.fruit-btn {
background: transparent;
width: 100% !important;
height: 100%;
z-index: 1;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.tips {
margin: 12rpx 30rpx 38rpx;
font-size: 24rpx;
font-weight: 300;
color: #999999;
line-height: 24rpx;
letter-spacing: 1rpx;
text-align: center;
}
Tips:
- 小手指是上下移动的,可以使用CSS关键帧动画实现
- 按钮列表中,需要绑定按钮序号,使用 data-index 绑定循环中的序号。
- 节点自适应左右居中常用的方法就是设置 margin: 0 auto 。
这里面用到了 flex-grow 和 flex-shrink。着重简单介绍一下,因为这两个属性经常会用到.
flex-grow
flex-grow 处理父元素在还有剩余空间时的分配规则,分为两种情况。
即:所有元素的 flex-grow 值之和大于1,和小于1。
-
大于1时,例如:
父元素宽600,子元素A和B,宽分别为200,300。还剩余100。
此时A,B的 flex-grow 分别为2,3。则剩余100,分给A 2/5,分给B 3/5。
A,B宽度为:200 + 40 = 240 300 + 60 = 360
-
小于1时,作为分母的总和会引入1来处理。例如:
上例中,A,B flex-grow 分别为 0.2,0.3。则分给A 0.2/1,分给B 0.3/1。
A,B宽度为:200 + 20 = 220 300 + 30 = 330
还剩50没有分配给任何子元素扩张。
-
另外,flex-grow 还会受到父元素的 max-width 影响。如果grow后的结果超出 max-width,max-width 会优先生效。
flex-shrink
和 flex-grow 处理父元素剩余空间相对应的,是 flex-shrink 处理父元素空间不足时,子元素的收缩规则。
同样分为两种情况,所有元素的 flex-shrink 值之和大于1,和小于1。
-
大于1时,例如:
父元素宽度为600,子元素宽度为400,300。超出100。
A,B flex-shrink 分别为 1,2。总权重为400 + 300 * 2 = 1000
A收缩 -100 * 1 * 400 / 1000 = -40 B收缩 -100 * 2 * 300 / 1000 = -60
A,B实际宽度为:
400 - 40 = 360 300 - 60 = 240
-
小于1时,例如,
A,B flex-shrink 分别为 0.1,0.2。总权重为400 * 0.1 + 300 * 0.2 = 100
子元素收缩总和为100 * 0.3 / 1 = 30
A收缩 -30 * 0.1 * 400 / 100 = -12 B收缩 -30 * 0.2 * 300 / 100 = -18
A,B实际宽度为:
400 - 12 = 388 300 - 18 = 282
多出70没有分配给任何子元素收缩。
-
同样,也会受到min-width的限制。
组件传递数据
父组件向子组件传递数据
在组件的属性列表中新增参数字段:
properties: {
option: {
type: Boolean,
value: true
}
}
这个属性需要在使用组件的位置赋值,并作为参数传递下去:
<Fruits option="{{true}}"></Fruits>
子组件向父组件传递数据
在子组件内 trigger 一个事件,然后在子组件被引用的位置 bind 事件。并且在事件响应函数中,使用传递过来的数据。
-
Trigger:
this.triggerEvent('eventName', { index });
可以在后面夹带参数。 -
Bind:
<Fruits option="{{true}}" bindeventName="callBack"></Fruits>
-
CallBack:
callBack: function (e) { // 事件传递过来的参数 const index = e.detail.index; }
CSS关键帧动画
为了实现手指向下的小动画,使用关键帧处理。
如果在 @keyframes 规则中指定了 CSS 样式,动画将在设定时间逐渐从当前样式更改为新样式。
.finger:first-child {
margin-right: 10rpx;
animation: moveDownLeft .9s infinite;
}
.finger:last-child {
margin-left: 10rpx;
animation: moveDownRight .9s infinite;
}
- 左右手各使用一个动画,因为如果使用同一个动画,再Y轴翻转一下也可以。但是会出现左右动画不同时运动的问题。
- 0.9s 是持续时间,infinite 是无限循环。
keyframes :
@keyframes moveDownLeft {
0% {
transform: translateY(0rpx);
}
50% {
transform: translateY(9rpx);
}
100% {
transform: translateY(0rpx);
}
}
@keyframes moveDownRight {
0% {
transform: translateY(0rpx) scale(-1, 1);
}
50% {
transform: translateY(9rpx) scale(-1, 1);
}
100% {
transform: translateY(0rpx) scale(-1, 1);
}
}
左手动作设置了从开始到一半,再到结束时的Y轴位移。右手Y轴动作一致,只不过水平翻转一下。