- 小程序拖动浮窗组件的一种实现(可二次封装业务组件使用)
文档 Fab 浮动按钮 可拖动的悬浮窗按钮 Props 参数 说明 类型 可选值 默认值 position 距离对象 Boolean – {left:0,right:0,top:0,bottom:0} left 左边距离(无需单位) Number – 0 right 右边距离(无需单位) Number/String – 0 top 顶部距离(无需单位) Number/String – 0 bottom 底部距离(无需单位) Number/String – 0 h-margin 拖动后的水平方向最小边距(左右边距) Number/String – 10px v-margin 拖动后的垂直方向最小边距(上下边距) Number/String – 10px 注:rpx单位对应的是750的屏幕,上述属性取值使用375下的数值,如果是750设计稿在使用时需手动除2传入 浮动按钮位置仅推荐left、top / right 、bottom 两两使用,默认优先使用bottom、right配置 Slots name 说明 default 悬浮窗默认插槽,用户可自定义内容及大小 Events 无 Methods 无 使用示例 支持二次封装业务组件以复用 [代码]<!-- 375设计稿下位于右下10rpx、100rpx的浮动按钮 --> <c-fab bottom="100" right='10'> <view class="float" style="width:100rpx;height:100rpx;border-radius: 99em;background:#fad84c;color:#333;display:flex;align-items:center;justify-content: center;">活动</view> </c-fab> [代码] 完整实现代码 [代码]<movable-area> <movable-view bind:touchend="onTouchend" bind:touchmove="onTouchMove" bind:touchstart="addAnimation" animation="{{animation}}" style="width:{{elementWidth}}px;height:{{elementHeight}}px" x="{{x}}" y="{{y}}" direction="all"> <view class="float-box" > <slot></slot> </view> </movable-view> </movable-area> [代码] [代码]Component({ /** * 组件的属性列表 */ properties: { position: { type: Object, value: { left: 0, right: 0, bottom: 0, top: 0 } // left、top、right、bottom }, left: { type: Number, value: 0 }, right: { type: Number, value: 0 }, top: { type: Number, value: 0 }, bottom: { type: Number, value: 0 }, hMargin: { // 拖动后的水平方向最小边距(左右边距) type: Number, value: 10 }, vMargin: { // 拖动后的垂直方向最小边距(上下边距) type: Number, value: 10 } }, data: { x: 999, y: 999, windowWidth: wx.getSystemInfoSync().windowWidth, windowHeight: wx.getSystemInfoSync().windowHeight, elementWidth: 0, elementHeight: 0, animation: false, isMoved: false // 是否拖动 }, lifetimes: { attached() { // 初始化位置 wx.createSelectorQuery().in(this).select('.float-box').boundingClientRect().exec((res) => { console.log(233, res); this.data.elementWidth = res[0].width; this.data.elementHeight = res[0].height; if (this.properties.position.left || this.properties.left) { this.data.x = this.properties.position.left || this.properties.left; } if (this.properties.position.right || this.properties.right) { this.data.x = this.data.windowWidth - this.data.elementWidth - (this.properties.position.right ? this.properties.position.right : this.properties.right); } if (this.properties.position.top || this.properties.top) { this.data.y = this.properties.position.top || this.properties.top; } if (this.properties.position.bottom || this.properties.bottom) { this.data.y = this.data.windowHeight - this.data.elementHeight - (this.properties.position.bottom ? this.properties.position.bottom : this.properties.bottom); } this.setData({ elementWidth: this.data.elementWidth, elementHeight: this.data.elementHeight, x: this.data.x, y: this.data.y }); }); } }, /** * 组件的方法列表 */ methods: { onTouchend(e) { console.log(this.data.isMoved, this.data.x, e.changedTouches[0].clientX, this.data.elementWidth); if (!this.data.isMoved) return; const currentX = e.changedTouches[0].clientX; let currentY = e.changedTouches[0].clientY; if (currentY <= this.properties.vMargin) { currentY = this.properties.vMargin + this.data.elementHeight / 2; } if (currentY >= this.data.windowHeight - this.properties.vMargin) { currentY = this.data.windowHeight - this.properties.vMargin - this.data.elementHeight / 2; } if (currentX + this.data.elementWidth / 2 > this.data.windowWidth / 2) { this.setData({ x: this.data.windowWidth - this.properties.hMargin - this.data.elementWidth, y: currentY - this.data.elementHeight / 2 }); } if (currentX + this.data.elementWidth / 2 <= this.data.windowWidth / 2) { this.setData({ x: this.properties.hMargin, y: currentY - this.data.elementHeight / 2 }); } }, addAnimation() { this.data.isMoved = false; if (!this.data.animation) { this.setData({ animation: true }); } }, onTouchMove() { this.data.isMoved = true; } } }); [代码] [代码]movable-area { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; pointer-events: none; z-index: 50; movable-view { pointer-events: auto; //可以点击 } .float-box { display: inline-block; } } [代码] [代码]{ "component": true, "usingComponents": {} } [代码]
01-18 - 社区常见相关问题总结贴--新增外部链接管理规范、模版消息公告
9月份社区为更好地保护用户隐私信息,优化用户体验,平台将会对小程序内的帐号登录功能进行规范、然后总结下关于新规以及社区的常见问题、不定时增加社区重要公告 增加小程序违规公告:https://developers.weixin.qq.com/community/operate 增加小程序模版消息公告:https://developers.weixin.qq.com/community/develop/doc/00008a8a7d8310b6bf4975b635a401?blockType=1 增加微信外部链接内容管理规范:https://weixin.qq.com/cgi-bin/readtemplate?t=weixin_external_links_content_management_specification&from=timeline&isappinstalled=0 相关链接模版消息公告 新规范链接 类目资质 恶意对抗平台规则的违规行为公告 小程序修改名称说明 小程序账号相关问题 小程序常见违规整改处理方案 微信外部链接内容管理规范 常见问题 服务范围开放的小程序? 对于用户注册流程是对外开放、无需验证特定范围用户,且注册后即可提供线上服务的小程序,不得在用户清楚知悉、了解小程序的功能之前,要求用户进行帐号登录。 若小程序属于第一种服务范围开放的小程序,还是建议可以在体验小程序功能后,用户主动点击登录按钮后触发登录流程,且为用户提供暂不登录选项 服务范围特定的小程序? 对于客观上服务范围特定、未完全开放用户注册,需通过更多方式完成身份验证后才能提供服务的小程序,可以直接引导用户进行帐号登录。例如为学校系统、员工系统、社保卡信息系统等提供服务的小程序 只用于公司内部使用的小程序,应该怎么改? 公司内部小程序,只限公司内部使用。如果是公司内部人员,授权微信会自动登录,或者后台数据库存在的手机号码,利用手机验证码登录也可以,这个并没有账号密码登录模式。 你好,经核实,贵方小程序功能无法体验,建议增加一种登录方式(如:账号、密码),并提供可登录体验的测试账号信息,填写在 版本描述处提交,以便审核人员及时体验到小程序功能 经核实,贵方小程序打开即要求授权信息,且点击取消授权后仍强制授权,为企业内部工具,建议在小程序的登录页面明确介绍小程序的具体功能,并且在登录及授权界面为用户提供接受/拒绝登录的权利,由用户自主选择是否进一步授权登录。 类似本地化生活服务的小程序只开通了部分城市,审核被拒绝? 经核实,贵方小程序打开提示:该地区暂未开放,功能无法体验,建议增加手动定位,并在版本描述中写明已上架正式内容的城市,以便审核人员审核。 审核团队的测试微信或手机号并未在我们数据库存在,导致审核失败? 或者类似的问题:我们可以提供测试手机号,或者测试微信账号密码,但是这都涉及到手机验证码,请问当你们登录的时候,我如何把验证码发送给你们? 请将测试信息填写在版本描述处提交审核,审核人员不能对外联系,所以请提供一个写死的验证码,感谢您的支持和理解! 因为小程序特殊性,用户打开必须要获取用户位置。地理位置授权影响登陆规范要求? 你好,如小程序仅要求地理位置授权,暂不属于帐号登录规范要求内 关键字搜索排序与审核通过后搜索不到小程序? 系统会根据query和小程序的相关性来判断召回和排序,后续我们会优化搜索策略,感谢反馈。有异议,提供appid、搜索词、搜索入口、搜索页面截图 刚审核通过发布后搜索不到是有延迟的,大概半天左右的延迟。如果着急可以发帖,注意发帖规范、附上小程序的APPID 仅提供注册服务的小程序,审核拒绝? 你好,如果你的小程序仅提供线上注册功能,后续服务是需要以其他方式提供的话,可以在说明要求使用帐号登录功能的原因后,引导用户进行帐号注册或登录。 然后官方加了一句⬇️。说明让用户体验小程序功能是很重要的。 而如果你的小程序除了线上注册外,还同时提供其他线上服务,建议先让用户体验、知悉小程序的功能后,再要求用户进行注册、登录。 审核好几天了,官方可以加急审核吗?? 你好,暂不支持加急审核,请耐心等待审核结果。根据社区规定,提审时间为7个工作日内的催审问题暂不予反馈,故此贴隐藏 关于 企业信息或法定代表人信息不一致? 建议发帖联系客服。注意发帖格式 等15个工作日,平台是拉取的工商局的数据。 可以联系客服工作人员吗,急在线等。 联系客服工作人员的正确姿势 是想咨询什么问题呢?不方便发帖咨询的话可以私信哦(进入个人主页私信功能) 审核超过7天了还是没有消息?? 审核时间为7个工作日,和7天还是有点不一样。 审核超过7个工作日,请刷新页面注意查看站内信,点击小🔔查看最新审核消息。 为什么审核我的小程序这么久,别人可以那么快? 官方整理回答 为什么你以前可以2-3小时通过审核,是因为你在运营/性能/用户等指标都达到优秀,所以符合小程序评测——优秀的标准,因此拥有急速审核的权益。常见问题见:https://kf.qq.com/faq/190108BJnmUN190108RrEnqE.html 你在某一次提交版本之后,小程序的性能存在问题,被我们检测到,所以便失去急速审核的权益,如果你需要急速审核,请你优化好小程序的性能再重新提交,你们可以自己在开发阶段利用工具的体验评分面板先自查一次,详细见https://developers.weixin.qq.com/miniprogram/dev/devtools/audits.html 最后补充一点,如果没有达到优秀的标准,日常承诺处理的审核时间是7个工作日内,如果没有超过7个工作日,催提审是不受理的 好消息是:官方加急审核的权益已经在开发,估计不久后会上线。 同一开放平台账号,绑定的移动应用和公众号,获取的unionid为什么不一致? 只要绑在一个开发者帐号下,即使主体不一样,也允许获取到统一的unionID。绑定同一个微信开放平台帐号下,同一个用户的unionID如果不同的,原因只能是开发者搞混openid。openid要对应所属的AppID,才会相同。 举个例子: 1. 小程序AppID:wxc104eb635b8cxxxx ——帐号A, 公众号AppID:wx311a2a9a8e1dxxxx ——帐号B, 2.核实帐号A和帐号B 绑定同一个微信开放平台帐号是:xxxxxx@sina.com ,所以用一个用户的unionID相同, 3.而开发者所反馈的出现unionID不同,原因是:所提供的openid不属于帐号A,也不属于帐号B,而是属于帐号C或帐号D,而帐号C或帐号D并没有绑定在同一个微信开放平台帐号下,所以unionID不同。
2020-01-10 - 搭建一个https网站的全过程
概述:本着学习的目的,做了这个分享。自己切切实实的做完了整个流程,发现其中的坑也是蛮多的,当然自己的收获对应也是蛮多的。写下这个流程一方面为了加深自己的印象、可以在将来回顾一下,另一方面也是为了给有需要的人提供帮助~ 一、服务器准备 [代码]为了演示方便,我购买了一台腾讯云服务器 [代码] [图片] 安装的操作系统是centos 二、域名准备 1、域名注册 可以从万网上进行注册:https://wanwang.aliyun.com/ 2、域名备案 备案流程略复杂,这里只列了一个步骤简介 入口:https://beian.aliyun.com/ [图片] 域名的备案时间较长,建议大家提前准备起来。 3、域名解析 域名备案通过之后,为我们的网站准备一个子域名,入口:https://dns.console.aliyun.com/?spm=a2c1d.8251892.aliyun_sidebar.daliyun_sidebar_dns.37575b76kNuXEO#/dns/domainList [图片] 点击上图的解析设置 [图片] 将记录值填写我们刚刚买的服务器的公网ip 三、申请ssl证书 我是从腾讯提供的证书服务里申请的,腾讯申请入口 https://console.cloud.tencent.com/ssl 点击“申请免费证书” [图片] 这里选择了手动Dns验证 [图片] 申请完成后会有个表格,是说明如何Dns校验的,要求域名下添加一条解析记录 [图片] Dns校验 首先,在域名下添加一条txt记录 [图片] 然后,单机自助诊断旁边的“查询” [图片] 下载证书 [图片] 点击图中的下载即可 四、服务器安装软件 我用ssh连接服务器 登录服务器:ssh root@212.129.*.* , 然后输入密码(从腾讯云管理后台进行密码的设置和获取)进入服务器,然后安装软件,如下~ 1、git:用于代码管理 2、nvm:用于管理node版本 3、node:用于启动web服务 4、pm2:用于守护node进程 安装git [代码]yum install git -y [代码] 下载nvm [代码]git clone git://github.com/creationix/nvm.git ~/nvm [代码] 设置nvm 自动运行; [代码]echo "source ~/nvm/nvm.sh" >> ~/.bashrc source ~/.bashrc [代码] 查询node版本 [代码]nvm list-remote [代码] 安装node.js [代码]nvm install v10.16.3 [代码] 使用nodejs [代码]nvm use v10.16.3 [代码] 使用npm安装pm2 [代码]npm install -g pm2 [代码] 五、下载一个web项目 & 使用 pm2 启动 这里我使用了自己的一个github上的项目:https://github.com/myronliu/ssr-koa-react-redux.git 1、git clone https://github.com/myronliu/ssr-koa-react-redux.git 2、cd ssr-koa-react-redux 3、npm install 4、pm2 start server.js 服务启动之后如下图: [图片] 现在我们可以用IP(服务器的公网ip) + 端口号来进行访问了 [图片] 六、安装nginx 步骤 1: 添加 yum 源 Nginx 不在默认的 yum 源中,可以使用 epel 或者官网的 yum 源,本例使用官网的 yum 源。 sudo rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm 安装完 yum 源之后,可以查看一下。 sudo yum repolist 步骤 2: 安装 yum 安装 Nginx,非常简单,一条命令: sudo yum install nginx 常用nginx命令介绍 设置开机启动 $ sudo systemctl enable nginx 启动服务 $ sudo systemctl start nginx 停止服务 $ sudo systemctl restart nginx 重新加载,因为一般重新配置之后,不希望重启服务,这时可以使用重新加载。 $ sudo systemctl reload nginx 步骤3: 打开防火墙端口 sudo firewall-cmd --permanent --zone=public --add-service=http sudo firewall-cmd --permanent --zone=public --add-service=https sudo firewall-cmd --reload [图片] 步骤4: 配置nginx 找到并查看配置文件/etc/nginx/nginx.conf [图片] 步骤5: 配置我们自己的conf [图片] 编写为 [图片] 步骤6: 现在可以使用域名访问我们的网站了,目前是http协议的 [图片] 七、安装证书 将第三步下载的证书里选择Nginx下的两个文件上传到服务器的/etc/nginx/conf.d文件夹下 [图片] Mac上利用scp上传 scp /Users/xxx/Downloads/todo.xxx.com/Nginx/1_todo.xx.com_bundle.crt root@212.129.*.*:/etc/nginx/conf.d [图片] 八、创建https的conf文件 进入到/etc/nginx/conf.d下,执行命令:touch https.conf && vi https.conf [图片] 测试nginx配置 & 重启 [图片] 九、访问https协议的站点 [图片] 备注: 如有问题,欢迎指出! 如有侵权,联系删除~
2019-09-24 - 【开箱即用】分享几个好看的波浪动画css效果!
以下代码不一定都是本人原创,很多都是借鉴参考的(模仿是第一生产力嘛),有些已忘记出处了。以下分享给大家,供学习参考!欢迎收藏补充,说不定哪天你就用上了! 一、第一种效果 [图片] [代码]//index.wxml <view class="zr"> <view class='user_box'> <view class='userInfo'> <open-data type="userAvatarUrl"></open-data> </view> <view class='userInfo_name'> <open-data type="userNickName"></open-data> , 欢迎您 </view> </view> <view class="water"> <view class="water-c"> <view class="water-1"> </view> <view class="water-2"> </view> </view> </view> </view> //index.wxss .zr { color: white; background: #4cb4e7; /*#0396FF*/ width: 100%; height: 100px; position: relative; } .water { position: absolute; left: 0; bottom: -10px; height: 30px; width: 100%; z-index: 1; } .water-c { position: relative; } .water-1 { background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgd2lkdGg9IjYwMHB4IiBoZWlnaHQ9IjYwcHgiIHZpZXdCb3g9IjAgMCA2MDAgNjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiPgogICAgPCEtLSBHZW5lcmF0b3I6IFNrZXRjaCAzLjQgKDE1NTc1KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT53YXRlci0xPC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGRlZnM+PC9kZWZzPgogICAgPGcgaWQ9IuaIkSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc2tldGNoOnR5cGU9Ik1TUGFnZSI+CiAgICAgICAgPGcgaWQ9Ii0iIHNrZXRjaDp0eXBlPSJNU0FydGJvYXJkR3JvdXAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xMjEuMDAwMDAwLCAtMTMzLjAwMDAwMCkiIGZpbGwtb3BhY2l0eT0iMC4zIiBmaWxsPSIjRkZGRkZGIj4KICAgICAgICAgICAgPGcgaWQ9IndhdGVyLTEiIHNrZXRjaDp0eXBlPSJNU0xheWVyR3JvdXAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyMS4wMDAwMDAsIDEzMy4wMDAwMDApIj4KICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0wLDcuNjk4NTczOTUgTDQuNjcwNzE5NjJlLTE1LDYwIEw2MDAsNjAgTDYwMCw3LjM1MjMwNDYxIEM2MDAsNy4zNTIzMDQ2MSA0MzIuNzIxMDUyLDI0LjEwNjUxMzggMjkwLjQ4NDA0LDcuMzU2NzQxODcgQzE0OC4yNDcwMjcsLTkuMzkzMDMwMDggMCw3LjY5ODU3Mzk1IDAsNy42OTg1NzM5NSBaIiBpZD0iUGF0aC0xIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj48L3BhdGg+CiAgICAgICAgICAgIDwvZz4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==") repeat-x; background-size: 600px; -webkit-animation: wave-animation-1 3.5s infinite linear; animation: wave-animation-1 3.5s infinite linear; } .water-2 { top: 5px; background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cjxzdmcgd2lkdGg9IjYwMHB4IiBoZWlnaHQ9IjYwcHgiIHZpZXdCb3g9IjAgMCA2MDAgNjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiPgogICAgPCEtLSBHZW5lcmF0b3I6IFNrZXRjaCAzLjQgKDE1NTc1KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT53YXRlci0yPC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGRlZnM+PC9kZWZzPgogICAgPGcgaWQ9IuaIkSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc2tldGNoOnR5cGU9Ik1TUGFnZSI+CiAgICAgICAgPGcgaWQ9Ii0iIHNrZXRjaDp0eXBlPSJNU0FydGJvYXJkR3JvdXAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xMjEuMDAwMDAwLCAtMjQ2LjAwMDAwMCkiIGZpbGw9IiNGRkZGRkYiPgogICAgICAgICAgICA8ZyBpZD0id2F0ZXItMiIgc2tldGNoOnR5cGU9Ik1TTGF5ZXJHcm91cCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTIxLjAwMDAwMCwgMjQ2LjAwMDAwMCkiPgogICAgICAgICAgICAgICAgPHBhdGggZD0iTTAsNy42OTg1NzM5NSBMNC42NzA3MTk2MmUtMTUsNjAgTDYwMCw2MCBMNjAwLDcuMzUyMzA0NjEgQzYwMCw3LjM1MjMwNDYxIDQzMi43MjEwNTIsMjQuMTA2NTEzOCAyOTAuNDg0MDQsNy4zNTY3NDE4NyBDMTQ4LjI0NzAyNywtOS4zOTMwMzAwOCAwLDcuNjk4NTczOTUgMCw3LjY5ODU3Mzk1IFoiIGlkPSJQYXRoLTIiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDMwMC4wMDAwMDAsIDMwLjAwMDAwMCkgc2NhbGUoLTEsIDEpIHRyYW5zbGF0ZSgtMzAwLjAwMDAwMCwgLTMwLjAwMDAwMCkgIj48L3BhdGg+CiAgICAgICAgICAgIDwvZz4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==") repeat-x; background-size: 600px; -webkit-animation: wave-animation-2 6s infinite linear; animation: wave-animation-2 6s infinite linear; } .water-1, .water-2 { position: absolute; width: 100%; height: 60px; } .back-white { background: #fff; } @keyframes wave-animation-1 { 0% { background-position: 0 top; } 100% { background-position: 600px top; } } @keyframes wave-animation-2 { 0% { background-position: 0 top; } 100% { background-position: 600px top; } } .user_box { display: flex; z-index: 10000 !important; opacity: 0; /* 透明度*/ animation: love 1.5s ease-in-out; animation-fill-mode: forwards; } .userInfo_name { flex: 1; vertical-align: middle; width: 100%; margin-left: 5%; margin-top: 5%; font-size: 42rpx; } .userInfo { flex: 1; width: 100%; border-radius: 50%; overflow: hidden; max-height: 50px; max-width: 50px; margin-left: 5%; margin-top: 5%; border: 2px solid #fff; } [代码] 二、第二种效果 [图片] [代码]//index.wxml <view class="waveWrapper waveAnimation"> <view class="waveWrapperInner bgTop"> <view class="wave waveTop" style="background-image: url('https://s2.ax1x.com/2019/09/26/um8g7n.png')"></view> </view> <view class="waveWrapperInner bgMiddle"> <view class="wave waveMiddle" style="background-image: url('https://s2.ax1x.com/2019/09/26/umGZ38.png')"></view> </view> <view class="waveWrapperInner bgBottom"> <view class="wave waveBottom" style="background-image: url('https://s2.ax1x.com/2019/09/26/umGuuQ.png')"></view> </view> </view> //index.wxss .waveWrapper { overflow: hidden; position: absolute; left: 0; right: 0; height: 300px; top: 0; margin: auto; } .waveWrapperInner { position: absolute; width: 100%; overflow: hidden; height: 100%; bottom: -1px; background-image: linear-gradient(to top, #86377b 20%, #27273c 80%); } .bgTop { z-index: 15; opacity: 0.5; } .bgMiddle { z-index: 10; opacity: 0.75; } .bgBottom { z-index: 5; } .wave { position: absolute; left: 0; width: 500%; height: 100%; background-repeat: repeat no-repeat; background-position: 0 bottom; transform-origin: center bottom; } .waveTop { background-size: 50% 100px; } .waveAnimation .waveTop { animation: move-wave 3s; -webkit-animation: move-wave 3s; -webkit-animation-delay: 1s; animation-delay: 1s; } .waveMiddle { background-size: 50% 120px; } .waveAnimation .waveMiddle { animation: move_wave 10s linear infinite; } .waveBottom { background-size: 50% 100px; } .waveAnimation .waveBottom { animation: move_wave 15s linear infinite; } @keyframes move_wave { 0% { transform: translateX(0) translateZ(0) scaleY(1) } 50% { transform: translateX(-25%) translateZ(0) scaleY(0.55) } 100% { transform: translateX(-50%) translateZ(0) scaleY(1) } } [代码] 三、第三种效果 [图片] [代码]//index.wxml <view class="container"> <image class="title" src="https://ftp.bmp.ovh/imgs/2019/09/74bada9c4143786a.png"></image> <view class="content"> <view class="hd" style="transform:rotateZ({{angle}}deg);"> <image class="logo" src="https://ftp.bmp.ovh/imgs/2019/09/d31b8fcf19ee48dc.png"></image> <image class="wave" src="wave.png" mode="aspectFill"></image> <image class="wave wave-bg" src="wave.png" mode="aspectFill"></image> </view> <view class="bd" style="height: 100rpx;"> </view> </view> </view> //index.wxss image{ max-width:none; } .container { background: #7acfa6; align-items: stretch; padding: 0; height: 100%; overflow: hidden; } .content{ flex: 1; display: flex; position: relative; z-index: 10; flex-direction: column; align-items: stretch; justify-content: center; width: 100%; height: 100%; padding-bottom: 450rpx; background: -webkit-gradient(linear, left top, left bottom, from(rgba(244,244,244,0)), color-stop(0.1, #f4f4f4), to(#f4f4f4)); opacity: 0; transform: translate3d(0,100%,0); animation: rise 3s cubic-bezier(0.19, 1, 0.22, 1) .25s forwards; } @keyframes rise{ 0% {opacity: 0;transform: translate3d(0,100%,0);} 50% {opacity: 1;} 100% {opacity: 1;transform: translate3d(0,450rpx,0);} } .title{ position: absolute; top: 30rpx; left: 50%; width: 600rpx; height: 200rpx; margin-left: -300rpx; opacity: 0; animation: show 2.5s cubic-bezier(0.19, 1, 0.22, 1) .5s forwards; } @keyframes show{ 0% {opacity: 0;} 100% {opacity: .95;} } .hd { position: absolute; top: 0; left: 50%; width: 1000rpx; margin-left: -500rpx; height: 200rpx; transition: all .35s ease; } .logo { position: absolute; z-index: 2; left: 50%; bottom: 200rpx; width: 160rpx; height: 160rpx; margin-left: -80rpx; border-radius: 160rpx; animation: sway 10s ease-in-out infinite; opacity: .95; } @keyframes sway{ 0% {transform: translate3d(0,20rpx,0) rotate(-15deg); } 17% {transform: translate3d(0,0rpx,0) rotate(25deg); } 34% {transform: translate3d(0,-20rpx,0) rotate(-20deg); } 50% {transform: translate3d(0,-10rpx,0) rotate(15deg); } 67% {transform: translate3d(0,10rpx,0) rotate(-25deg); } 84% {transform: translate3d(0,15rpx,0) rotate(15deg); } 100% {transform: translate3d(0,20rpx,0) rotate(-15deg); } } .wave { position: absolute; z-index: 3; right: 0; bottom: 0; opacity: 0.725; height: 260rpx; width: 2250rpx; animation: wave 10s linear infinite; } .wave-bg { z-index: 1; animation: wave-bg 10.25s linear infinite; } @keyframes wave{ from {transform: translate3d(125rpx,0,0);} to {transform: translate3d(1125rpx,0,0);} } @keyframes wave-bg{ from {transform: translate3d(375rpx,0,0);} to {transform: translate3d(1375rpx,0,0);} } .bd { position: relative; flex: 1; display: flex; flex-direction: column; align-items: stretch; animation: bd-rise 2s cubic-bezier(0.23,1,0.32,1) .75s forwards; opacity: 0; } @keyframes bd-rise{ from {opacity: 0; transform: translate3d(0,60rpx,0); } to {opacity: 1; transform: translate3d(0,0,0); } } [代码] wave.png(可下载到本地) [图片] 在这个基础上,再加上js的代码,即可实现根据手机倾向,水波晃动的效果 wx.onAccelerometerChange(function callback) 监听加速度数据事件。 [图片] [代码]//index.js Page({ onReady: function () { var _this = this; wx.onAccelerometerChange(function (res) { var angle = -(res.x * 30).toFixed(1); if (angle > 14) { angle = 14; } else if (angle < -14) { angle = -14; } if (_this.data.angle !== angle) { _this.setData({ angle: angle }); } }); }, }); [代码] 四、第四种效果 [图片] [代码]//index.wxml <view class='page__bd'> <view class="bg-img padding-tb-xl" style="background-image:url('http://wx4.sinaimg.cn/mw690/006UdlVNgy1g2v2t1ih8jj31hc0p0qej.jpg');background-size:cover;"> <view class="cu-bar"> <view class="content text-bold text-white"> 悦拍屋 </view> </view> </view> <view class="shadow-blur"> <image src="https://raw.githubusercontent.com/weilanwl/ColorUI/master/demo/images/wave.gif" mode="scaleToFill" class="gif-black response" style="height:100rpx;margin-top:-100rpx;"></image> </view> </view> //index.wxss @import "colorui.wxss"; .gif-black { display: block; border: none; mix-blend-mode: screen; } [代码] 本效果需要引入ColorUI组件库
2019-09-26 - setData 学问多
为什么不能频繁 setData 先科普下 setData 做的事情: 在数据传输时,逻辑层会执行一次 JSON.stringify 来去除掉 setData 数据中不可传输的部分,之后将数据发送给视图层。同时,逻辑层还会将 setData 所设置的数据字段与 data 合并,使开发者可以用 this.data 读取到变更后的数据。 因此频繁调用,视图会一直更新,阻塞用户交互,引发性能问题。 但频繁调用是常见开发场景,能不能频繁调用的同时,视图延迟更新呢? 参考 Vue,我们能知道,Vue 每次赋值操作并不会直接更新视图,而是缓存到一个数据更新队列中,异步更新,再触发渲染,此时多次赋值,也只会渲染一次。 [代码]let newState = null; let timeout = null; const asyncSetData = ({ vm, newData, }) => { newState = { ...newState, ...newData, }; clearTimeout(timeout); timeout = setTimeout(() => { vm.setData({ ...newState, }); newState = null }, 0); }; [代码] 由于异步代码会在同步代码之后执行,因此,当你多次使用 asyncSetData 设置 newState 时,newState 都会被缓存起来,并异步 setData 一次 但同时,这个方案也会带来一个新的问题,同步代码会阻塞页面的渲染。 同步代码会阻塞页面的渲染的问题其实在浏览器中也存在,但在小程序中,由于是逻辑、视图双线程架构,因此逻辑并不会阻塞视图渲染,这是小程序的优点,但在这套方案将会丢失这个优点。 鱼与熊掌不可兼得也! 对于信息流页面,数据过多怎么办 单次设置的数据不能超过 1024kB,请尽量避免一次设置过多的数据 通常,我们拉取到分页的数据 newList,添加到数组里,一般是这么写: [代码]this.setData({ list: this.data.list.concat(newList) }) [代码] 随着分页次数的增加,list 会逐渐增大,当超过 1024 kb 时,程序会报 [代码]exceed max data size[代码] 错误。 为了避免这个问题,我们可以直接修改 list 的某项数据,而不是对整个 list 重新赋值: [代码]let length = this.data.list.length; let newData = newList.reduce((acc, v, i)=>{ acc[`list[${length+i}]`] = v; return acc; }, {}); this.setData(newData); [代码] 这看着似乎还有点繁琐,为了简化操作,我们可以把 list 的数据结构从一维数组改为二维数组:[代码]list = [newList, newList][代码], 每次分页,可以直接将整个 newList 赋值到 list 作为一个子数组,此时赋值方式为: [代码]let length = this.data.list.length; this.setData({ [`list[${length}]`]: newList }); [代码] 同时,模板也需要相应改成二重循环: [代码]<block wx:for="{{list}}" wx:for-item="listItem" wx:key="{{listItem}}"> <child wx:for="{{listItem}}" wx:key="{{item}}"></child> </block> [代码] 下拉加载,让我们一夜回到解放前 信息流产品,总避免不了要做下拉加载。 下拉加载的数据,需要插到 list 的最前面,所以我们应该这样做: [代码]this.setData({ `list[-1]`: newList }) [代码] 哦不,对不起,上面是错的,应该是下面这样: [代码]this.setData({ list: this.data.list.unshift(newList) }); [代码] 这下好,又是一次性修改整个数组,一夜回到解放前… 为了解决这个问题,这里需要一点奇淫巧技: 为下拉加载维护一个单独的二维数组 pullDownList 在渲染时,用 wxs 将 pullDownList reverse 一下 此时,当下拉加载时,便可以只修改数组的某个子项: [代码]let length = this.data.pullDownList.length; this.setData({ [`pullDownList[${length}]`]: newList }); [代码] 关键在于渲染时候的反向渲染: [代码]<wxs module="utils"> function reverseArr(arr) { return arr.reverse() } module.exports = { reverseArr: reverseArr } </wxs> <block wx:for="{{utils.reverseArr(pullDownList)}}" wx:for-item="listItem" wx:key="{{listItem}}"> <child wx:for="{{listItem}}" wx:key="{{item}}"></child> </block> [代码] 问题解决! 参考资料 终极蛇皮上帝视角之微信小程序之告别 setData, 佯真愚, 2018年08月12日
2019-04-11