- 如何实现一个简单的http请求的封装
好久没发文章了,最近浏览社区看到比较多的请求封装,以及还有在使用原始请求的童鞋。为了减少代码,提升观赏性,我也水一篇吧,希望对大家有所帮助。 默认请求方式,大家每次都这样一些写相同的代码,会不会觉得烦,反正我是觉得头大 😂 [代码]wx.request({ url: 'test.php', //仅为示例,并非真实的接口地址 data: { x: '', y: '' }, header: { 'content-type': 'application/json' // 默认值 }, success (res) { console.log(res.data) } }) [代码] 来,进入正题吧,把这块代码封装下。 首先新建个request文件夹,内含request.js 代码如下: [代码]/** * 网络请求封装 */ import config from '../config/config.js' import util from '../util/util.js' // 获取接口地址 const _getPath = path => (config.DOMAIN + path) // 封装接口公共参数 const _getParams = (data = {}) => { const timestamp = Date.now() //时间戳 const deviceId = Math.random() //随机数 const version = data.version || config.version //当前版本号,自定或者取小程序的都行 const appKey = data.appKey || config.appKey //某个小程序或者客户端的字段区分 //加密下,防止其他人随意刷接口,加密目前采用的md5,后端进行校验,这段里面的参数你们自定,别让其他人知道就行,我这里就是举个例子 const sign = data.sign || util.md5(config.appKey + timestamp + deviceId) return Object.assign({}, { timestamp, sign, deviceId, version, appKey }, data) } // 修改接口默认content-type请求头 const _getHeader = (headers = {}) => { return Object.assign({ 'content-type': `application/x-www-form-urlencoded` }, headers) } // 存储登录态失效的跳转 const _handleCode = (res) => { const {statusCode} = res const {msg, code} = res.data // code为 4004 时一般表示storage里存储的token失效或者未登录 if (statusCode === 200 && (code === 4004)) { wx.navigateTo({ url: '/pages/login/login' }) } return true } /** * get 请求, post 请求 * @param {String} path 请求url,必须 * @param {Object} params 请求参数,可选 * @param {String} method 请求方式 默认为 POST * @param {Object} option 可选配置,如设置请求头 { headers:{} } * * option = { * headers: {} // 请求头 * } * */ export const postAjax = (path, params) => { const url = _getPath(path) const data = _getParams(params) //如果某个参数值为undefined,则删掉该字段,不传给后端 for (let e in data) { if (data[e] === 'undefined') { delete data[e] } } // 处理请求头,加上最近比较流行的jwtToken(具体的自己百度去) const header = util.extend( true, { "content-type": "application/x-www-form-urlencoded", 'Authorization': wx.getStorageSync('jwtToken') ? `Bearer ${wx.getStorageSync('jwtToken')}` : '', }, header ); const method = 'POST' return new Promise((resolve, reject) => { wx.request({ url, method, data, header, success: (res) => { const result = _handleCode(res) result && resolve(res.data) }, fail: function (res) { reject(res.data) } }); }) } [代码] 那么如何调用呢? [代码]//把request的 postAjax注册到getApp()下,调用时: const app = getApp() let postData = { //这里填写请求参数,基础参数里的appKey等参数可在这里覆盖传入。 } app.postAjax(url, postData).then((res) => { if (res.success) { //这里处理请求成功逻辑。 } else { //wx.showToast大家觉得麻烦也可以写到util.js里,调用时:util.toast(msg) 即可。 wx.showToast({ title: res.msg || '服务器错误,请稍后重试', icon: "none" }) } }).catch(err => { //这里根据自己场景看是否封装到request.js里 console.log(err) }) [代码] config.js 主要是处理正式环境、预发环境、测试环境、开发环境的配置 [代码]//发版须修改version, env const env = { dev: { DOMAIN: 'https://dev-api.weixin.com' }, test: { DOMAIN: 'https://test-api.weixin.com', }, pro: { DOMAIN: 'https://api.qtshe.com' } } module.exports = { ...env.pro } [代码] 以上就是简单的一个request的封装,包含登录态失效统一跳转、包含公共参数的统一封装。 老规矩,最后放代码片段,util里内置了md5方法以及深拷贝方法,具体的我也不啰嗦,大家自行查看即可~ https://developers.weixin.qq.com/s/gbPSLOmd7Aft
2020-04-03 - 如何实现一个自定义数据版省市区二级、三级联动
社区可能有其他的方案了,但是再分享下吧,给有需要的童鞋。 效果图: [图片] 额,这个视频转GIF因为社区上传不了大图,所以剪了一部分,具体的效果还是直接工具打开代码片段预览吧~ 第一步:你的页面JSON引入该组件: [代码]{ "usingComponents": { "city-picker": "/components/cityPicker/index" } } [代码] 第二步:你的页面WXML引入该组件 [代码]<city-picker visible="{{visible}}" column="2" bind:close="handleClick" bind:confirm="handleConfirm" /> [代码] 第三步:你的页面JS调用 [代码]// 显示/隐藏picker选择器 handleClick() { this.setData( visible: !this.data.visible }) }, // 用户选择城市后 点击确定的返回值 handleConfirm(e) { const { detail: { provinceName = '', provinceId = '', cityName, cityId='', areaName = '', areaId = '' } = {} } = e this.setData({ cityId, cityName, areaId, areaName, provinceId, provinceName }) } [代码] 组件属性 属性 默认值 描述 visible false 是否显示picker选择器 column 3 显示几列,可选值:1,2,3 values [0, 0, 0] 必填,默认回填的省市区下标,可选择具体省市区后查看AppData的regionValue字段 close function 点击关闭picker弹窗 confirm function 点击选择器的确定返回值 confirm: 属性 默认值 描述 provinceName 北京市 省份名称 provinceId 110000 省份ID cityName 市辖区 城市名称 cityId 110100 城市ID areaName 东城区 区域名称 areaId 110000 区域Id 至于怎么获取你想默认城市的下标,可以滑动操作下选中省市区后,点击确定后查看appData里的regionValue的值。 以上就是一个自定义数据版本的省市区二级、三级联动啦,老规矩,结尾放代码片段。 https://developers.weixin.qq.com/s/F9k9cTmT7LAz
2022-07-20 - 炫酷的wxss动画效果
因为没啥事,研究了下小程序的粒子动画,最后放弃了,实在是头大。去搞了一些花里胡哨的效果,没啥实际用处,就分享玩玩,看能不能提供一些其他灵感啥的。 [图片] 代码片段如下:https://developers.weixin.qq.com/s/VQwYjYm47dgH 使用wxss绘制烟花动画 [图片] https://developers.weixin.qq.com/s/xcJdoMmW7lh3 蜡烛逼真燃烧效果: [图片] https://developers.weixin.qq.com/s/Iom47XmO7rh5 螺旋旋转效果 [图片] https://developers.weixin.qq.com/s/1BnRTXmZ7Rhj 炫酷wxss粒子动画 [图片] https://developers.weixin.qq.com/s/cRpjQXmb7khN 水文章
2020-08-03 - 【交互方案】针对不小心触发返回按钮的交互
17年 因为业务上有该类需求,所以提过一个问题。 想实现监听左上角按钮来做其他操作,比如跳转其他页面。 https://developers.weixin.qq.com/community/develop/doc/92f7cdbacdf724cba640955423a8444f 此交互优化只应对表单等内容提交回填的处理 应用场景: 某个表单提交页,页面挺多内容,用户填写完后,不小心触发了左上角返回,或者不小心点了物理按键,那么用户填写的内容就丢失了,那么最开始大家的想法都是,点击左上角返回按钮或者物理按键返回能触发监听我们再弹窗提醒用户是否退出当前页面。这个交互应该是再正常不过的了,在各种app都有见过这种交互。 网页里: h5里都是通过监听popstate,以及设置pushstate实现。 小程序里: 然而小程序的翻遍官方文档,没找到该方法,发帖询问后也是得到官方童鞋回复,不会提供该方法。 官方童鞋给的原因是:会有某些开发者,阻止用户退出某些页面,以达到一些xxx目的,所以官方为了防止开发者滥用,并不打算开放该功能。 翻了社区大家实现方式,有大佬给了一个方案: https://developers.weixin.qq.com/community/develop/article/doc/000844b537c230b04b999a54f56013 该监听方法的缺点: [图片] 最后确实没发现有什么好的监听方案了,那既然代码无法实现,那么我们可以优化用户体验来达到该效果。 实现操作方案如下: [代码]// app.js下跟onLaunch同级新增个globalData字段。 globalData: { formData: {} // 这里需要默认填写该字段,不然其他地方使用了会报错。 } // 首先用户填写任意字段都存储一个对象到globalData下。 <input type="text" placeholder="请输入用户昵称" bindinput="handleUserName" /> handleUserName(e) { getApp().globalData.formData.userName = e.detail.value } [代码] 这样将用户填写的内容都存到globalData下,而我们最初的交互是,存储后用户点击返回下次进来自动回填。 [代码]onShow() { this.setData({ userName: getApp().globalData.formData.userName || '' }) } [代码] 而最终的交互是这样操作: 如果用户填写完一整页内容,而内容我们都存到了globalData下,用户不小心返回了上一页,那么我们在用户重新进入该页面时,判断globalData的formData下是否存在内容,如果存在,弹窗提醒用户是否回填上次填写的内容,如果用户确认回填那么我们给用户自动回填上次填写的信息,如果用户取消回填,那么我们将globalData下的formData设置为空对象即可。 如此 我们即从交互上规避了不小心点击返回导致需要重新输入的问题,并且交互体验得到极大提升。。
2020-06-04 - 如何在微信内外部浏览器唤起小程序
微信外部浏览器唤起微信小程序目的:通过发送短信召回流失用户。官方文档地址https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-scheme/urlscheme.generate.html步骤一该API我们主要用到的配置如下:jump_wxa:跳转到的目标小程序信息。该对象内包含两个字段。path:通过scheme码进入的小程序页面路径,必须是已经发布的小程序存在的页面,不可携带queryquery:通过scheme码进入小程序时的query。步骤二需要注意的是,query必传,然而如果我们需要跳转的页面不需要参数,也需要配置上query,默认我们填写的是from=sms, 用于统计是从短信渠道来的。步骤三与后端约定access_token 通过前端传递appKey进行区分,传哪个微信小程序的appKey 就生成对应小程序的access_token。步骤四支持设置scheme的过期时间,默认永久,我们的应用场景很少,暂不详说。步骤五代码里操作如下,由后端聚合参数信息。let postData = { appKey: 'QTSHE_MINI_APP', path: 'pages/partdetails/partdetails', query: 'partJobId=123456' || 'a=1' } this.$axios.post('xxx', postData).then((res) => { if (res.success) { const url = res.data.openlink // 将scheme转为我们平台的短链接,否则在安卓手机上无法打开微信小程序,会默认打开浏览器搜索。 this.$axios.get(`xxx`, {url}).then((res) => { if (res.success) { this.shortLink = res.data // 将weixin://xxxx 转换为 https://b.qtshe.com/xxx } }) } else { this.$Message.error('获取失败,请稍后重试') } }).catch((err) => { console.log(err) this.$Message.error('获取失败,请稍后重试') }) 微信内部浏览器唤起小程序官方文档地址https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_Open_Tag.html步骤一首先需要登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”,未配置会导致wx.config签名失败,导致无法使用开放标签。步骤二在需要调用JS接口的页面引入如下JS文件:https://res.wx.qq.com/open/js/jweixin-1.6.0.js,1.6.0版本内才增加了上述标签,低于该版本的都无法显示。步骤三通过config接口注入权限验证配置并申请所需开放标签, 在wx.config里增加openTagList标签,内置两个开放标签 [代码]wx-open-launch-app[代码] 微信h5唤起本地已经安装的app,以及 [代码]wx-open-launch-weapp[代码] 微信h5唤起小程序,操作如下:wx.config({ debug: false, appId: window.g_info.wx_appid, // 全局变量appId timestamp: data.data.timestamp, // 调用微信接口返回的 nonceStr: data.data.nonceStr, // 调用微信接口返回的 signature: data.data.signature, // 调用微信接口返回的 jsApiList: ['openLocation', 'getLocation'], // 这里需要任意填写api,不能为空 openTagList: [ 'wx-open-launch-app', 'wx-open-launch-weapp' ] }) 注意点对于Vue等视图框架,为了避免template标签冲突的问题,可使用[代码]<script type="text/wxtag-template"></script>[代码]进行代替,来包裹插槽模版和样式。页面中与布局和定位相关的样式,如[代码]position: fixed; top -100;[代码]等,尽量不要写在插槽模版的节点中,请声明在标签或其父节点上;对于有CSP要求的页面,需要添加白名单frame-src https://*.qq.com webcompt:,才能在页面中正常使用开放标签。// Vue里操作代码如下,如果需要写样式,最好是外部包一个div进行样式编写,内部的内容宽高100%即可,调试样式可使用微信开发者工具的网页模式: .quick-btn { // 这里写样式 width: 3.43rem; height: 0.96rem; display: flex; img { width: 100%; display: block; } } <div class="quick-btn"> <img src="https://qiniu-image.qtshe.com/20220317quick-apply.png" alt="" /> </div> 示例效果[图片] 为了方便运营同学,做了个工具给他们使用。 [图片] 2023年4.11号新版本后的改版方案: 首先做一个落地页:https://b.qtshe.com/1DF43E 代码如下: import toast from 'toast' export default { created() { this.init() }, methods: { // 获取微信scheme地址,并且主动跳转一次 init() { this.$axios.post(`https://xxx/你们的接口地址/user/device/scheme`, this.$route.query).then(res => { if (res.success) { window.location.href = res.data } else { toast(res.msg) } }).catch(() => { toast('服务器错误,请稍后重试') }) } } } 原理:通过地址栏配置需要跳转的参数,h5页面拿到参数后通过接口转换为weixin://xxxx 开头的scheme协议地址,并且主动location.href一次。这样能保证用户每次打开都是新的scheme地址,针对一天50w数量上限的问题,可尝试同一个用户在固定时间内相同的参数,返回相同的scheme地址。 以上限制只存在于除微信外的外部浏览器,微信容器里没有以上的限制。 2023年12月18日后方案: 官方公告如下: https://developers.weixin.qq.com/community/develop/doc/00024e32cbc36055c0c0a34b066401 ,没有外部浏览器、微信环境的限制,可以被多人打开。 使用方式通过明文拼接: weixin://dl/business/?appid=*APPID*&path=*PATH*&query=*QUERY*&env_version=*ENV_VERSION* 示例: // query需要encode编码,ENV_VERSION为控制打开开发版、体验版、线上版本 weixin://dl/business/?appid=wxa7b0b022472e55e6&path=pages/partdetails/partdetails&query=partJobId%3D123456 通过明文 URL Scheme 拉起的小程序(页面)必须要提前在「小程序管理后台 -> 设置 -> 隐私与安全 -> 明文 scheme 拉起此小程序」中进行声明; 小程序:配置能够通过明文 scheme 进入的小程序页面[图片] 这一点很烦很烦,希望能支持下 默认可打开所有的,不然每次都得去配置。
08-07 - 如何实现快速生成朋友圈海报分享图
由于我们无法将小程序直接分享到朋友圈,但分享到朋友圈的需求又很多,业界目前的做法是利用小程序的 Canvas 功能生成一张带有小程序码的图片,然后引导用户下载图片到本地后再分享到朋友圈。相信大家在绘制分享图中应该踩到 Canvas 的各种(坑)彩dan了吧~ 这里首先推荐一个开源的组件:painter(通过该组件目前我们已经成功在支付宝小程序上也应用上了分享图功能) 咱们不多说,直接上手就是干。 [图片] 首先我们新增一个自定义组件,在该组件的json中引入painter [代码]{ "component": true, "usingComponents": { "painter": "/painter/painter" } } [代码] 然后组件的WXML (代码片段在最后) [代码]// 将该组件定位在屏幕之外,用户查看不到。 <painter style="position: absolute; top: -9999rpx;" palette="{{imgDraw}}" bind:imgOK="onImgOK" /> [代码] 重点来了 JS (代码片段在最后) [代码]Component({ properties: { // 是否开始绘图 isCanDraw: { type: Boolean, value: false, observer(newVal) { newVal && this.handleStartDrawImg() } }, // 用户头像昵称信息 userInfo: { type: Object, value: { avatarUrl: '', nickName: '' } } }, data: { imgDraw: {}, // 绘制图片的大对象 sharePath: '' // 生成的分享图 }, methods: { handleStartDrawImg() { wx.showLoading({ title: '生成中' }) this.setData({ imgDraw: { width: '750rpx', height: '1334rpx', background: 'https://qiniu-image.qtshe.com/20190506share-bg.png', views: [ { type: 'image', url: 'https://qiniu-image.qtshe.com/1560248372315_467.jpg', css: { top: '32rpx', left: '30rpx', right: '32rpx', width: '688rpx', height: '420rpx', borderRadius: '16rpx' }, }, { type: 'image', url: this.data.userInfo.avatarUrl || 'https://qiniu-image.qtshe.com/default-avatar20170707.png', css: { top: '404rpx', left: '328rpx', width: '96rpx', height: '96rpx', borderWidth: '6rpx', borderColor: '#FFF', borderRadius: '96rpx' } }, { type: 'text', text: this.data.userInfo.nickName || '青团子', css: { top: '532rpx', fontSize: '28rpx', left: '375rpx', align: 'center', color: '#3c3c3c' } }, { type: 'text', text: `邀请您参与助力活动`, css: { top: '576rpx', left: '375rpx', align: 'center', fontSize: '28rpx', color: '#3c3c3c' } }, { type: 'text', text: `宇宙最萌蓝牙耳机测评员`, css: { top: '644rpx', left: '375rpx', maxLines: 1, align: 'center', fontWeight: 'bold', fontSize: '44rpx', color: '#3c3c3c' } }, { type: 'image', url: 'https://qiniu-image.qtshe.com/20190605index.jpg', css: { top: '834rpx', left: '470rpx', width: '200rpx', height: '200rpx' } } ] } }) }, onImgErr(e) { wx.hideLoading() wx.showToast({ title: '生成分享图失败,请刷新页面重试' }) //通知外部绘制完成,重置isCanDraw为false this.triggerEvent('initData') }, onImgOK(e) { wx.hideLoading() // 展示分享图 wx.showShareImageMenu({ path: e.detail.path, fail: err => { console.log(err) } }) //通知外部绘制完成,重置isCanDraw为false this.triggerEvent('initData') } } }) [代码] 那么我们该如何引用呢? 首先json里引用我们封装好的组件share-box [代码]{ "usingComponents": { "share-box": "/components/shareBox/index" } } [代码] 以下示例为获取用户头像昵称后再生成图。 [代码]<button class="intro" bindtap="getUserInfo">点我生成分享图</button> <share-box isCanDraw="{{isCanDraw}}" userInfo="{{userInfo}}" bind:initData="handleClose" /> [代码] 调用的地方: [代码]const app = getApp() Page({ data: { isCanDraw: false }, // 组件内部关掉或者绘制完成需重置状态 handleClose() { this.setData({ isCanDraw: !this.data.isCanDraw }) }, getUserInfo(e) { wx.getUserProfile({ desc: "获取您的头像昵称信息", success: res => { const { userInfo = {} } = res this.setData({ userInfo, isCanDraw: true // 开始绘制海报图 }) }, fail: err => { console.log(err) } }) } }) [代码] 最后绘制分享图的自定义组件就完成啦~效果图如下: [图片] tips: 文字居中实现可以看下代码片段 文字换行实现(maxLines)只需要设置宽度,maxLines如果设置为1,那么超出一行将会展示为省略号 代码片段:https://developers.weixin.qq.com/s/J38pKsmK7Qw5 附上painter可视化编辑代码工具:点我直达,因为涉及网络图片,代码片段设置不了downloadFile合法域名,建议真机开启调试模式,开发者工具 详情里开启不校验合法域名进行代码片段的运行查看。 最后看下面大家评论问的较多的问题:downLoadFile合法域名在小程序后台 开发>开发设置里配置,域名为你图片的域名前缀 比如我文章里的图https://qiniu-image.qtshe.com/20190605index.jpg。配置域名时填写https://qiniu-image.qtshe.com即可。如果你图片cdn地址为https://aaa.com/xxx.png, 那你就配置https://aaa.com即可。
2022-01-20 - 如何实现一个6位数的密码输入框
背景: 因为公司业务调整需要做用户支付这一块 开发者需要在小程序上实现一个简单的6位数密码输入框 [图片] 首先想下如何实现该效果: 1.使用input覆盖在框上面,设置letter-spacing达到数字之间间距的效果,实现时发现在input组件上使用letter-spacing无效果 2.循环六个view模拟的框,光标使用动画模拟,一个隐藏的input,点击view框时触发input的Focus属性弹起键盘,同时模拟的光标展示出来,输入值后,input的value长度发生变化,设置光标位置以及模拟的密码小黑圆点 好了,废话不多数,咱们直接上手。 wxml [代码]<view class='container'> <!-- 模拟输入框 --> <view class='pay-box {{focusType ? "focus-border" : ""}}' bindtap="handleFocus" style='width: 604rpx;height: 98rpx'> <block wx:for="{{boxList}}" wx:key="{{index}}"> <view class='password-box {{index === 0 ? "b-l-n":""}}'> <view wx:if="{{(dataLength === item - 1)&& focusType}}" class="cursor"></view> <view wx:if="{{dataLength >= item}}" class="input-black-dot"></view> </view> </block> </view> <!-- 隐藏input框 --> <input value="{{input_value}}" focus="{{isFocus}}" maxlength="6" type="number" class='hidden-input' bindinput="handleSetData" bindfocus="handleUseFocus" bindblur="handleUseFocus" /> </view> [代码] wxss [代码]/* 第一个格子输入框 */ .container .b-l-n { border-left: none; } .pay-box { margin: 0 auto; display: flex; flex-direction: row; border-left: 1px solid #cfd4d3; } /* 支付密码框聚焦的时候 */ .focus-border { border-color: #0c8; } /* 单个格式样式(聚焦的时候) */ .password-box { flex: 1; border: 1px solid #0c8; margin-right: 10rpx; display: flex; align-items: center; justify-content: center; } /* 模拟光标 */ .cursor { width: 2rpx; height: 36rpx; background-color: #0c8; animation: focus 1.2s infinite; } /* 光标动画 */ @keyframes focus { from { opacity: 1; } to { opacity: 0; } } /* 模拟输入的password的黑点 */ .input-black-dot { width: 20rpx; height: 20rpx; background-color: #000; border-radius: 50%; } /* 输入框 */ .hidden-input { margin-top: 200rpx; position: relative; } [代码] JS [代码]Component({ data: { //输入框聚焦状态 isFocus: false, //输入框聚焦样式 是否自动获取焦点 focusType: true, valueData: '', //输入的值 dataLength: '', boxList: [1, 2, 3, 4, 5, 6] }, // 组件属性 properties: { }, // 组件方法 methods: { // 获得焦点时 handleUseFocus() { this.setData({ focusType: true }) }, // 失去焦点时 handleUseBlur() { this.setData({ focusType: false }) }, // 点击6个框聚焦 handleFocus() { this.setData({ isFocus: true }) }, // 获取输入框的值 handleSetData(e) { // 更新数据 this.setData({ dataLength: e.detail.value.length, valueData: e.detail.value }) // 当输入框的值等于6时(发起支付等...) if (e.detail.value.length === 6) { // 通知用户输入数字达到6位数可以发送接口校验密码是否正确 this.triggerEvent('initData', e.detail.value) } } } }) [代码] 实现方式很简单,有点小问题,还有一些后续准备做的优化点,等完善后上线后再来修改一波。 最后附上代码片段: https://developers.weixin.qq.com/s/8CtRqJmT7W8k
2020-07-06 - 如何使用scroll-view制作左右滚动导航条效果
最新:2020/06/13。修改为scroll-view与swiper联动效果,新增下拉刷新以及上拉加载效果。。具体效果查看代码片段,以下文章内容和就不改了 刚刚在社区里看到 有老哥在问如何做滚动的导航栏。这里简单给他写了个代码片段,需要的大哥拿去随便改改,先看效果图: [图片] 代码如下: wxml [代码]<scroll-view class="scroll-wrapper" scroll-x scroll-with-animation="true" scroll-into-view="item{{currentTab < 4 ? 0 : currentTab - 3}}" > <view class="navigate-item" id="item{{index}}" wx:for="{{taskList}}" wx:key="{{index}}" data-index="{{index}}" bindtap="handleClick"> <view class="names {{currentTab === index ? 'active' : ''}}">{{item.name}}</view> <view class="currtline {{currentTab === index ? 'active' : ''}}"></view> </view> </scroll-view> [代码] wxss [代码].scroll-wrapper { white-space: nowrap; -webkit-overflow-scrolling: touch; background: #FFF; height: 90rpx; padding: 0 32rpx; box-sizing: border-box; } ::-webkit-scrollbar { width: 0; height: 0; color: transparent; } .navigate-item { display: inline-block; text-align: center; height: 90rpx; line-height: 90rpx; margin: 0 16rpx; } .names { font-size: 28rpx; color: #3c3c3c; } .names.active { color: #00cc88; font-weight: bold; font-size: 34rpx; } .currtline { margin: -8rpx auto 0 auto; width: 100rpx; height: 8rpx; border-radius: 4rpx; } .currtline.active { background: #47CD88; transition: all .3s; } [代码] JS [代码]const app = getApp() Page({ data: { currentTab: 0, taskList: [{ name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, ] }, onLoad() { }, handleClick(e) { let currentTab = e.currentTarget.dataset.index this.setData({ currentTab }) }, }) [代码] 最后奉上代码片段: https://developers.weixin.qq.com/s/nkyp64mN7fim
2020-06-13