# 微信小程序常见开发技巧
最近从零开发一个微信小程序项目,将一些常见的技巧与坑记录在此,文章以问题为导向,提供思路与源代码,希望能帮助到大家。
# Q1: 如何实现一个 rate
评分?
# 版本一
首先来一个简单版本,效果如下图所示:
首先需要准备两张图片,一张是高亮的星星图片,一张是暗色的星星图片。
主要的思路是建立一个数组
[1,2,3,4,5]
,并在 data 中保存一个starNum
,用于表示有几个星星。然后通过遍历数组,比 startNum 小时则渲染高亮的星星,否则渲染暗色的星星。
通过绑定
tap
事件,修改starNum
具体代码见下。
wxml
如下所示,下文中的 for 内容为[1,2,3,4,5],不知道为啥无法显示:
<view class="star">
录取指数:
<block wx:for="{{[1,2,3,4,5]}}" wx:key="*this">
<image
src="{{starNum>=item?'/images/Star.png':'/images/Star (gray).png'}}"
data-num="{{item}}"
bindtap="handleChange"
style="width:40rpx;height:40rpx"
/>
</block>
</view>
2
3
4
5
6
7
8
9
10
11
JS
如下图所示:
handleChange(e) {
const { num } = e.currentTarget.dataset
this.setData({
starNum: num
})
}
2
3
4
5
6
7
如果说需要半个星星的,那么可以通过如下方法:
<view style="display: flex;">
录取指数:
<block wx:for="{{[1,2,3,4,5]}}" wx:key="*this">
<image
src="{{starNum>index?(starNum>index+0.5?fullStarUrl:halfStarUrl):nullStarUrl}}"
style="width:40rpx;height:40rpx"
/>
</block>
</view>
2
3
4
5
6
7
8
9
data
中数据如下:
Page({
data: {
// 满星图片
fullStarUrl: "/images/full@2.png",
// 半星图片
halfStarUrl: "/images/half-star.png",
// 空星图片
nullStarUrl: "/images/null@2.png",
},
});
2
3
4
5
6
7
8
9
10
看到了吧,几行代码就可以搞定,这个版本可以和后端进行交互,比如发送Post
请求,修改星星数。
# 版本二
接着我们用一个纯 CSS 的方法实现一个相对来说比较复杂的(以下版本作者@子瑜说 IT)。
去找个好看的 iconfont,[Iconfont-阿里巴巴矢量图标库];
借用 5 个 radio 单选框,把默认样式都去掉,显示默认的星星;
用 checked 伪类监听用户选中 ✅,由默认的星星变成高亮的星星;
然后配合~兄弟操作符把当前选中的所有兄弟元素都一起高亮;
把 5 个 radio 单选框反向排列 ❗;
<link rel="stylesheet" href="//at.alicdn.com/t/font_1356455_c5d3d3ohlbq.css" />
一个很简洁的布局:
<div class="rate-content">
<input type="radio" name="rate">
<input type="radio" name="rate">
<input type="radio" name="rate">
<input type="radio" name="rate">
<input type="radio" name="rate">
</div>
2
3
4
5
6
7
# 1、先把默认的星星显示出来
/* 去掉默认样式 */
input {
-webkit-appearance: none;
border: none;
outline: none;
cursor: pointer;
}
:root {
/*高亮颜色*/
--main: #ffa822;
/*默认颜色*/
--basic: #999;
}
.rate-content input[name="rate"] {
font-family: "iconfont";
/*之前引入的iconfont字体*/
font-size: 30px;
padding-right: 10px;
}
.rate-content input[name="rate"]::after {
content: "\e645";
color: var(--basic);
/*加点颜色过渡效果*/
transition: color 0.4s ease;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
效果如下:
# 2、实现选中单个星星
/* 实现选中单个星星 */
/* 高亮的星星 */
input[name="rate"]:checked::after {
content: "\e73c";
color: var(--main);
}
2
3
4
5
6
效果如下:
# 3、实现连同兄弟元素一起高亮
/* 实现选中单个星星 */
/* 高亮的星星 */
input[name="rate"]:checked::after,
input[name="rate"]:checked ~ input[name="rate"]::after {
/*实现连同兄弟元素一起高亮*/
content: "\e73c";
color: var(--main);
}
2
3
4
5
6
7
8
效果如下:
# 4、然后把 input 反向排列
.rate-content {
display: flex;
flex-flow: row-reverse;
}
2
3
4
效果如下:
# 5、鼠标移入预览选中效果
input[name="rate"]:checked,
input[name="rate"]:hover::after {
content: "\e73c";
color: var(--main);
}
/* 兄弟元素一起高亮 */
input[name="rate"]:hover ~ input[name="rate"]::after {
content: "\e73c";
color: var(--main);
}
2
3
4
5
6
7
8
9
10
11
效果如下:
# 6、加入放大动画
input[name="rate"] {
transition: transform 0.2s ease;
}
input[name="rate"]:checked,
input[name="rate"]:hover {
transform: scale(1.2);
}
2
3
4
5
6
7
效果如下:
# Q2: 如何实现联想搜索并将联想结果的关键字高亮显示?
想要实现的效果是在input
输入框中输入查询关键字的同时,向后端发起请求,获取联想结果,并展示。同时在展示的时候,将关键字高亮显示。
# 联想搜索
<view class="seachBar">
<input
value="{{inputValue}}"
focus="true"
confirm-type="search"
placeholder="搜索感兴趣的城市、大学、专业"
bindinput="onInput"
/>
</view>
<scroll-view scroll-y="true" class="search-res" hidden="{{hideScroll}}">
<block wx:for="{{searchTip}}" wx:key="id">
<view class="tip-item" bindtap="itemtap" data-info="{{item}}">
{{item.name}}
</view>
</block>
</scroll-view>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
如上代码所示首先在input
输入框中绑定onInput
,onInput
函数的实现如下所示。通过设置定时器,在规定时间内如果不再继续输入,则向后端发起请求,如果有数据,则通过scroll-view
展示请求回来的联系结果,如果没有则隐藏scroll-view
。
// pages/search/search.js
Page({
data: {
// 模糊查询时长
timer: 0,
// 点击结果项之后替换到文本框的值
inputValue: "",
// 是否隐藏模糊查询的面板
hideScroll: true,
// 模糊查询结果
searchTip: [],
},
onInput(e) {
const inputValue = e.detail.value;
clearTimeout(this.data.timer);
// 设置一个定时器,在600秒内不输入新的内容则发起联想搜索,否则清空定时器
let timer = setTimeout(() => {
if (inputValue) {
// 如果输入的关键字不为空,则发起请求获取联想值,这里以tips作为请求获取结果
const tips = [
{ search_type: "city", name: "北京", id: 11 },
{ search_type: "school", name: "北京大学", id: 22 },
{ search_type: "major", name: "北京测绘工程", id: 33 },
];
this.setData({
inputValue: inputValue,
searchTip: tips,
hideScroll: false,
});
return;
}
// 如果为空,则收起
this.setData({
searchTip: [],
hideScroll: true,
inputValue: "",
});
}, 600);
this.data.timer = timer;
},
itemtap(e) {
const { info } = e.currentTarget.dataset;
this.setData({
// 将点击选择的值展示在input框中
inputValue: info.name,
// 当用户选择某个联想词,隐藏下拉列表
hideScroll: true,
});
// 发起请求,获取查询结果
this.searchByKeyWord(info);
},
searchByKeyWord(info) {
// 发起请求,获取面板数据
},
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 关键字高亮显示
联系搜索相对来说比较简单,主要是通过计时器做到防抖。接下来需要介绍一下关键字高亮显示。实现效果如下图所示。
像React
和Vue
可以通过正则表达式把匹配到的关键字用标签如<b class="你的高亮样式">关键字</b>
替换掉原来的关键字,再将内容通过 dom 操作塞回原来的容器,但是小程序,我并没有发现可以这样实现的方法,如果有实现的可以私聊教教我哦。但是我换了一个思路,我用正则表达式将原来的关键字替换成%%${key}%%
,再根据%%
将整个字符串拆成数组。然后通过遍历的这个数组,如果值与输入的inputValue
相同则切换高亮样式,否则原来的样式。代码如下所示:
<view>
<view class="seachBar">
<input
value="{{inputValue}}"
focus="true"
confirm-type="search"
placeholder="搜索感兴趣的城市、大学、专业"
bindinput="onInput"
/>
</view>
<scroll-view scroll-y="true" class="search-res" hidden="{{hideScroll}}">
<block wx:for="{{searchTip}}" wx:key="id">
<view class="tip-item" bindtap="itemtap" data-info="{{item}}">
<view class="left">
<view class="content">
<!-- 关键字高亮关键 -->
<view
wx:for="{{item.name}}"
wx:for-item="textItem"
wx:key="index"
class="{{textItem == inputValue ? 'searchHigh' : '' }}"
>
{{textItem}}
</view>
</view>
</view>
</view>
</block>
</scroll-view>
</view>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Page({
data: {
// 模糊查询定时器id
timer: 0,
// 点击结果项之后替换到文本框的值
inputValue: "",
// 是否隐藏模糊查询的面板
hideScroll: true,
// 模糊查询结果
searchTip: [],
},
// 将关键字左、关键字、右拆成数组
getInf(str, key) {
return str
.replace(new RegExp(`${key}`, "g"), `%%${key}%%`)
.split("%%")
.filter((item) => {
if (item) {
return true;
}
return false;
});
},
onInput(e) {
const inputValue = e.detail.value;
clearTimeout(this.data.timer);
let timer = setTimeout(() => {
if (inputValue) {
// 如果输入的关键字不为空,则发起请求获取联想值,以tips假设为请求结果
const tips = [
{ search_type: "city", name: "北京", id: 11 },
{ search_type: "school", name: "北京大学", id: 22 },
{ search_type: "major", name: "北京测绘工程", id: 33 },
];
const newTips = tips.map((item) => {
const { search_type, name, id } = item;
const newContent = this.getInf(name, inputValue);
return { search_type, name: newContent, id };
});
this.setData({
inputValue: inputValue,
searchTip: newTips,
hideScroll: false,
});
return;
}
// 如果为空,则收起
this.setData({
searchTip: [],
hideScroll: true,
inputValue: "",
});
}, 600);
this.data.timer = timer;
},
itemtap(e) {
const { info } = e.currentTarget.dataset;
this.setData({
// 将点击选择的值展示在input框中
inputValue: info.name.join(""),
// 当用户选择某个联想词,隐藏下拉列表
hideScroll: true,
});
// 发起请求,获取查询结果
this.searchByKeyWord(info);
},
searchByKeyWord(info) {
// 发起请求,获取面板数据
},
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
❤️ 爱心三连击
1.看到这里了就点个在看支持下吧,你的「在看」是我创作的动力。
2.关注公众号前端梦想家,「一起学前端」!
3.添加微信【qdw1370336125】,拉你进技术交流群一起学习。