尤尼奥

高板凳低桌子都是木头


  • 首页

  • 归档

  • 标签

  • 分类

  • 关于

微信小程序新能力:URL Scheme,可从短信跳转小程序

Veröffentlicht am 2021-03-10 | in 小程序

最近小程序上线了一个超级流量的新入口:URL Scheme。通过小程序页面的URL Scheme,可以在短信、邮件或微信外部的网页中打开小程序。

那么如何实现呢?官方文档已经写的很清楚啦,这里简单介绍一下。

首先,获取URL Scheme,通过服务端接口可以获取打开小程序任意页面的URL Scheme,支持生成到期失效和永久有效的URL Scheme。

然后,通过短信群发平台将获取的URL Scheme + 营销文案发送到用户的手机上。

最后,用户收到短信后,直接点击URL Scheme唤起微信,跳转到对应小程序页面,就是这么简单。
除此之外,还可以通过邮件或外部浏览器打开跳转小程序。

由于部分操作系统仍不支持直接识别URL Scheme,因此直接将Scheme发送给用户可能存在无法打开小程序的情况。
为此,我们可以先准备一个H5页面,再从H5页面跳转到URL Scheme实现打开小程序。

1
location.href = 'weixin://dl/business/?ticket= *TICKET*'

H5的示例代码我已经更新到Github,可以复用起来,基于官方的案例做了些改动,增加PC端打开时生成二维码方便手机扫码使用。

这次新能力的更新将使微信小程序不再局限于微信内部的流量,天花板被掀开啦。
而且短信和邮件营销的触达成本非常低,营销成本的压低也会催生出很多新的流量玩法,我们敬请期待吧。

引导添加到我的小程序组件开发

Veröffentlicht am 2021-03-10 | in 小程序

出发点

开发了一个小程序,经过一段时间的观察,发现访问人数和添加到我的小程序的数据差异比较大,我想可能存在2种可能:

  1. 用户不清楚有添加到我的小程序功能
  2. 本身产品做的不够好,不愿意添加

那为了验证可能性,我就做了这个引导添加的提示组件,组件开发非常简单,这里分享给大家。

代码实现

  1. 制作一张gif动图或者静态提示图片也行

  2. wxml布局如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <view wx:if="{{showTip}}" class="tip-wraper">
    <image class="tip-gif"
    mode="widthFix"
    src="./images/add_tip.gif"></image>

    <image bindtap="closeTip"
    class="tip-close"
    src='./images/close.png'></image>
    </view>
  3. js代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    methods: {
    // 初始化关注提示
    initTip() {
    let showTip = wx.getStorageSync('showTip')
    this.setData({
    showTip: typeof showTip=='boolean'?showTip:true
    })
    },
    // 关闭提示
    closeTip() {
    wx.setStorageSync('showTip', false)
    this.setData({
    showTip: false
    })
    }
    },
    lifetimes:{
    attached:function(){
    this.initTip()
    }
    }

结果

上线2天后,发现添加人数相比之前是倍数增长,这也就验证了产品本身其实没有什么大问题,大多数用户是不知道这个功能,或者说用户的行为需要我们去小小的引导一下,就能产生意想不到的收获。

组件源码在minicode-debug项目的/add-tip目录下,可直接拿去复用或者参照修改。

收集整理好用的工具资源库

Veröffentlicht am 2020-10-28 | Edited on 2020-10-29 | in 工具资源

在如今互联网信息泛滥的时代,每天都会被推送很多的垃圾信息,有价值的信息越发的分散且稀缺。老话说得好“好记性不如烂笔头”,很多时候看到好用的工具或资源我便会随手收藏起来,时间一久也就有了一定积累,于是便想分享出来。

建立仓库

寻思很久,最终还是觉得放到github上比较适合,这样也方便大家伙一起集思广益。
自从全职自由开发者之后,不用上下班挤地铁,留给自己的时间也多出许多。目前好用的工具资源库已经收集整理了206条,分类包含:创意、效率、设计、前端、后端、后端、区块链、人工智能、免费教程等。

资源分享

Toonify

一个在线工具,可以将真人头像变成卡通头像。

Drive & Listen

一个很有趣的网站,让你在线体验一边开车一边听广播。使用驾驶员的视角,播放街景,有车速和街道噪音开关,配上当地的广播电台。目前支持几十个城市,包括北京和武汉。

Youka

一个卡拉OK生成器,它可以从 Youtube 下载音乐视频,然后配上歌词,生成卡拉OK视频。

EasyOCR

一个开源的 OCR 软件,支持识别40种语言,包括中日韩文字。

pose-animator

一个浏览器 JS 库,通过摄像头,实时捕捉用户的动作,生成 2D 动画

996.Blockchain
一个区块链项目,专门用于保存加班记录,用户可以把自己的加班记录存到区块链。

webRTC 执行流程演示
这个网页演示和讲解,如何在无后端的情况下,通过 webRTC 建立双方的实时视频通话。

avatarify
一个开源软件,只需要提供一张头像照片,就可以生成人物开口讲话的视频。

如何在抖音上找到漂亮小姐姐?
作者用 Python + ADB 做的 Bot。它会自动打开 APP 对视频截图,然后请求腾讯的人脸识别 API,当颜值大于门限值 BEAUTY_THRESHOLD时,点赞并关注,接着翻到下一页,重复进行该过程用来收集漂亮的小姐姐。

you-get
You-Get 一小小哒命令行程序,提供便利的方式来下载近乎全网的视频。

共同构建

仓库资源来自于我平时阅读各博客大佬、技术分享、微信社群时收集整理出来的,重在平时的积累。
当然,由于个人喜好问题也会有所偏向,所以欢迎各位大佬不吝分享,多给仓库提交Issue 推荐好用的工具/资源。觉得质量还不错,也欢迎给个Star。

好用的工具/资源聚合仓库 https://github.com/YuniorZen/hub

CenterOS安装Puppeteer踩坑记录分享

Veröffentlicht am 2019-12-16 | Edited on 2020-07-26 | in NodeJS

最近闲来无事,决定写个爬虫玩,用request+cheerio的方式已没有新鲜感,那试试谷歌推出的神器Puppeteer。

  • 生成页面的屏幕截图和PDF。
  • 抓取SPA并生成预先呈现的内容(即“SSR”)。
  • 自动表单提交,UI测试,键盘输入等。
  • 创建一个最新的自动化测试环境。使用最新的的JavaScript和浏览器功能,直接在最新版本的Chrome浏览器中运行测试。
  • 捕获您网站的时间线跟踪,以帮助诊断性能问题。

你可以在浏览器中手动完成的大部分事情都可以使用Puppteer完成。但是今天不说Puppeteer怎么入门,来说说我是怎么被它无情折磨的。

安装步骤

全局安装 #方便其余项目使用

1
npm install -g puppeteer

新建test.js,编码实现生成页面截图

1
2
3
4
5
6
7
8
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://baidu.com');
await page.screenshot({path: 'baidu.png'});
await browser.close();
})();

执行生成截图

1
node test.js

然后我们看下,ok,目录下生成一张baidu.png!!!
这是想象的完美流程,这张截图我到现在还没有见到,且听我细细道来。

安装Puppeteer的坑

全局安装puppeteer

1
npm install -g puppeteer

安装这个puppeteer本地很快1分钟的样子,无奈服务器网络问题安装到40分钟时不动了,出于信任我又等了它十分钟!最后还是没有成功,卧槽 无情!

然后切换国内淘宝镜像,118mb,很快1分钟安装完成,

1
2
npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm install -g puppeteer

高高兴兴的执行node test.js

1
2
(node:20042) UnhandledPromiseRejectionWarning: Error: Failed to launch chrome!
xxx/xxx/node_modules/_puppeteer@1.19.0@puppeteer/.local-chromium/linux-674921/chrome-linux/chrome: error while loading shared libraries: libatk-bridge-2.0.so.0: cannot open shared object file: No such file or directory

卧槽 无情! Chromium执行需要安装特定依赖,搜索一番去官方找了下依赖。

执行yum install安装依赖库,拭目以待吧!

1
yum install pango.x86_64 libXcomposite.x86_64 libXcursor.x86_64 libXdamage.x86_64 libXext.x86_64 libXi.x86_64 libXtst.x86_64 cups-libs.x86_64 libXScrnSaver.x86_64 libXrandr.x86_64 GConf2.x86_64 alsa-lib.x86_64 atk.x86_64 gtk3.x86_64 -y
1
No package gtk3.x86_64 available.

卧槽 无情! 我又搜索了很久,各种大佬各种说,复制黏贴一把梭,最后还是无情!
此时已是凌晨2点,算了还是睡觉吧


两天后点一个夜里… 我又没事啦,想起了无情,操起键盘又是一堆检索!

无果,我又运行了下 node test.js

1
libatk-bridge-2.0.so.0: cannot open shared object file: No such file or directory

回到问题本身,我又换回了检索词 libatk-bridge-2.0.so.0 再次在知识的海洋里遨游
又过了许久我翻了到一篇,也是归因安装依赖的问题,只不过依赖是其它应用copy过来,这不正符合no package的我嘛,又是一顿梭!!!

检测缺失的依赖包

1
ldd chrome | grep not
1
2
3
4
5
# 检测结果
libatk-bridge-2.0.so.0 => not found
libgtk-3.so.0 => not found
libgdk-3.so.0 => not found
libGL.so.1 => not found

查找云端哪些软件下面包含对应的依赖包

1
yum provides libatk-bridge-2.0.so.0
1
2
3
4
5
6
7
8
9
10
11
...
firefox-60.1.0-6.el6.centos.i686 : Mozilla Firefox Web browser
Repo : updates
Matched from:
Filename : /usr/lib/firefox/bundled/lib/libatk-bridge-2.0.so.0

#这个就是我们要找的可用的依赖包
firefox-60.1.0-6.el6.centos.x86_64 : Mozilla Firefox Web browser
Repo : installed// 已安装状态
Matched from:
Filename : /usr/lib64/firefox/bundled/lib64/libatk-bridge-2.0.so.0

注意:如果不是已安装状态,要先安装一下对应的软件名

找到对应的依赖包后,将依赖包复制或映射到对应的动态共享库目录中,一般是/usr/lib64/目录,复制或映射可以。

复制包到 /usr/lib64/

1
cp /usr/lib64/firefox/bundled/lib64/libatk-bridge-2.0.so.0  /usr/lib64/

如上,依次安装其它依赖后,再一次满怀期待的执行 node test.js

1
2
3
4
(node:32351) UnhandledPromiseRejectionWarning: Error: Failed to launch chrome!
/usr/local/lib/node_modules/puppeteer/.local-chromium/linux-706915/chrome-linux/chrome: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by /usr/local/lib/node_modules/puppeteer/.local-chromium/linux-706915/chrome-linux/chrome)
/usr/local/lib/node_modules/puppeteer/.local-chromium/linux-706915/chrome-linux/chrome: /lib64/libc.so.6: version `GLIBC_2.15' not found (required by /usr/local/lib/node_modules/puppeteer/.local-chromium/linux-706915/chrome-linux/chrome)
/usr/local/lib/node_modules/puppeteer/.local-chromium/linux-706915/chrome-linux/chrome: /lib64/libc.so.6: version `GLIBC_2.16' not found (required by /usr/local/lib/node_modules/puppeteer/.local-chromium/linux-706915/chrome-linux/chrome)

卧槽 无情! 了解到这是chrome运行需要更高版本libc,涉及底层库升级,有反馈升级不当导致很多系统命令不可用的风险! 吓到睡觉!!!


次日,我又想起了无情,再次检索一番,得知需要更新glibc到2.16,复制黏贴又是一梭。

下载,解压,编译等操作(用 root 全权操作,最后两步用时比较久)

1
2
3
4
5
6
7
8
wget https://ftp.gnu.org/gnu/glibc/glibc-2.16.0.tar.gz
tar -zxvf glibc-2.16.0.tar.gz
cd glibc-2.16.0
mkdir build
cd build
../configure --prefix=/usr --disable-profile --enable-add-ons --with-headers=/usr/include --with-binutils=/usr/bin
make -j 8
make install

查看安装后的glibc有没有更新到2.16

1
strings /lib64/libc.so.6 | grep GLIBC
1
2
3
4
5
...
GLIBC_2.14
GLIBC_2.15
GLIBC_2.16
GLIBC_PRIVATE

更新完毕,最后一次执行node test.js

1
2
symbol lookup error: /usr/lib64/firefox/bundled/lib64/libcairo-gobject.so.2: 
undefined symbol: cairo_region_destroy

卧槽,无情!睡觉去啦!

…许久之后,尝试在执行代码的时候去火狐查找此库解决了这个问题

1
LD_LIBRARY_PATH=/opt/glibc-2.16.0/lib/:/usr/lib64/firefox/bundled/lib64 node test.js

参考文章中的第2点 http://www.8y.work/index2.php/page/2/?thread-187.htm&orderby=asc

但是随之而来的还有其它问题,所以我决定升级我的center os,它太老啦 [😂]

最后

如上分享,希望能帮助到遇到同样场景的你。 对于太老的系统还是建议升级吧,许多依赖更新导致存在一堆的问题。

可视化页面编辑管理系统开发实践分享

Veröffentlicht am 2019-11-30 | Edited on 2019-12-02

背景

之前参与开发一个小程序商城,涉及非常多的落地页面需要前端开发维护,又由于小程序包大小限制,不能完成用小程序代码一一实现,所以最后决定把落地页的需求拆成一个单独的产品-落地页管理系统,通过可视化的界面编辑维护小程序和H5的落地页面,落地页面通过获取后台数据动态生成页面内容。
对于运营同事来说,在没有前端同事配合的情况下,也可通过可视化的操作来搭建所需的落地页面。解决页面生成的效率问题,同时释放开发人员。

落地页管理系统的需求

  • 页面的可维护(新建、编辑、删除、状态管理等)。
  • 页面的常见功能或属性编辑(标题,背景、有效期、微信自定义分享、返回顶部功能等)。
  • 组成页面的元素组件化,可自由拖拽组装,编辑修改组件属性。
  • 页面结构数据化,客户端根据数据动态渲染落地页面。

功能需求的技术实现

技术栈

后端技术栈 node+Koa+mongodb,提供落地页的更删改查API服务、素材的上传更新及有效期定时检测等。
前端技术栈 Vue+Vuex+Vue-route+EmenetUI+Axios,开发实现组成页面所需的基础组件及业务营销组件。
后端只是简单存储记录,本篇主要分享可视化页面编辑和渲染的前端技术实现。

项目目录说明

1
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
80
81
82
83
84
85
86
src							
|-- App.vue
|-- main.js // 项目入口
|-- components // 公共组件目录
| |-- BreadCrumb // 面包屑导航组件
| | |-- breadcrumb.scss
| | |-- index.vue
| |-- Carousel // 轮播图组件
| | |-- carousel.scss
| | |-- index.vue
| |-- CarouselSet // 轮播图属性编辑组件
| | |-- index.vue
| |-- ContextMenu // 右键菜单组件
| | |-- contextmenu.scss
| | |-- index.vue
| |-- Event // 事件组件
| | |-- event.scss
| | |-- index.vue
| |-- EventSet // 事件属性编辑组件
| | |-- index.vue
| |-- Goods // 商品组件
| | |-- goods.scss
| | |-- index.vue
| |-- GoodsSet // 商品属性编辑组件
| | |-- index.vue
| |-- HeadInfo // 头部显示组件
| | |-- headinfo.scss
| | |-- index.vue
| |-- Picture // 图片组件
| | |-- index.vue
| | |-- picture.scss
| |-- PictureSet // 图片属性编辑组件
| | |-- index.vue
| |-- Video // 视频组件
| | |-- index.vue
| | |-- video.scss
| |-- VideoSet // 视频属性编辑组件
| |-- index.vue
|-- pages // 页面目录
| |-- LandPage // 落地页渲染页面
| | |-- index.vue
| | |-- landpage.scss
| |-- Login // 登陆页面
| | |-- index.vue
| | |-- login.scss
| |-- PageCreate // 落地页创建页面
| | |-- index.vue
| | |-- pagecreate.scss
| | |-- components // 创建页面分成左侧组件列表、中间窗口渲染组件、右侧属性编辑组件
| | |-- pageCenter // 中间窗口渲染组件
| | | |-- index.vue
| | | |-- pagecenter.scss
| | |-- pageRight // 右侧属性编辑组件
| | |-- index.vue
| | |-- pageright.scss
| |-- PageEditor // 落地页编辑页面,共享创建页面,根据路由判断实现不同业务需求
| | |-- index.vue
| | |-- pageeditor.scss
| |-- PageList // 落地页列表页面(新建、编辑、删除、状态管理等)
| | |-- index.vue
| | |-- pagelist.scss
| |-- PagePreview // 落地页预览页面
| |-- index.vue
| |-- pagepreview.scss
|-- assets // 资源目录(样式文件)
| |-- style
| |-- animate.css
| |-- common.scss
| |-- normalize.css
| |-- var.scss
|-- http // http请求的接口与方法
| |-- index.js
| |-- request.js
|-- router // 页面路由目录
| |-- index.js
| |-- permission.js
|-- store // vuex状态管理目录
| |-- actions.js
| |-- index.js
| |-- mutations.js
| |-- state.js
|-- utility // 常用工具类函数与静态配置文件目录
|-- const.js
|-- filter.js
|-- index.js
|-- landpage.js

页面元素组件化和数据化

对落地页中的布局和功能需求进行分析总结并抽象出大概元素组件,分为基础组件(图片、视频、轮播图)和营销组件(事件、商品)。
落地页面的数据结构如下:

1
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
page:{
//页面名称
name:'',
//页面标题
title:'',
//页面唯一的key
urlKey:'',
//是否需要有效期
needExpire:false,
expireDate:'',
//额外功能字段
extra:{
//页面背景色
backgroundColor:'',
//是否需要返回顶部按钮
needBackTop:false,
backTopImage:'',
//是否需要自定义分享
needShare:false,
shareText:'',
shareImage:'',
},
//页面中的组件
components:[
{ type:'Picture', name:'图片' }
]
}

落地页的创建页面大致如下,分为左侧元素组件列表、中间实时渲染窗口和右侧组件属性编辑区域。
创建页面布局图片
页面中可以有多个元素组件,然后每个元素组件都有自己的特有属性,每个元素组件对应一个属性组件用来编辑该组件的特有属性,在公共组件目录中可以看到对应的Set组件。

从组件的拖拽到属性编辑与实时渲染

从左侧拖拽组件到中间窗口即为页面中添加一个元素组件,新组件为当前激活组件,右侧区域会根据当前激活组件动态加载对应的属性编辑组件。

当拖拽左侧元素组件时,使用@dragstart事件记录当前拖拽的元素,中间窗口使用@dragover计算正在拖拽元素的位置坐标实现拖拽排序和层级元素的判断,@drop事件中根据之前的记录和判断生成元素组件数据到page.components或元素组件的children层级数组中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- 中间窗口的渲染模版代码 -->
<div class="page-wraper" @dragover="dragover" @drop="drop" :style="style">
<div
v-for="(component) in components"
:key="component.id"
:class="`component-wraper`">
<!-- 动态组件加载 -->
<component :class="`${component.id==activeComponent.id?'active':''} ${relativeComponent&&relativeComponent.id==component.id?('hover-'+relativeType):''}`"
v-bind:is="component.type"
v-bind="component"
@contextmenu.native.prevent="contextmenu($event)"
@mousedown.native="setActiveComponent(component)"
@dragover.native.stop="checkDragOver(component,$event)"></component>
<!-- 动态层级子组件加载 -->
<component v-for="child in component.children"
:key="child.id"
:class="`component-child ${child.id==activeComponent.id?'active':''}`"
v-bind:is="child.type"
v-bind="child"
@contextmenu.native.prevent="contextmenu($event)"
@mousedown.native="setActiveComponent(child)"
@dragover.native.stop="checkDragOver(component, $event, child)"></component>
</div>
</div>

左侧组件到中间窗口组件及右侧属性编辑组件的通信使用vuex的commit触发响应。当编辑右侧属性组件时会触发修改vuex的state,中间窗口组件通过mapState映射当前编辑组件的实时数据,vue会自动响应中间窗口中元素组件的渲染更新。

动态渲染落地页面

请求接口获取当前落地页面数据,判断不同属性值添加不同功能,渲染页面的代码类似之前中间窗口的渲染模版代码,没有对应的事件处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div class="page-wraper" :style="style">
<div class="component-wraper" v-for="(component) in components" :key="component.id">
<!-- 动态组件加载 -->
<component v-bind:is="component.type" v-bind="component"> </component>
<!-- 动态层级子组件加载 -->
<component v-for="child in component.children" class="component-child"
:key="child.id"
v-bind:is="child.type"
v-bind="child"></component>
</div>

<transition name="el-fade-in">
<div class="backTop" v-if="scrollTop&&page&&page.extra.needBackTop" @click="backTop">
<img :src="staticURL+page.extra.backTopImage" />
</div>
</transition>
</div>

vue内置组件component通过v-bind:is动态加载所需的元素组件,并传递组件数据。
元素组件内部获取组件属性并渲染组件,组件会根据环境的不同响应事件,例如:在中间渲染窗口中假如给图片组件添加链接属性,点击并没有响应,但是在落地页面中加载图片组件点击则会响应该事件。

待完善的功能

  • 逐步实现事件组件、商品组件等。
  • 小程序端的渲染实现(数据结构相同,需使用小程序语法实现),目前只实现H5端。

最后

篇幅有限,细节性问题就不在此描述啦,代码已开源,感兴趣的朋友欢迎 猛戳围观地址。
此次分享属个人拙见,如有不足之处欢迎指正,谢谢。

讨论是绝佳的反思,被别人指出问题正是改进的空间。

巧用伪元素(:after/before)放大手机端点击焦点

Veröffentlicht am 2019-11-27 | Edited on 2019-11-30 | in 前端技术

问题场景

我们在使用手机的过程中,是不是遇到过一些特别让你累的交互,就如手机端的按钮或图标老是点击不到,要多点击几次才能命中!

如何解决

手机的屏幕普遍比较小且分辨率高,这就导致设计的时候不能过大且要保持美观,严格按钮设计图尺寸开发后在手机端是会存在这样的问题。
那么怎么解决呢?不按照设计图切图?增加内边距撑大容器?这些方案都有或多或少不合理的地方,我有妙计可骚一把!

解决方案:after/before

通过设置点击元素的:after或:before伪元素大小来放大点击元素的点击区域

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* 放大点击区域功能类 */
.nice-focus{
position:relative;
}
.nice-focus::after{
content:'';
position:absolute;
width:80px;
height:80px;
top:50%;
left:50%;
margin-top:-40px;
margin-left:-40px;
}

/*返回按钮*/
.back{
width:12px;
heigh:24px;
background-image: url(/static/img/icon-back.png);
background-size: 100% auto;
}

1
2
<!-- 页面使用 -->
<div class="back nice-focus">返回</div>

总结

很多看似不能解决问题,换个角度就可以轻松解决!此方法也同样适用于小程序开发。

小程序顶部自定义导航组件实现原理及坑分享

Veröffentlicht am 2019-10-25 | Edited on 2019-11-30

为什么使用自定义导航

对比默认导航栏,我们会更需要:

  • 统一Android、IOS手机对于页面title的展示样式及位置
  • 更丰富的导航栏定制效果,如添加home图标等
  • 左上角返回事件的监听处理
  • 统一实现双击返回顶部功能

自定义导航组件实现思路

自定义导航组件实现的核心是需要计算导航栏的真实高度

这里以官方文档->扩展能力中的Navigation组件为例分析实现思路。当使用”navigationStyle”: “custom”时,默认导航被移除,页面的开始位置变成了屏幕顶部,这时我们需要实现的导航栏是在状态栏下面。

导航栏的真实高度=状态栏高度+导航栏内容。

状态栏+导航栏内容图

使用wx.getSystemInfo获取到statusBarHeight便是导航栏的高度,但是导航栏内容高度呢?

有人可能觉得导航栏内容高度顾名思义就是导航栏内容高度啊,内容撑起还用管嘛!要,必须要!
因为右上角胶囊按钮是原生加载的,我们的导航栏内容需要正好贴在胶囊的下方且垂直居中。

导航栏内容高度=(胶囊按钮的顶部距离 - 状态高度)*2 + 胶囊高度

状态栏+导航栏内容+胶囊图

如何计算胶囊的数据呢?幸运的是我们有 wx.getMenuButtonBoundingClientRect() 获取胶囊按钮的布局位置信息,那么动态计算导航栏的内容高度就很方便啦。
好了,以上就是动态计算的核心思路,我们再来看官方Navigation组件高度是怎么实现的

1
2
3
4
5
6
page{--height:44px;--right:190rpx;}
.weui-navigation-bar .android{--height:48px;--right:222rpx}
.weui-navigation-bar__inner{
position:fixed;top:0;left:0;z-index:5001;display:flex;align-items:center;
height:var(--height);padding-right:var(--right);width:calc(100% - var(--right))
}

导航栏内容的高度是通过- -height这个css变量提前声明好的,安卓机型会重新覆盖为新的css变量值,目前没发现有适配问题。
官方就是官方啊,具体尺寸都知道,那就不用一番计算周折啦,直接拿来主义即可。
导航的布局位置已经搞定啦,剩下就是写具体的内容,不同业务实现需求不同这里就不一一赘述了。

完善官方顶部导航组件

本着拿来主义,直接使用官方Navigation组件,但在实际业务开发中还是遇到不少需要自定义的需求,就比如:

  • loadding样式没实现
  • 标题内容超出没有出现省略号
  • 和原生顶部的样式不兼容,导致单个页面引入时跳转有明显差异出现
  • 没有双击返回顶部功能开关功能
  • 引入页面需要获取导航栏的高度,来控制其他元素距离顶部的位置,
  • 不能根据页面栈数据动态显示隐藏back按钮,

针对以上需求,我们对官方的组件进行二次完善开发,满足常规的自定义需求绰绰有余,直接引入开箱即用。
源码使用示例 https://github.com/YuniorZen/minicode-debug/tree/master/minicode02
顶部导航示例
使用说明

1
2
3
/*自定义头部导航组件,基于官方组件Navigation开发。*/

<navigation-bar title="会员中心" bindgetBarInfo="getBarInfo"></navigation-bar>

组件属性列表

属性 类型 描述
bindgetBarInfo function 组件实例载入页面时触发此事件,首参为event对象,event.detail携带当前导航栏信息,如导航栏高度 event.detail.topBarHeight
bindback function 点击back按钮触发此事件响应函数
backImage string back按钮的图标地址
homeImage string home按钮的图标地址
ext-class string 添加在组件内部结构的class,可用于修改组件内部的样式
title string 导航标题,如果不提供为空
background string 导航背景色,默认#ffffff
color string 导航字体颜色
dbclickBackTop boolean 是否开启双击返回顶部功能,默认true
border boolean 是否显示顶部导航下边框分割线,默认false
loading boolean 是否显示标题左侧的loading,默认false
show boolean 显示隐藏导航,隐藏的时候navigation的高度占位还在,默认true
left boolean 左侧区域是否使用slot内容,默认false
center boolean 中间区域是否使用slot内容,默认false

Slot

name 描述
left 左侧slot,在back按钮位置显示,当left属性为true的时候有效
center 标题slot,在标题位置显示,当center属性为true的时候有效

自定义顶部导航目前存在的坑

  • 弹窗的背景蒙层无法覆盖原生胶囊按钮
  • 页面下拉刷新的圆点会被自定义导航遮盖

如果要自定义顶部导航,以上问题避免不了,只能忍着接受。
目前还没遇到完美的解决方案,针对下拉刷新圆点被遮挡的问题微信官方还在需求开发中,如果你有好的想法欢迎留言反馈,一起学习交流。

小程序取消下拉橡皮筋回弹效果解决方案及坑总结(持续更新)

Veröffentlicht am 2019-09-22 | Edited on 2019-11-30 | in 前端技术

提到ios系统的橡皮筋效果,作为开发者是又爱又恨,有想要这个效果又有不想要的,无奈的是却没有一个简单的开关来设置这个效果是否开启。
最近在开发小程序时也遇到有关于ios橡皮筋回弹的问题,这里分两部分(取消橡皮筋回弹效果和因为这个效果遇到的坑)和大家分享一下。

取消IOS橡皮筋回弹效果的解决方案

1) 页面无滚动区域时,可通过页面json配置文件设置disableScroll:true禁止整个页面滚动,从而取消橡皮筋效果。

1
2
3
{
"disableScroll":true
}

测试代码:https://github.com/YuniorZen/minicode-debug/tree/master/minicode01/pages/demo1

2) 页面有滚动区域,滚动区域通过view模拟实现,然后在页面json配置文件设置disableScroll:true禁止整个页面滚动,从而取消橡皮筋效果。

1
2
3
4
5
6
7
8
9
10
json文件配置
{
"disableScroll":true
}

view元素模拟实现滚动样式
{
height: calc(100vh - 120rpx); //高度必须是固定的值
overflow-y: auto;
}

不足之处在于view元素模拟的滚动区域滚动时不够连贯,没有scroll-view那种原生丝滑般的感觉。
测试代码:https://github.com/YuniorZen/minicode-debug/tree/master/minicode01/pages/demo2

3) 页面有滚动区域,滚动区域使用scroll-view,这时通过disableScroll则无法实现,尝试设置一下scroll-view的scroll-y=”false“,上拉或下拉时居然不再触发橡皮筋的回弹啦,当然滚动区域也不能滚动。
小脑袋动一动,解决方法有啦!
通过设置一个变量scrollY动态控制滚动区域的滚动从而阻止回弹。
监听bindscrolltoupper\bindscrolltolower当scroll-view区域滚动到顶部或底部时候设置scrollY:false来关闭页面滚动,从而阻止回弹。
监听bindtouchstart\bindtouchmove 当用户反方向滑动的时候设置scrollY:true,再次开启页面滚动。

1
2
3
4
5
6
7
8
wxml滚动区域属性和事件处理,具体实现请点击测试代码链接
<scroll-view scroll-y="{{scrollY}}" class="list" upper-threshold="5" lower-threshold="5"
bindscrolltoupper="bindscrolltoupper"
bindscrolltolower="bindscrolltolower"
bindtouchstart="touchstart"
bindtouchmove="touchmove">
<view class="list-item" wx:for="{{list}}" wx:key="{{index}}">{{item}}</view>
</scroll-view>

相对view模拟实现滚动区域,scroll-view滚动更丝滑,不过每次滚动到底部或顶部的时候,再反向滑动时由于再次开启scroll-view滚动会有操作卡顿的感觉,暂时没想到好的解决方法,有解决的大佬希望提供一下想法,一起学习下。
测试代码:https://github.com/YuniorZen/minicode-debug/tree/master/minicode01/pages/demo3

IOS橡皮筋效果遇到的坑

1) 操作左滑删除组件时上下移动,会触发橡皮筋效果导致页面抖动的问题
这个坑的严重程度看设计师的意愿了,反正我们团队目前是需要解决的,方案类似取消橡皮筋解决方案的第三种
在左滑的时候关闭scroll-view的滚动,取消时再次开启滚动

2) 如果页面顶部有置顶的横向滚动区域scroll-view,当页面滚动到底部时继续上拉会导致置顶头部消失,松开回弹后头部又会出现。
坑是社区里的朋友提出来的,我借了个iphone x 一预览,我嚓,还是真是个奇坑!
微信官方回复已复现正在解决中… 不想继续等下去的,暂时解决方法是
监听页面的滚动区域,当滚动到底部时设置顶部横向滚动scroll-view的scroll-x=false来解决。

写在最后

以上便是我在小程序开发中有关于ios橡皮筋回弹效果的分享,示例代码已上传github,可自行下载体验。

https://github.com/YuniorZen/minicode-debug/tree/master/minicode01

目前微信官方虽说已经着手解决(已两年)此类bug,但哪吒说 我命由我不由天,所以还是我们开发者多分享些解决方案自救来的快。
分享方案如有问题还望不吝指出,没有涉及到的坑也欢迎评论提出,一起学习和解决,后续也会基于此篇不断更新总结。

如何在Vue项目中使用压缩字体库

Veröffentlicht am 2018-09-10 | Edited on 2019-11-30 | in 前端技术

如题,在一些项目中经常会碰到在web端使用设计图上的特殊字体,目前已知的解决方案:

  1. 购买字体库服务器,对方会提供个引入脚本置于你的站点中,不过天下没有免费的午餐。
  2. 通过搜集项目需要的字型集,然后生成压缩后的字体库文件用于项目中,目前成熟的是使用字蜘提供插件可以自动生成。

作为一个创业团队显然不愿意花钱付费解决了,但是字蜘的也只能针对于静态的html提取的文字,现在的前端开发环境已经不在像以前单纯操作html文件,比如:vue项目中,我们通常的代码和文字都会写在.vue中,或者js文件中。
那么我想到是解决办法是:

首先获取到.vue、.js文件中字型集,然后使用字体库工具把你需要的文字生成压缩后的字体库文件,最后在项目中引用即可。

第一步:手写一个获取项目中字型集的node代码 getFonts.js

1
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
#源码地址:https://github.com/YuniorZen/js-tool/blob/master/getFonts.js

let fs=require('fs'),
files,
content=[];

let walk=function(dir,exclude) {
let results = [];
let list = fs.readdirSync(dir);
list.forEach(function(file) {
file = dir + '/' + file;
let stat = fs.statSync(file);
if(stat&& stat.isDirectory()){
results = results.concat(walk(file))
}else{
if(/.(vue|html|js)$/.test(file)){ //也可以是其它文件比如json,后缀名添加上即可
results.push(file)
}
}
});
return results
};

((dir)=>{
files=walk(dir);
files.forEach(file=>{
let text=fs.readFileSync(file);
text.toString().split('').forEach(letter=>{
if(/[a-zA-Z0-9\u4e00-\u9fa5]/.test(letter)&&content.indexOf(letter)==-1){
content.push(letter);
}
})
})
console.log(content.join(''));
})('./src');
// ./src 是项目目录地址,可以是绝对地址也可以是相对的,视getFonts.js运行的目录而定

第二步:用获取的字型集生成压缩的字体库文件,这里使用fontmin的win客户端;

fontmin客户端

第三步:把生成的字体库文件放在用到的项目中,@font-face声明字体并使用

1
2
3
4
5
6
7
8
9
@font-face {
font-family: "PingFangSC";
src:url("../fonts/PingFangSC.eot"), /* IE9 */
url("../fonts/PingFangSC.woff") format("woff"), /* chrome、firefox */
url("../fonts/PingFangSC.ttf") format("truetype"), /* chrome、firefox、opera、Safari, Android, iOS 4.2+ */
url("../fonts/PingFangSC.svg#PingFangSC") format("svg"); /* iOS 4.1- */
font-style: normal;
font-weight: normal;
}

最后这个方案仅适用于项目中文字较固定,不会经常变更的,不然每次变更新增字体都要手动跑一边,类似社交、论坛这种UGC项目就不要这样折腾了,还是老老实实的购买字体服务器吧!

一分钟理清 Gas/Gas Price/Gas Limit

Veröffentlicht am 2018-08-18 | Edited on 2019-11-30 | in 区块链

好多朋友第一次接触以太坊的时候,都会搞不清什么是Gas,更搞不清Gas Price和Gas Limit是什么。 本文将逐一介绍并理清这三者之间的关系,相信你看完后就会理解这三个gas相关的概念了。

Gas

Gas对应于一个交易(Transaction)中以太坊虚拟机(EVM)的实际运算步数。 越简单的交易,例如单纯的 以太币转帐交易,需要的运算步数越少, Gas亦会需要的少一点。 反之,如果要计算一些复杂运算,Gas的消耗 量就会大。 所以你提交的交易需要EVM进行的计算量越大,所需的Gas消耗量就越高了。

Gas Price

Gas Price就是你愿意为一个单位的Gas出多少Eth,一般用Gwei作单位。 所以Gas Price 越高, 就表示交易中每运算一步,会支付更多的Eth。

大家可能对Gwei 这个单位感到陌生,Gwei 其实就是10 ^ -9 Eth,也就是说1 Gwei = 0.000000001 Eth。 所以,当你设定Gas price = 20 Gwei ,就意味着你愿意为单步运算支付0.00000002 Eth。

说到这里,聪明如你就会意识到以太坊的手续费计算公式很简单

交易手续费(Tx Fee) = 实际运行步数(Actual Gas Used) * 单步价格(Gas Price)

例如你的交易需要以太坊执行50步完成运算,假设你设定的Gas Price是2 Gwei ,那么整个交易的手续费 就是50 * 2 = 100 Gwei 了。

Gas Limit

Gas Limit就是一次交易中Gas的可用上限,也就是你的交易中最多会执行多少步运算。 由于交易复杂程度各有不同, 确切的Gas消耗量是在完成交易后才会知道,因此在你提交交易之前,需要为交易设定一个Gas用量的上限。

如果说你提交的交易尚未完成,消耗的Gas就已经超过你设定的Gas Limit,那么这次交易就会被取消,而 已经消耗的手续费同样被扣取 —— 因为要奖励已经付出劳动的矿工。 而如果交易已经完成,消耗的Gas未达到Gas Limit, 那么只会按实际消耗的Gas 收取交易服务费。 换句话说,一个交易可能被收取的最高服务费就是Gas Limit * Gas​​ Price 了。


最后值得一提的是Gas Price 越高,你提交的交易会越快被矿工接纳。 但通常人们都不愿多支付手续费, 那么究竟应该将Gas Price设置为多少,才可以在正常时间(eg 10 mins)内,确保交易被确认到区域链上呢?这个网站可以帮到你。 写这篇文章时候,1 Gwei的Gas Price 就可以确保 交易在50 秒左右被接纳。

Yunior

Yunior

尤尼奥(Yunior)的个人博客

10 Artikel
5 Kategorien
29 Tags
GitHub Blog
© 2021 他大舅他二舅都是他舅