- 微信小程序 -- 基于 movable-view 实现拖拽排序
微信小程序 – 基于 movable-view 实现拖拽排序 项目基于[代码]colorui[代码]样式组件 ColorUI组件库 (color-ui.com) 1.实现效果 [图片] 2. 设计思路 movable-view 绑定块移动事件的 块[代码]ID[代码] ,块移动的坐标 移动结束后触发[代码]moveEnd[代码]事件,根据[代码]Y[代码]坐标对对象数组进行排序 根据排序结果重置块位置 3.实现代码 代码已经进行了最简化处理 图中效果实现需引入[代码]colorui[代码]的[代码]main.wxss[代码]样式部分。 wxml [代码]<movable-area class="padding text-center bg-grey" style="width:100%;height:500px;" > <movable-view class="radius shadow bg-white" style="width:80%;height:80px;z-index:{{index==moveId?2:1}}" wx:for="{{tabList}}" wx:key="index" x="{{item.x}}" y="{{item.y}}" direction="all" bindchange="moveStatus" bindtouchend='moveEnd' data-moveid="{{index}}"> {{item.name}}</movable-view> </movable-area> [代码] js [代码]var compare = function (obj1, obj2) { var val1 = obj1.y; var val2 = obj2.y; if (val1 < val2) { return -1; } else if (val1 >= val2) { return 1; } else { return 0; } } Page({ /** * 页面的初始数据 */ data: { branchid:'', appdocid:'', tabList:[ { name:'十步杀一人' }, { name:'千里不留行' }, { name:'事了拂衣去' }, { name:'深藏身与名' } ], //移动的是哪个元素块 moveId:null, //最终停止的位置 endX:0, endY:0 }, initMove(){ let tabList = this.data.tabList; var tarr = [] tabList.forEach(function(ele,index){ let obj = ele obj.id = index obj.x = 30 obj.y = 100*index +20 tarr.push(obj) }) console.log(tarr) this.setData({ tabList:tarr }) }, moveEnd(e){ console.log(e) var that = this; that.setData({ ["tabList["+that.data.moveId+"].x"]:that.data.endX, ["tabList["+that.data.moveId+"].y"]:that.data.endY },()=>{ let tabList = this.data.tabList; tabList = tabList.sort(compare); that.setData({ tabList },()=>{ setTimeout(function(){ that.initMove(); },500) }) }) //计算位置 }, moveStatus(e){ // console.log(e) //移动的块ID var moveid = e.currentTarget.dataset.moveid; //最终坐标 let x = e.detail.x let y = e.detail.y this.setData({ moveId:moveid, endX:x, endY:y }) }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { this.initMove(); } }) [代码] 4.参考文档 movable-view | 微信开放文档 (qq.com)
2021-06-17 - 【改进版】如何从零实现上拉无限加载瀑布流组件
前言 之前分享过一篇瀑布流如何实现的文章,经过时间的证明,之前的做法并不好,性能上会有问题,所以还是不投机取巧了,老老实实的实现。 回顾: 通过grid-auto-rows的特性实现 item通过grid-row设置高度 js获取节点高度计算span的值 通过wxs设置css的变量实现修改样式 痛点: grid-auto-rows数值越大,span计算准确度越低。 谷歌浏览器、微信开发工具,如果界面高度超过[代码]1000 * grid-auto-rows[代码]的高度,那么后面的内容就不会显示了,谷歌解释说是为了不过渡消耗性能。 因为性能问题,超过1000的item就不会显示了,全会挤压在最下面,导致页面非常卡,开发工具能直接卡崩溃,手机上还没发现这个问题,之前也忽视了这个问题,后面调试的时候就非常恼火,开发工具跟真机上效果不一致。 为了保证span计算的准确度高,grid-auto-rows一般设置成1-10px,1px准确就等于view的高度,但是超过1000px就卡没了。 实现思路 通过selectAllComponents获取所有的子节点 通过getComputedStyle获取节点的高度 简单的排序算法计算节点位置 设置节点的样式 通过wxs的[代码]getState[代码]储存每屏节点渲染的数据 触发[代码]image[代码]组件的[代码]load[代码]事件重新计算并渲染节点位置 创建组件 需要开启抽象节点 [代码]// waterfall/index.json { "componentGenerics": { "selectable": true } } [代码] 利用wxs响应事件获取页面的节点 [代码]<view class="waterfall" views="{{ views.length }}" data-option="{{ {span} }}" change:views="{{ wxs.init }}" > <!-- 嵌套遍历views二维数组 --> <block wx:for="{{ views }}" wx:key="item" wx:for-index="i" > <selectable class="item view-{{ i }}" wx:for="{{ item }}" wx:key="item" value="{{ item }}" /> </block> </view> [代码] 创建item的x,y边距变量 [代码]--span[代码] [代码].waterfall { --span: 5px; width: 100%; position: relative; .item { width: calc(50% - var(--span)); position: absolute; } } [代码] 创建 [代码]index.wxs[代码],核心业务代码都写在这里 [代码]// 当views被setData的时候会被触发 module.export = { init: function(newValue, oldValue, ownerInstance, instance) { console.log(newValue, oldValue, ownerInstance, instance) } } [代码] 业务逻辑 步骤一:获取所有节点 [代码]function init(length, oldValue, ownerInstance, instance) { // 加个判断,避免views长度为0时,或者长度为发生变化时也会执行业务代码 // 只有当views被push新的内容才会执行下面的业务 if (!length || length === oldValue) return // index 其实就是views的长度减一,就等于当前的数组下标 var index = length - 1 var views = ownerInstance.selectAllComponents('.view-' + index) console.log(JSON.stringify(views)) } [代码] [图片] [图片] 步骤二:遍历views获取节点的高度 [代码]views.forEach(function(v, k){ var viewStyle = v.getComputedStyle(['width', 'height']) // 获取高度 var height = viewStyle.height console.log(viewStyle) // [WXS Runtime info] {"width":"182.5px", "height":"242px"} }) [代码] 步骤三:计算view的位置信息 [代码]var LH = 0 var RH = 0 views.forEach(function (v, k) { var viewStyle = v.getComputedStyle(['width', 'height']) // 格式化高度,将px去掉 var height = fixUnit(viewStyle.height) var style = {} if (LH <= RH) { style = { left: 0, top: LH + 'px' } LH += height } else if (RH < LH) { style = { right: 0, top: RH + 'px' } RH += height } // 设置view的样式 v.setStyle(style) }) [代码] 此时,页面的节点会根据position自动排列好 [图片] 步骤四:储存LH,RH到局部变量 [代码]function init(length, oldValue, ownerInstance, instance) { if (!length || length === oldValue) return // 获取局部变量 var state = ownerInstance.getState() // 获取当前节点的dataset var dataset = instance.getDataset() var index = length - 1 state.option = dataset.option state.page = length // 创建并生成记录左侧、右侧高度 // 用二维数组来记录 if (!state.heights) { state.heights = [[0, 0]] } // 记录初次渲染时间戳 if (!state.timeouts) { state.timeouts = [] } // 获取时间戳,并且加上3000毫秒,用于后面计算图片loaded完是否超时 state.timeouts[index] = getDate().getTime() + 3000 refreshViews(index, ownerInstance, state) } function refreshViews(index, ownerInstance, state) { var views = ownerInstance.selectAllComponents('.view-' + index) var span = state.option.span var LH = state.heights[index][0] // 左侧 var RH = state.heights[index][1] // 右侧 views.forEach(function (v, k) { var viewStyle = v.getComputedStyle(['width', 'height']) var height = fixUnit(viewStyle.height) var style = {} if (LH <= RH) { style = { left: 0, top: LH + 'px' } LH += height + span[0] } else if (RH < LH) { style = { right: 0, top: RH + 'px' } RH += height + span[0] } v.setStyle(style) // 保存LH, RH的值到state.heights // 当前的LH,RH其实就是下屏开始的坐标 state.heights[index + 1] = [LH, RH] console.log('渲染', index, k) }) } [代码] 步骤五:图片加载完重新计算位置 [代码]// waterfall/index.js Component({ properties: { views: Array, span: { type: Array, value: [10, 10], }, }, methods: { onLoaded({ detail: { width, height, pIndex, index } }) { this.setData({ [`views[${pIndex}][${index}].loaded`]: { width, height }, }) }, }, }) [代码] [代码]function loaded(value, oldValue, ownerInstance, instance) { if (!value.item.loaded || !oldValue) return // 获取局部变量 var state = ownerInstance.getState() // 判断加载是否超时,如果超时则不触发计算渲染事件 // 让该节点保持当前的位置及高度 var timeout = state.timeouts[value.pIndex] if (timeout < getDate().getTime()) { console.log('加载超时') return } var view = instance.selectComponent('.loaded-view') var viewWidth = view.getComputedStyle(['width']).width // 设置虚拟节点card组件里的loaded-view高度 view.setStyle({ height: computedHeight( viewWidth, value.item.loaded.width, value.item.loaded.height ) + 'px', }) // 加个函数防抖,因为图片加载快的情况下,会并发触发事件 // 尽可能的少触发计算,渲染事件,保证性能 ownerInstance.clearTimeout(timer) timer = ownerInstance.setTimeout(function () { // 渲染当前图片加载完后面的所有views // for循环处理当前图片所在的views,以及后面所有的views // 因为有些图片过大,可能会加载5s左右,但是用户如果上拉又加载了 // 一屏内容并且也通过计算渲染了,这时候上一屏又触发了计算渲染 // 那么可能位置信息就会发生变化,导致被遮挡,或者有空白,这时候只能 // 计算触发事件的图片以及后面的图片,保证位置信息是正确的 for (var i = value.pIndex; i < state.page; i++) { console.log('需要渲染', i) refreshViews(i, ownerInstance, state) } }, 300) } [代码] [图片] 优化 瀑布流最好后台会返回图片的尺寸信息,然后初次渲染的时候就计算好节点的长宽比例,这样就不用监听图片loaded事件了,瀑布流组件代码也不会频繁触发计算渲染,性能也好,方法也简单。 [代码]<image src="xxxx" style="{{ wxs.computed({width, height}) }}" /> [代码] [代码]// wxs function computed(option) { // 节点宽度自己去计算 var viewWidth = 375 / 2 var width = option.width var height = option.height return (viewWidth / width) * height + 'px } [代码] 完整代码 打开代码片段https://developers.weixin.qq.com/s/SO5q6UmF7doL,可直接运行。 https://github.com/liziwork/li-ui github 如果打不开,请切换到码云,gitee.com,代码同步更新的,觉得有用动动您的小手点个Star。 扫码查看更多组件 [图片]
2021-03-19 - 微信小程序tree-select下拉复选组件
[图片] 做的不好的还请指正,自己摸索好久,雀食是比较菜的 微信代码片段:https://developers.weixin.qq.com/s/vWL2CcmX7Zwv gittee仓库https://gitee.com/hwt_code/wx-tree-select.git csdn文章https://blog.csdn.net/usernotdefined/article/details/121392970
2022-01-10 - 请问如何实现微信小程序跳到小商店?
谢谢大佬回答
2020-09-05 - "@babel/runtime/helpers/Objectvalues" 错误?
今天上线一个新版本有很多下面这个错误,好像是新开发工具的问题,我更新了这个版本之后在开发工具中也遇到过一次,重新编译就好了,今天上线之后报警了,很多类似下面的错误 开发者工具版本:1.04.2006112 Error line:2,column:1892936,Error: module "@babel/runtime/helpers/Objectvalues" is not defined Stack:require@https://lib/WASubContext.js:2:1892936 https://lib/WASubContext.js:2:1892555 https://usr//packageEvent/app-service.js:1941:21 require@https://lib/WASubContext.js:2:1893066 global code@https://usr//packageEvent/app-service.js:1942:17 value@https://lib/WAServiceMainContext.js:2:148496 https://lib/WAServiceMainContext.js:2:150988 https://lib/WAServiceMainContext.js:2:150955 h@https://lib/WAServiceMainContext.js:2:81109 g@https://lib/WAServiceMainContext.js:2:81359 a@https://lib/WAServiceMainContext.js:2:150162 https://lib/WAServiceMainContext.js:2:151529 https://lib/WAServiceMainContext.js:2:142012 https://lib/WAServiceMainContext.js:2:250179 https://lib/WAServiceMainContext.js:2:249147 https://lib/WAServiceMainContext.js:2:249111 https://lib/WAServiceMainContext.js:2:250179 _@https://lib/WAServiceMainContext.js:2:77927
2020-06-17 - 小程序报错找不到@babel/runtime/helpers/interopRequire ?
前面还好好的,突然报错 WAService.js:1 Uncaught Error: can't find module : ../../../../@babel/runtime/helpers/interopRequireDefault at WAService.js:1 这是啥意思?
2020-05-24 - 显示插屏广告的方法经常报错
- 代码 [代码]let intsetAd = wx.createInterstitialAd({[代码][代码] [代码][代码]adUnitId: [代码][代码]'adunit-6782f5dc36d498e4'[代码][代码]})[代码][代码]intsetAd.show().then(() => {//出错行view.js:749[代码][代码] [代码][代码]if[代码] [代码](!page.data.adFlag){[代码][代码] [代码][代码]page.setData({ adFlag: 1 })[代码][代码] [代码][代码]}[代码][代码]}).[代码][代码]catch[代码][代码](err => {[代码][代码] [代码][代码]console.log(err.errMsg)[代码][代码] [代码][代码]page.setData({ adFlag: 2 })[代码][代码]})[代码] - 当前 Bug 的表现(可附上截图) Object.values is not a function. (In 'Object.values(ub.show)', 'Object.values' is undefined);at pages/item/view page adInsert functionhttps://lib/WAService.js:1:738513Im@https://lib/WAService.js:1:738574value@https://lib/WAService.js:1:745315adInsert@https://pages/item/view.js:749:14https://lib/WAService.js:1:1007171
2019-04-19 - 10行代码实现微信小程序支付功能,使用小程序云开发实现小程序支付功能(含源码)
前面给大家讲过一个借助小程序云开发实现微信支付的,但是那个操作稍微有点繁琐,并且还会经常出现问题,今天就给大家讲一个简单的,并且借助官方支付api实现小程序支付功能。 传送门 借助小程序云开发实现小程序支付功能 老规矩,先看本节效果图 [图片] 我们实现这个支付功能完全是借助小程序云开发实现的,不用搭建自己的服务器,不用买域名,不用备案域名,不用支持https。只需要一个简单的云函数,就可以轻松的实现微信小程序支付功能。 核心代码就下面这些 [图片] 一,创建一个云开发小程序 关于如何创建云开发小程序,这里我就不再做具体讲解。不知道怎么创建云开发小程序的同学,可以去翻看我之前的文章,或者看下我录制的视频:https://edu.csdn.net/course/play/9604/204528 创建云开发小程序有几点注意的 1,一定不要忘记在app.js里初始化云开发环境。 [图片] 2,创建完云函数后,一定要记得上传 二, 创建支付的云函数 1,创建云函数pay [图片] [图片] 三,引入三方依赖tenpay 我们这里引入三方依赖的目的,是创建我们支付时需要的一些参数。我们安装依赖是使用里npm 而npm必须安装node,关于如何安装node,我这里不做讲解,百度一下,网上一大堆。 1,首先右键pay,然后选择在终端中打开 [图片] 2,我们使用npm来安装这个依赖。 在命令行里执行 npm i tenpay [图片] 安装完成后,我们的pay云函数会多出一个package.json 文件 [图片] 到这里我们的tenpay依赖就安装好了。 四,编写云函数pay [图片] 完整代码如下 [代码]//云开发实现支付 const cloud = require('wx-server-sdk') cloud.init() //1,引入支付的三方依赖 const tenpay = require('tenpay'); //2,配置支付信息 const config = { appid: '你的小程序appid', mchid: '你的微信商户号', partnerKey: '微信支付安全密钥', notify_url: '支付回调网址,这里可以先随意填一个网址', spbill_create_ip: '127.0.0.1' //这里填这个就可以 }; exports.main = async(event, context) => { const wxContext = cloud.getWXContext() let { orderid, money } = event; //3,初始化支付 const api = tenpay.init(config); let result = await api.getPayParams({ out_trade_no: orderid, body: '商品简单描述', total_fee: money, //订单金额(分), openid: wxContext.OPENID //付款用户的openid }); return result; } [代码] 一定要注意把appid,mchid,partnerKey换成你自己的。 到这里我们获取小程序支付所需参数的云函数代码就编写完成了。 不要忘记上传这个云函数。 [图片] 出现下图就代表上传成功 [图片] 五,写一个简单的页面,用来提交订单,调用pay云函数。 [图片] 这个页面很简单, 1,自己随便编写一个订单号(这个订单号要大于6位) 2,自己随便填写一个订单价(单位是分) 3,点击按钮,调用pay云函数。获取支付所需参数。 下图是官方支付api所需要的一些必须参数。 [图片] 下图是我们调用pay云函数获取的参数,和上图所需要的是不是一样。 [图片] 六,调用wx.requestPayment实现支付 下图是官方的示例代码 [图片] 这里不在做具体讲解了,把完整代码给大家贴出来 [代码]// pages/pay/pay.js Page({ //提交订单 formSubmit: function(e) { let that = this; let formData = e.detail.value console.log('form发生了submit事件,携带数据为:', formData) wx.cloud.callFunction({ name: "pay", data: { orderid: "" + formData.orderid, money: formData.money }, success(res) { console.log("提交成功", res.result) that.pay(res.result) }, fail(res) { console.log("提交失败", res) } }) }, //实现小程序支付 pay(payData) { //官方标准的支付方法 wx.requestPayment({ timeStamp: payData.timeStamp, nonceStr: payData.nonceStr, package: payData.package, //统一下单接口返回的 prepay_id 格式如:prepay_id=*** signType: 'MD5', paySign: payData.paySign, //签名 success(res) { console.log("支付成功", res) }, fail(res) { console.log("支付失败", res) }, complete(res) { console.log("支付完成", res) } }) } }) [代码] 到这里,云开发实现小程序支付的功能就完整实现了。 实现效果 1,调起支付键盘 [图片] 2,支付完成 [图片] 3,log日志,可以看出不同支付状态的回调 [图片] 上图是支付成功的回调,我们可以在支付成功回调时,改变订单支付状态。 下图是支付失败的回调, [图片] 下图是支付完成的状态。 [图片] 到这里我们就轻松的实现了微信小程序的支付功能了。是不是很简单啊。 如果感觉图文不是很好理解,我后面会录制视频讲解。 视频讲解 https://edu.csdn.net/course/detail/25701 源码地址: https://github.com/qiushi123/xiaochengxu_demos [图片] 014云开发实现小程序支付,就是我们的源码,如果你导入源码或者学习过程中有任何问题,都可以加我微信2501902696(备注小程序)
2019-08-14