提交 dd3f9793 编写于 作者: fxy060608's avatar fxy060608

Merge branch 'dev' of https://github.com/dcloudio/uni-app into v3

# Conflicts:
#	packages/uni-h5/dist/index.umd.min.js
#	src/core/view/plugins/index.js
#	src/platforms/app-plus-nvue/service/api/index.js
#	src/platforms/app-plus-nvue/service/api/route/util.js
#	src/platforms/app-plus/service/api/route/navigate-to.js
#	src/platforms/app-plus/service/api/route/switch-tab.js
#	src/platforms/app-plus/service/constants.js
#	src/platforms/app-plus/service/framework/app.js
...@@ -57,7 +57,7 @@ uni.getLocation({ ...@@ -57,7 +57,7 @@ uni.getLocation({
- H5:在较新的手机浏览器上,H5 端获取定位信息,要求部署在 **https** 服务上,本地预览(localhost)仍然可以使用 http 协议。 - H5:在较新的手机浏览器上,H5 端获取定位信息,要求部署在 **https** 服务上,本地预览(localhost)仍然可以使用 http 协议。
- H5:无 GPS 模块的 PC 设备使用 Chrome 浏览器的时候,位置信息是连接谷歌服务器获取的,国内用户可能获取位置信息失败。 - H5:无 GPS 模块的 PC 设备使用 Chrome 浏览器的时候,位置信息是连接谷歌服务器获取的,国内用户可能获取位置信息失败。
- H5:微信公众号可使用微信js sdk,[详见](https://ask.dcloud.net.cn/article/35380) - H5:微信公众号可使用微信js sdk,[详见](https://ask.dcloud.net.cn/article/35380)
- App:Android由于谷歌服务被墙,想在国产手机上正常定位,需要向高德等三方服务商申请SDK资质,获取AppKey。云打包时需要在manifest的SDK配置中填写Appkey。在manifest可视化界面有详细申请指南。离线打包自行在原生工程中配置。 - App:Android由于谷歌服务被墙,想在国产手机上正常定位,需要向高德等三方服务商申请SDK资质,获取AppKey。云打包时需要在manifest的SDK配置中填写Appkey。在manifest可视化界面有详细申请指南。离线打包自行在原生工程中配置。注意包名和appkey信息必须匹配。
- App:``<map>`` 组件默认为国测局坐标gcj02,调用 ``uni.getLocation`` 返回结果传递给 ``<map>`` 组件时,需指定 type 为 gcj02。 - App:``<map>`` 组件默认为国测局坐标gcj02,调用 ``uni.getLocation`` 返回结果传递给 ``<map>`` 组件时,需指定 type 为 gcj02。
- App:持续定位方案:iOS端可以申请持续定位权限,[参考](https://ask.dcloud.net.cn/article/12569)。Android如果进程被杀,代码无法执行。可以使用[unipush](https://ask.dcloud.net.cn/article/35622),通过服务器激活App,执行透传消息,让App启动然后采集位置。Android上,即使自己写原生插件做后台进程,也很容易被杀,unipush是更合适的方案 - App:持续定位方案:iOS端可以申请持续定位权限,[参考](https://ask.dcloud.net.cn/article/12569)。Android如果进程被杀,代码无法执行。可以使用[unipush](https://ask.dcloud.net.cn/article/35622),通过服务器激活App,执行透传消息,让App启动然后采集位置。Android上,即使自己写原生插件做后台进程,也很容易被杀,unipush是更合适的方案
- 小程序:api默认不返回详细地址中文描述。需要中文地址有2种方式:1、使用高德地图小程序sdk,在app和微信上都可以获得中文地址,[参考](http://ask.dcloud.net.cn/article/35070)。2、只考虑app,使用``plus.geolocation``也可以获取中文地址 - 小程序:api默认不返回详细地址中文描述。需要中文地址有2种方式:1、使用高德地图小程序sdk,在app和微信上都可以获得中文地址,[参考](http://ask.dcloud.net.cn/article/35070)。2、只考虑app,使用``plus.geolocation``也可以获取中文地址
......
...@@ -77,6 +77,32 @@ mapContext ...@@ -77,6 +77,32 @@ mapContext
|fail|Function|否|接口调用失败的回调函数| |fail|Function|否|接口调用失败的回调函数|
|complete|Function|否|接口调用结束的回调函数(调用成功、失败都会执行)| |complete|Function|否|接口调用结束的回调函数(调用成功、失败都会执行)|
## mapSearch 模块(module) #### reverseGeocode(Object,callback) > 反向地理编码
##### Object 属性|类型 |默认值|必填|说明 :--|:--|:--|:--|:--| point|Object| |是|{latitude: 纬度, longitude: 经度}
##### callback 返回 Object 参数说明
属性|类型 |说明 :--|:--|:--| type|String|"success" 表示成功, "fail" 表示失败 code|Number| 成功返回 0,失败返回相应 code 码
message|String|失败描述
address|String|查询后地址 (成功时返回)
#### poiSearchNearBy(Object,callback); > 周边检索 ##### Object 属性|类型 |默认值|必填|说明 :--|:--|:--|:--|:--| point|Object| |是|检索的中心点坐标 {latitude: 纬度, longitude: 经度}
key|String| | 是|搜索关键字 radius|Number|3000|否|检索的半径,单位为米 index|Number|1|否|要获取检索结果的页号索引 ##### callback 返回 Object 参数说明
属性|类型 |说明 :--|:--|:--|
type|String|"success" 表示成功, "fail" 表示失败 code|Number| 成功返回 0,失败返回相应 code 码
message|String|失败描述
totalNumber|Number|返回的POI数目
currentNumber|Number|当前页POI数目
pageNumber|Number|页数
pageIndex|Number|当前页号索引
poiList|Array.&lt;poiObject&gt;|POI信息数组
##### poiObject
属性|类型 |说明 :--|:--|:--|
location|Object|{latitude: 纬度, longitude: 经度}
name|String|名称
type|String|类型
distance|Number|距离(单位米)
address|String|地址
**Tips** **Tips**
......
...@@ -188,6 +188,7 @@ uni.share({ ...@@ -188,6 +188,7 @@ uni.share({
```javascript ```javascript
uni.share({ uni.share({
provider: 'weixin', provider: 'weixin',
scene: "WXSceneSession",
type: 5, type: 5,
imageUrl: 'https://img-cdn-qiniu.dcloud.net.cn/uniapp/app/share-logo@3.png', imageUrl: 'https://img-cdn-qiniu.dcloud.net.cn/uniapp/app/share-logo@3.png',
title: '欢迎体验uniapp', title: '欢迎体验uniapp',
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
|method|String|否|GET|有效值详见下方说明|| |method|String|否|GET|有效值详见下方说明||
|dataType|String|否|json |如果设为 json,会尝试对返回的数据做一次 JSON.parse|| |dataType|String|否|json |如果设为 json,会尝试对返回的数据做一次 JSON.parse||
|responseType|String|否|text |设置响应的数据类型。合法值:text、arraybuffer|5+App和支付宝小程序不支持| |responseType|String|否|text |设置响应的数据类型。合法值:text、arraybuffer|5+App和支付宝小程序不支持|
|sslVerify|Boolean|否|true|验证 ssl 证书|仅5+App安卓端支持(HBuilderX 2.3.4+)| |sslVerify|Boolean|否|true|验证 ssl 证书|仅5+App安卓端支持(HBuilderX 2.3.3+)|
|success|Function|否||收到开发者服务成功返回的回调函数|| |success|Function|否||收到开发者服务成功返回的回调函数||
|fail|Function|否||接口调用失败的回调函数|| |fail|Function|否||接口调用失败的回调函数||
|complete|Function|否||接口调用结束的回调函数(调用成功、失败都会执行)|&nbsp;| |complete|Function|否||接口调用结束的回调函数(调用成功、失败都会执行)|&nbsp;|
...@@ -124,5 +124,5 @@ requestTask.abort(); ...@@ -124,5 +124,5 @@ requestTask.abort();
- iOS App第一次安装启动后,会弹出是否允许联网的询问框,在用户点击同意前,调用联网API会失败。请注意判断这种情况。比如官方提供的新闻模板示例(HBuilderX新建项目可选择),会判断如果无法联网,则提供一个错误页,提示用户设置网络及下拉刷新重试。 - iOS App第一次安装启动后,会弹出是否允许联网的询问框,在用户点击同意前,调用联网API会失败。请注意判断这种情况。比如官方提供的新闻模板示例(HBuilderX新建项目可选择),会判断如果无法联网,则提供一个错误页,提示用户设置网络及下拉刷新重试。
- 良好体验的App,还会判断当前是否处于飞行模式([参考](https://ext.dcloud.net.cn/plugin?id=594))、是wifi还是3G([参考](https://uniapp.dcloud.io/api/system/network) - 良好体验的App,还会判断当前是否处于飞行模式([参考](https://ext.dcloud.net.cn/plugin?id=594))、是wifi还是3G([参考](https://uniapp.dcloud.io/api/system/network)
- 部分安卓设备,真机运行或debug模式下的网络,低于release模式很多。 - 部分安卓设备,真机运行或debug模式下的网络,低于release模式很多。
- 安卓端请求某些 https 服务会失败,可以尝试配置 sslVerify 为 false 关闭 ssl 证书验证 - 使用一些比较小众的证书机构(如:CFCA OV OCA)签发的 ssl 证书在安卓设备请求会失败,因为这些机构的根证书不在系统内置根证书库,可以更换其他常见机构签发的证书(如:Let's Encrypt),或者配置 sslVerify 为 false 关闭 ssl 证书验证(不推荐)。
- 单次网络请求数据量建议控制在50K以下(仅指json数据,不含图片),过多数据应分页获取,以提升应用体验。 - 单次网络请求数据量建议控制在50K以下(仅指json数据,不含图片),过多数据应分页获取,以提升应用体验。
...@@ -184,8 +184,17 @@ uni.setTabBarBadge({ ...@@ -184,8 +184,17 @@ uni.setTabBarBadge({
|fail|Function|否|接口调用失败的回调函数| |fail|Function|否|接口调用失败的回调函数|
|complete|Function|否|接口调用结束的回调函数(调用成功、失败都会执行)| |complete|Function|否|接口调用结束的回调函数(调用成功、失败都会执行)|
### uni.onTabBarMidButtonTap(CALLBACK)
监听中间按钮的点击事件
**平台差异说明**
|App|H5|微信小程序|支付宝小程序|百度小程序|头条小程序|QQ小程序|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|√(HBuilderX 2.3.4+)|x|x|x|x|x|x|
**Tip** **Tip**
- tabbar是原生的,层级高于前端元素 - tabbar是原生的,层级高于前端元素
- [uni-app插件市场](https://ext.dcloud.net.cn/search?q=%E5%BA%95%E9%83%A8%E5%9B%BE%E6%A0%87%E8%8F%9C%E5%8D%95)有封装的前端tabbar,但性能不如原生tabbar - [uni-app插件市场](https://ext.dcloud.net.cn/search?q=%E5%BA%95%E9%83%A8%E5%9B%BE%E6%A0%87%E8%8F%9C%E5%8D%95)有封装的前端tabbar,但性能不如原生tabbar
- 如果想要一个中间带+号的tabbar,在HBuilderX中新建uni-app项目、选择 底部选项卡 模板 - HBuilderX 2.3.4 版本以下如果想要一个中间带+号的tabbar,在HBuilderX中新建uni-app项目、选择 底部选项卡 模板
\ No newline at end of file \ No newline at end of file
...@@ -55,6 +55,9 @@ ...@@ -55,6 +55,9 @@
"selectedColor": "#3cc51f", "selectedColor": "#3cc51f",
"borderStyle": "black", "borderStyle": "black",
"backgroundColor": "#ffffff", "backgroundColor": "#ffffff",
"height": "56px",
"fontSize": "12px",
"iconWidth": "24px",
"list": [{ "list": [{
"pagePath": "pages/component/index", "pagePath": "pages/component/index",
"iconPath": "static/image/icon_component.png", "iconPath": "static/image/icon_component.png",
...@@ -65,7 +68,15 @@ ...@@ -65,7 +68,15 @@
"iconPath": "static/image/icon_API.png", "iconPath": "static/image/icon_API.png",
"selectedIconPath": "static/image/icon_API_HL.png", "selectedIconPath": "static/image/icon_API_HL.png",
"text": "接口" "text": "接口"
}] }],
"midButton": {
"width": "80px",
"height": "56px",
"text": "文字",
"iconPath": "static/image/midButton_iconPath.png",
"iconWidth": "24px",
"backgroundImage": "static/image/midButton_backgroundImage.png"
}
} }
} }
``` ```
...@@ -663,6 +674,10 @@ h5 平台下拉刷新动画,只有 circle 类型。 ...@@ -663,6 +674,10 @@ h5 平台下拉刷新动画,只有 circle 类型。
|borderStyle|String|否|black|tabbar 上边框的颜色,仅支持 black/white|| |borderStyle|String|否|black|tabbar 上边框的颜色,仅支持 black/white||
|list|Array|是||tab 的列表,详见 list 属性说明,最少2个、最多5个 tab|| |list|Array|是||tab 的列表,详见 list 属性说明,最少2个、最多5个 tab||
|position|String|否|bottom|可选值 bottom、top|top 值仅微信小程序支持| |position|String|否|bottom|可选值 bottom、top|top 值仅微信小程序支持|
|fontSize|String|否|12px|文字默认大小|App(HBuilderX 2.3.4+)|
|iconWidth|String|否|24px|图标默认宽度(高度等比例缩放)|App(HBuilderX 2.3.4+)|
|height|String|否|56px|tabBar 默认高度|App(HBuilderX 2.3.4+)|
|midButton|Object|否||中间按钮 仅在 list 项为偶数时有效|App(HBuilderX 2.3.4+)|
其中 list 接收一个数组,数组中的每个项都是一个对象,其属性值如下: 其中 list 接收一个数组,数组中的每个项都是一个对象,其属性值如下:
...@@ -673,6 +688,17 @@ h5 平台下拉刷新动画,只有 circle 类型。 ...@@ -673,6 +688,17 @@ h5 平台下拉刷新动画,只有 circle 类型。
|iconPath|String|否|图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,当 postion 为 top 时,此参数无效,不支持网络图片,不支持字体图标| |iconPath|String|否|图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,当 postion 为 top 时,此参数无效,不支持网络图片,不支持字体图标|
|selectedIconPath|String|否|选中时的图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px ,当 postion 为 top 时,此参数无效| |selectedIconPath|String|否|选中时的图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px ,当 postion 为 top 时,此参数无效|
**midButton 属性说明**
|属性|类型|必填|默认值|描述|
|:-|:-|:-|:-|:-|
|width|String|否|80px|中间按钮的宽度,tabBar 其它项为减去此宽度后平分,默认值为与其它项平分宽度|
|height|String|否|56px|中间按钮的高度,可以大于 tabBar 高度|
|text|String|否||中间按钮的文字|
|iconPath|String|否||中间按钮的图片路径|
|iconWidth|String|否|24px||图片宽度(高度等比例缩放)|
|backgroundImage|String|否||中间按钮的背景图片路径|
#### **tabbar常见问题** @tips-tabbar #### **tabbar常见问题** @tips-tabbar
- tabbar 的 js api 见[接口-界面-tabbar](https://uniapp.dcloud.io/api/ui/tabbar),可实现动态显示隐藏(如弹出层无法覆盖tabbar)、内容修改(如国际化)、item加角标等功能。hello uni-app中也有示例。 - tabbar 的 js api 见[接口-界面-tabbar](https://uniapp.dcloud.io/api/ui/tabbar),可实现动态显示隐藏(如弹出层无法覆盖tabbar)、内容修改(如国际化)、item加角标等功能。hello uni-app中也有示例。
- tabbar 的 item 点击事件见[页面生命周期的onTabItemTap](https://uniapp.dcloud.io/frame?id=%E9%A1%B5%E9%9D%A2%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F) - tabbar 的 item 点击事件见[页面生命周期的onTabItemTap](https://uniapp.dcloud.io/frame?id=%E9%A1%B5%E9%9D%A2%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F)
...@@ -680,7 +706,6 @@ h5 平台下拉刷新动画,只有 circle 类型。 ...@@ -680,7 +706,6 @@ h5 平台下拉刷新动画,只有 circle 类型。
- tabbar 在H5端是div模拟的,属于前端屏幕窗口的一部分,如果要使用bottom居底定位方式,应该使用css变量`--window-bottom`,比如悬浮在tabbar上方10px的按钮,样式如下`bottom: calc(var(--window-bottom) + 10px)` - tabbar 在H5端是div模拟的,属于前端屏幕窗口的一部分,如果要使用bottom居底定位方式,应该使用css变量`--window-bottom`,比如悬浮在tabbar上方10px的按钮,样式如下`bottom: calc(var(--window-bottom) + 10px)`
- tabbar 的默认高度,在不同平台不一样。[详见](https://uniapp.dcloud.io/frame?id=%e5%9b%ba%e5%ae%9a%e5%80%bc) - tabbar 的默认高度,在不同平台不一样。[详见](https://uniapp.dcloud.io/frame?id=%e5%9b%ba%e5%ae%9a%e5%80%bc)
- 中间带+号的tabbar模板例子,[参考](https://ext.dcloud.net.cn/plugin?id=98)。可跨端,但+号不凸起。 - 中间带+号的tabbar模板例子,[参考](https://ext.dcloud.net.cn/plugin?id=98)。可跨端,但+号不凸起。
- 如需 tabbar 中间凸起,可自定义tabbar,插件市场有例子,如colorUI等。但注意前端tabbar的性能不如原生tabbar,如果是多页方式,底部tabbar会在切换时闪执行转场动画,如果是单页方式,承载复杂页面内容会有性能问题。[插件市场](https://ext.dcloud.net.cn/search?q=tabbar)搜索tabbar有不少类似例子。
- App端若使用nvue,自定义tabbar,没有性能体验问题。 - App端若使用nvue,自定义tabbar,没有性能体验问题。
- 纯nvue项目(manifest里renderer为native),目前使用pages.json里的tabbar反而影响性能,建议使用前端自己实现单页面的tabbar。后续会解决这个bug。 - 纯nvue项目(manifest里renderer为native),目前使用pages.json里的tabbar反而影响性能,建议使用前端自己实现单页面的tabbar。后续会解决这个bug。
- Android App上弹出键盘顶起tabbar的问题。升级到HBuilderX 2.2后不再存在。 - Android App上弹出键盘顶起tabbar的问题。升级到HBuilderX 2.2后不再存在。
......
...@@ -29,11 +29,11 @@ editor组件目前只有App的vue页面和微信支持,其他端的富文本 ...@@ -29,11 +29,11 @@ editor组件目前只有App的vue页面和微信支持,其他端的富文本
| show-img-size | boolean | false | 否 | 点击图片时显示图片大小控件 | | show-img-size | boolean | false | 否 | 点击图片时显示图片大小控件 |
| show-img-toolbar | boolean | false | 否 | 点击图片时显示工具栏控件 | | show-img-toolbar | boolean | false | 否 | 点击图片时显示工具栏控件 |
| show-img-resize | boolean | false | 否 | 点击图片时显示修改尺寸控件 | | show-img-resize | boolean | false | 否 | 点击图片时显示修改尺寸控件 |
| bindready | eventhandle | | 否 | 编辑器初始化完成时触发 | | @ready | eventhandle | | 否 | 编辑器初始化完成时触发 |
| bindfocus | eventhandle | | 否 | 编辑器聚焦时触发,event.detail = {html, text, delta} | | @focus | eventhandle | | 否 | 编辑器聚焦时触发,event.detail = {html, text, delta} |
| bindblur | eventhandle | | 否 | 编辑器失去焦点时触发,detail = {html, text, delta} | | @blur | eventhandle | | 否 | 编辑器失去焦点时触发,detail = {html, text, delta} |
| bindinput | eventhandle | | 否 | 编辑器内容改变时触发,detail = {html, text, delta} | | @input | eventhandle | | 否 | 编辑器内容改变时触发,detail = {html, text, delta} |
| bindstatuschange | eventhandle | | 否 | 通过 Context 方法改变编辑器内样式时触发,返回选区已设置的样式 | | @statuschange | eventhandle | | 否 | 通过 Context 方法改变编辑器内样式时触发,返回选区已设置的样式 |
编辑器内支持部分 HTML 标签和内连样式,不支持**class****id** 编辑器内支持部分 HTML 标签和内连样式,不支持**class****id**
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
|controls|Array||控件|| |controls|Array||控件||
|include-points|Array||缩放视野以包含所有给定的坐标点|App-nvue 2.1.5+、微信小程序、H5、百度小程序、支付宝小程序| |include-points|Array||缩放视野以包含所有给定的坐标点|App-nvue 2.1.5+、微信小程序、H5、百度小程序、支付宝小程序|
|show-location|Boolean||显示带有方向的当前定位点|微信小程序、H5、百度小程序、支付宝小程序| |show-location|Boolean||显示带有方向的当前定位点|微信小程序、H5、百度小程序、支付宝小程序|
|@markertap|EventHandle||点击标记点时触发|App-nvue暂不支持, App平台需要指定 marker 对象属性 id| |@markertap|EventHandle||点击标记点时触发|App-nvue 2.3.3+, App平台需要指定 marker 对象属性 id|
|@callouttap|EventHandle||点击标记点对应的气泡时触发|| |@callouttap|EventHandle||点击标记点对应的气泡时触发||
|@controltap|EventHandle||点击控件时触发|| |@controltap|EventHandle||点击控件时触发||
|@regionchange|EventHandle||视野发生变化时触发|微信小程序、H5、百度小程序、支付宝小程序| |@regionchange|EventHandle||视野发生变化时触发|微信小程序、H5、百度小程序、支付宝小程序|
......
...@@ -203,6 +203,7 @@ export default { ...@@ -203,6 +203,7 @@ export default {
// #ifdef APP-PLUS // #ifdef APP-PLUS
wv = plus.webview.create("","custom-webview",{ wv = plus.webview.create("","custom-webview",{
plusrequire:"none", //禁止远程网页使用plus的API,有些使用mui制作的网页可能会监听plus.key,造成关闭页面混乱,可以通过这种方式禁止 plusrequire:"none", //禁止远程网页使用plus的API,有些使用mui制作的网页可能会监听plus.key,造成关闭页面混乱,可以通过这种方式禁止
'uni-app': 'none', //不加载uni-app渲染层框架,避免样式冲突
top:uni.getSystemInfoSync().statusBarHeight+44 //放置在titleNView下方。如果还想在webview上方加个地址栏的什么的,可以继续降低TOP值 top:uni.getSystemInfoSync().statusBarHeight+44 //放置在titleNView下方。如果还想在webview上方加个地址栏的什么的,可以继续降低TOP值
}) })
wv.loadURL("https://www.baidu.com") wv.loadURL("https://www.baidu.com")
......
...@@ -732,6 +732,7 @@ const package = require('packageName') ...@@ -732,6 +732,7 @@ const package = require('packageName')
|支付宝小程序|支持支付宝小程序组件|mycomponents| |支付宝小程序|支持支付宝小程序组件|mycomponents|
|百度小程序|支持百度小程序组件|swancomponents| |百度小程序|支持百度小程序组件|swancomponents|
|头条小程序|支持头条小程序组件|ttcomponents| |头条小程序|支持头条小程序组件|ttcomponents|
|QQ小程序|支持QQ小程序组件|wxcomponents|
此文档要求开发者对各端小程序的**自定义组件**有一定了解,没接触过小程序**自定义组件**的可以参考: 此文档要求开发者对各端小程序的**自定义组件**有一定了解,没接触过小程序**自定义组件**的可以参考:
...@@ -739,6 +740,7 @@ const package = require('packageName') ...@@ -739,6 +740,7 @@ const package = require('packageName')
- [百度小程序自定义组件](https://smartprogram.baidu.com/docs/develop/framework/custom-component/) - [百度小程序自定义组件](https://smartprogram.baidu.com/docs/develop/framework/custom-component/)
- [支付宝小程序自定义组件](https://docs.alipay.com/mini/framework/custom-component-overview) - [支付宝小程序自定义组件](https://docs.alipay.com/mini/framework/custom-component-overview)
- [头条小程序自定义组件](https://developer.toutiao.com/docs/framework/custom_component_intro.html) - [头条小程序自定义组件](https://developer.toutiao.com/docs/framework/custom_component_intro.html)
- [QQ小程序自定义组件](https://q.qq.com/wiki/develop/miniprogram/frame/diy_components/)
**目录结构** **目录结构**
...@@ -785,7 +787,7 @@ const package = require('packageName') ...@@ -785,7 +787,7 @@ const package = require('packageName')
"path": "index/index", "path": "index/index",
"style": { "style": {
"usingComponents": { "usingComponents": {
// #ifdef APP-PLUS || MP-WEIXIN // #ifdef APP-PLUS || MP-WEIXIN || MP-QQ
"custom": "/wxcomponents/custom/index" "custom": "/wxcomponents/custom/index"
// #endif // #endif
// #ifdef MP-BAIDU // #ifdef MP-BAIDU
......
#### 运行原理 #### 运行原理
``uni-app`` 在 App 端或小程序端运行时,从架构上分为逻辑层和视图层两个部分。逻辑层负责储存数据和执行业务逻辑,视图层负责页面渲染。 1. 逻辑层和视图层分离,且非H5端通信有折损
``uni-app`` 在非H5端运行时,从架构上分为逻辑层和视图层两个部分。逻辑层负责储存数据和执行业务逻辑,视图层负责页面渲染。
页面加载时,联网和逻辑运算在逻辑层(Android是v8,iOS是jscore),然后会传递数据给视图层渲染。这种通信有损耗。同样,在视图层操作时,比如拖动页面,要实时传递事件给逻辑层接收,也是有损耗的。 页面加载时,联网和逻辑运算在逻辑层(Android是v8,iOS是jscore),然后会传递数据给视图层渲染。这种通信有损耗。同样,在视图层操作时,比如拖动页面,要实时传递事件给逻辑层接收,也是有损耗的。
2. App端渲染引擎可切换
在App端,nvue页面的视图层是由原生引擎渲染的,vue页面的视图层是os的webview渲染的。 在App端,nvue页面的视图层是由原生引擎渲染的,vue页面的视图层是os的webview渲染的。
uni-app的webview渲染经过优化,性能也足够好。它比nvue弱的地方主要在于启动速度和可左右拖动的长列表。
3. app-vue和小程序的数据更新,分页面级和组件级
对于复杂页面,更新某个区域的数据时,需要把这个区域做成组件,这样更新数据时就只更新这个组件,否则会整个页面的数据更新,造成点击延迟卡顿。
这就是自定义组件编译模式的特点。
比如微博长列表页面,点击一个点赞图标,赞数要立即+1,此时这个点赞图标一定要做成组件。否则这个+1会引发页面级所有数据的更新。
app-nvue和h5不存在此问题。造成差异的原因是小程序目前只提供了组件差量更新的机制,不能自动计算所有页面差量。
#### 优化建议 #### 优化建议
...@@ -52,7 +65,7 @@ ...@@ -52,7 +65,7 @@
**优化页面切换动画** **优化页面切换动画**
* 页面初始化时若存在大量图片渲染和大量数据通讯,很有可能造成页面切换卡顿、掉帧。建议延时100ms~300ms渲染图片,分批进行数据通讯,以减少一次性渲染的节点数量。 * 页面初始化时若存在大量图片或原生组件渲染和大量数据通讯,会发生新页面渲染和窗体进入动画抢资源,造成页面切换卡顿、掉帧。建议延时100ms~300ms渲染图片或复杂原生组件,分批进行数据通讯,以减少一次性渲染的节点数量。
* App端动画效果可以自定义。popin/popout的双窗体联动挤压动画效果对资源的消耗更大,如果动画期间页面里在执行耗时的js,可能会造成动画掉帧。此时可以使用消耗资源更小的动画效果,比如slide-in-right/slide-out-right。 * App端动画效果可以自定义。popin/popout的双窗体联动挤压动画效果对资源的消耗更大,如果动画期间页面里在执行耗时的js,可能会造成动画掉帧。此时可以使用消耗资源更小的动画效果,比如slide-in-right/slide-out-right。
**优化样式渲染速度** **优化样式渲染速度**
......
# uni-app 更新日志 # uni-app 更新日志
====================================== ======================================
#### 2.3.3.20190924
* App-Android平台 新增 uni.request 网络请求增加参数 sslVerify,配置是否验证 ssl 证书。但建议使用普遍受信的证书而不是忽略证书校验。[详情](https://uniapp.dcloud.io/api/request/request)
* App-iOS平台 调整 非自定义组件模式 逻辑层默认从 WKWebview 切回 UIWebview,避免 uni.request 无法跨域的问题。但仍建议开发者尽快升级自定义组件模式
* App-iOS平台 新增 非自定义组件模式 iOS13上页面无法滚动的问题,如不能很快升级为自定义组件模式,可临时使用本文的方案2来解决,[详情](https://ask.dcloud.net.cn/article/36410)
* App-iOS平台 修复 nvue swiper组件包含子组件少于3个时布局可能不正确的Bug
* App-iOS平台 修复 nvue video组件退出全屏动画会闪现底层组件的Bug
* H5端 修复 监听页面滚动(onPageScroll)后切换页面报错的Bug [详情](https://ask.dcloud.net.cn/question/78955)
#### 2.3.2.20190921 #### 2.3.2.20190921
* App平台 修复 uni.writeBLECharacteristicValue 无法写入数据的 Bug [详情](https://ask.dcloud.net.cn/question/79204) * App平台 修复 uni.writeBLECharacteristicValue 无法写入数据的 Bug [详情](https://ask.dcloud.net.cn/question/79204)
* App平台 修复 调用 uni.pageScrollTo 时页面内元素 fixed 定位失效的 Bug [详情](https://ask.dcloud.net.cn/question/73179) * App平台 修复 调用 uni.pageScrollTo 时页面内元素 fixed 定位失效的 Bug [详情](https://ask.dcloud.net.cn/question/73179)
......
...@@ -12,5 +12,5 @@ ...@@ -12,5 +12,5 @@
"message": "chore(release): publish %s" "message": "chore(release): publish %s"
} }
}, },
"version": "2.0.0-23020190919001" "version": "2.0.0-23320190923002"
} }
...@@ -115,7 +115,7 @@ const device = [ ...@@ -115,7 +115,7 @@ const device = [
] ]
const keyboard = [ const keyboard = [
'hideKeyboard', 'hideKeyboard',
'onKeyboardHeightChange' 'onKeyboardHeightChange'
] ]
...@@ -138,6 +138,7 @@ const ui = [ ...@@ -138,6 +138,7 @@ const ui = [
'removeTabBarBadge', 'removeTabBarBadge',
'showTabBarRedDot', 'showTabBarRedDot',
'hideTabBarRedDot', 'hideTabBarRedDot',
'onTabBarMidButtonTap',
'setBackgroundColor', 'setBackgroundColor',
'setBackgroundTextStyle', 'setBackgroundTextStyle',
'createAnimation', 'createAnimation',
...@@ -209,4 +210,4 @@ const apis = [ ...@@ -209,4 +210,4 @@ const apis = [
...third ...third
] ]
module.exports = apis module.exports = apis
...@@ -24,7 +24,7 @@ module.exports = function genRequireCode (baseDirname, modules) { ...@@ -24,7 +24,7 @@ module.exports = function genRequireCode (baseDirname, modules) {
const moduleAbsolutePath = Path.resolve(baseDirname, file).replace(/\\/g, '/') const moduleAbsolutePath = Path.resolve(baseDirname, file).replace(/\\/g, '/')
importCode += genImportCode(moduleName, moduleAbsolutePath) importCode += genImportCode(moduleName, moduleAbsolutePath)
moduleProps += genPropsCode(file, moduleName) moduleProps += genPropsCode(file.replace(/\\/g, '/'), moduleName)
}) })
const requireFnCode = (` const requireFnCode = (`
(function() { (function() {
......
{ {
"name": "@dcloudio/uni-app-plus-nvue", "name": "@dcloudio/uni-app-plus-nvue",
"version": "2.0.0-23020190919001", "version": "2.0.0-23320190923002",
"description": "uni-app app-plus-nvue", "description": "uni-app app-plus-nvue",
"main": "dist/index.js", "main": "dist/index.js",
"repository": { "repository": {
...@@ -13,5 +13,5 @@ ...@@ -13,5 +13,5 @@
}, },
"author": "fxy060608", "author": "fxy060608",
"license": "Apache-2.0", "license": "Apache-2.0",
"gitHead": "0fac47b62d100213ce48c29dd9d0a335f8a00264" "gitHead": "10184426b19cb76e01c93fb25c982c72887557e8"
} }
{ {
"name": "@dcloudio/uni-app-plus", "name": "@dcloudio/uni-app-plus",
"version": "2.0.0-23020190919001", "version": "2.0.0-23320190923002",
"description": "uni-app app-plus", "description": "uni-app app-plus",
"main": "dist/index.js", "main": "dist/index.js",
"repository": { "repository": {
...@@ -13,5 +13,5 @@ ...@@ -13,5 +13,5 @@
}, },
"author": "fxy060608", "author": "fxy060608",
"license": "Apache-2.0", "license": "Apache-2.0",
"gitHead": "0fac47b62d100213ce48c29dd9d0a335f8a00264" "gitHead": "10184426b19cb76e01c93fb25c982c72887557e8"
} }
...@@ -71,7 +71,11 @@ function updateComponentJson (name, jsonObj, usingComponents = true) { ...@@ -71,7 +71,11 @@ function updateComponentJson (name, jsonObj, usingComponents = true) {
const oldJsonStr = getJsonFile(name) const oldJsonStr = getJsonFile(name)
if (oldJsonStr) { // update if (oldJsonStr) { // update
if (usingComponents) { // merge usingComponents if (usingComponents) { // merge usingComponents
jsonObj.usingComponents = JSON.parse(oldJsonStr).usingComponents || {} const oldJsonObj = JSON.parse(oldJsonStr)
jsonObj.usingComponents = oldJsonObj.usingComponents || {}
if (oldJsonObj.usingGlobalComponents) { // 复制 global components(针对不支持全局 usingComponents 的平台)
jsonObj.usingGlobalComponents = oldJsonObj.usingGlobalComponents
}
} }
const newJsonStr = JSON.stringify(jsonObj, null, 2) const newJsonStr = JSON.stringify(jsonObj, null, 2)
if (newJsonStr !== oldJsonStr) { if (newJsonStr !== oldJsonStr) {
......
{ {
"name": "@dcloudio/uni-cli-shared", "name": "@dcloudio/uni-cli-shared",
"version": "2.0.0-23020190919001", "version": "2.0.0-23320190923002",
"description": "uni-cli-shared", "description": "uni-cli-shared",
"main": "lib/index.js", "main": "lib/index.js",
"repository": { "repository": {
...@@ -20,5 +20,5 @@ ...@@ -20,5 +20,5 @@
"hash-sum": "^1.0.2", "hash-sum": "^1.0.2",
"strip-json-comments": "^2.0.1" "strip-json-comments": "^2.0.1"
}, },
"gitHead": "0fac47b62d100213ce48c29dd9d0a335f8a00264" "gitHead": "10184426b19cb76e01c93fb25c982c72887557e8"
} }
{ {
"name": "@dcloudio/uni-h5-ui", "name": "@dcloudio/uni-h5-ui",
"version": "2.0.0-23020190919001", "version": "2.0.0-23320190923002",
"description": "uni-app h5 ui", "description": "uni-app h5 ui",
"main": "dist/index.umd.min.js", "main": "dist/index.umd.min.js",
"repository": { "repository": {
...@@ -13,5 +13,5 @@ ...@@ -13,5 +13,5 @@
}, },
"author": "fxy060608", "author": "fxy060608",
"license": "Apache-2.0", "license": "Apache-2.0",
"gitHead": "0fac47b62d100213ce48c29dd9d0a335f8a00264" "gitHead": "10184426b19cb76e01c93fb25c982c72887557e8"
} }
{ {
"name": "@dcloudio/uni-h5", "name": "@dcloudio/uni-h5",
"version": "2.0.0-23020190919001", "version": "2.0.0-23320190923002",
"description": "uni-app h5", "description": "uni-app h5",
"main": "dist/index.umd.min.js", "main": "dist/index.umd.min.js",
"repository": { "repository": {
...@@ -18,5 +18,5 @@ ...@@ -18,5 +18,5 @@
"intersection-observer": "^0.7.0", "intersection-observer": "^0.7.0",
"safe-area-insets": "^1.4.1" "safe-area-insets": "^1.4.1"
}, },
"gitHead": "0fac47b62d100213ce48c29dd9d0a335f8a00264" "gitHead": "10184426b19cb76e01c93fb25c982c72887557e8"
} }
{ {
"name": "@dcloudio/uni-mp-alipay", "name": "@dcloudio/uni-mp-alipay",
"version": "2.0.0-23020190919001", "version": "2.0.0-23320190923002",
"description": "uni-app mp-alipay", "description": "uni-app mp-alipay",
"main": "dist/index.js", "main": "dist/index.js",
"repository": { "repository": {
...@@ -13,5 +13,5 @@ ...@@ -13,5 +13,5 @@
}, },
"author": "fxy060608", "author": "fxy060608",
"license": "Apache-2.0", "license": "Apache-2.0",
"gitHead": "0fac47b62d100213ce48c29dd9d0a335f8a00264" "gitHead": "10184426b19cb76e01c93fb25c982c72887557e8"
} }
{ {
"name": "@dcloudio/uni-mp-baidu", "name": "@dcloudio/uni-mp-baidu",
"version": "2.0.0-23020190919001", "version": "2.0.0-23320190923002",
"description": "uni-app mp-baidu", "description": "uni-app mp-baidu",
"main": "dist/index.js", "main": "dist/index.js",
"repository": { "repository": {
...@@ -13,5 +13,5 @@ ...@@ -13,5 +13,5 @@
}, },
"author": "fxy060608", "author": "fxy060608",
"license": "Apache-2.0", "license": "Apache-2.0",
"gitHead": "0fac47b62d100213ce48c29dd9d0a335f8a00264" "gitHead": "10184426b19cb76e01c93fb25c982c72887557e8"
} }
{ {
"name": "@dcloudio/uni-mp-qq", "name": "@dcloudio/uni-mp-qq",
"version": "2.0.0-23020190919001", "version": "2.0.0-23320190923002",
"description": "uni-app mp-qq", "description": "uni-app mp-qq",
"main": "dist/index.js", "main": "dist/index.js",
"repository": { "repository": {
...@@ -13,5 +13,5 @@ ...@@ -13,5 +13,5 @@
}, },
"author": "fxy060608", "author": "fxy060608",
"license": "Apache-2.0", "license": "Apache-2.0",
"gitHead": "0fac47b62d100213ce48c29dd9d0a335f8a00264" "gitHead": "10184426b19cb76e01c93fb25c982c72887557e8"
} }
{ {
"name": "@dcloudio/uni-mp-toutiao", "name": "@dcloudio/uni-mp-toutiao",
"version": "2.0.0-23020190919001", "version": "2.0.0-23320190923002",
"description": "uni-app mp-toutiao", "description": "uni-app mp-toutiao",
"main": "dist/index.js", "main": "dist/index.js",
"repository": { "repository": {
...@@ -13,5 +13,5 @@ ...@@ -13,5 +13,5 @@
}, },
"author": "fxy060608", "author": "fxy060608",
"license": "Apache-2.0", "license": "Apache-2.0",
"gitHead": "0fac47b62d100213ce48c29dd9d0a335f8a00264" "gitHead": "10184426b19cb76e01c93fb25c982c72887557e8"
} }
{ {
"name": "@dcloudio/uni-mp-weixin", "name": "@dcloudio/uni-mp-weixin",
"version": "2.0.0-23020190919001", "version": "2.0.0-23320190923002",
"description": "uni-app mp-weixin", "description": "uni-app mp-weixin",
"main": "dist/index.js", "main": "dist/index.js",
"repository": { "repository": {
...@@ -13,5 +13,5 @@ ...@@ -13,5 +13,5 @@
}, },
"author": "fxy060608", "author": "fxy060608",
"license": "Apache-2.0", "license": "Apache-2.0",
"gitHead": "0fac47b62d100213ce48c29dd9d0a335f8a00264" "gitHead": "10184426b19cb76e01c93fb25c982c72887557e8"
} }
{ {
"name": "@dcloudio/uni-stat", "name": "@dcloudio/uni-stat",
"version": "2.0.0-23020190919001", "version": "2.0.0-23320190923002",
"description": "", "description": "",
"main": "dist/index.js", "main": "dist/index.js",
"repository": { "repository": {
...@@ -34,5 +34,5 @@ ...@@ -34,5 +34,5 @@
"rollup-plugin-replace": "^2.2.0", "rollup-plugin-replace": "^2.2.0",
"rollup-plugin-uglify": "^6.0.2" "rollup-plugin-uglify": "^6.0.2"
}, },
"gitHead": "0fac47b62d100213ce48c29dd9d0a335f8a00264" "gitHead": "10184426b19cb76e01c93fb25c982c72887557e8"
} }
{ {
"name": "@dcloudio/uni-template-compiler", "name": "@dcloudio/uni-template-compiler",
"version": "2.0.0-23020190919001", "version": "2.0.0-23320190923002",
"description": "uni-template-compiler", "description": "uni-template-compiler",
"main": "lib/index.js", "main": "lib/index.js",
"repository": { "repository": {
...@@ -22,5 +22,5 @@ ...@@ -22,5 +22,5 @@
"@babel/types": "^7.3.3", "@babel/types": "^7.3.3",
"vue-template-compiler": "^2.6.10" "vue-template-compiler": "^2.6.10"
}, },
"gitHead": "0fac47b62d100213ce48c29dd9d0a335f8a00264" "gitHead": "10184426b19cb76e01c93fb25c982c72887557e8"
} }
{ {
"name": "@dcloudio/vue-cli-plugin-hbuilderx", "name": "@dcloudio/vue-cli-plugin-hbuilderx",
"version": "2.0.0-23020190919001", "version": "2.0.0-23320190923002",
"description": "HBuilderX plugin for vue-cli 3", "description": "HBuilderX plugin for vue-cli 3",
"main": "index.js", "main": "index.js",
"repository": { "repository": {
...@@ -18,5 +18,5 @@ ...@@ -18,5 +18,5 @@
"css": "~2.2.1", "css": "~2.2.1",
"escodegen": "^1.8.1" "escodegen": "^1.8.1"
}, },
"gitHead": "0fac47b62d100213ce48c29dd9d0a335f8a00264" "gitHead": "10184426b19cb76e01c93fb25c982c72887557e8"
} }
{ {
"name": "@dcloudio/vue-cli-plugin-uni-optimize", "name": "@dcloudio/vue-cli-plugin-uni-optimize",
"version": "2.0.0-23020190919001", "version": "2.0.0-23320190923002",
"description": "uni-app optimize plugin for vue-cli 3", "description": "uni-app optimize plugin for vue-cli 3",
"main": "index.js", "main": "index.js",
"repository": { "repository": {
...@@ -13,5 +13,5 @@ ...@@ -13,5 +13,5 @@
}, },
"author": "fxy060608", "author": "fxy060608",
"license": "Apache-2.0", "license": "Apache-2.0",
"gitHead": "0fac47b62d100213ce48c29dd9d0a335f8a00264" "gitHead": "10184426b19cb76e01c93fb25c982c72887557e8"
} }
{ {
"name": "@dcloudio/vue-cli-plugin-uni", "name": "@dcloudio/vue-cli-plugin-uni",
"version": "2.0.0-23020190919001", "version": "2.0.0-23320190923002",
"description": "uni-app plugin for vue-cli 3", "description": "uni-app plugin for vue-cli 3",
"main": "index.js", "main": "index.js",
"repository": { "repository": {
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
"author": "fxy060608", "author": "fxy060608",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@dcloudio/uni-stat": "^2.0.0-23020190919001", "@dcloudio/uni-stat": "^2.0.0-23320190923002",
"copy-webpack-plugin": "^4.6.0", "copy-webpack-plugin": "^4.6.0",
"cross-env": "^5.2.0", "cross-env": "^5.2.0",
"envinfo": "^6.0.1", "envinfo": "^6.0.1",
...@@ -34,5 +34,5 @@ ...@@ -34,5 +34,5 @@
"wrap-loader": "^0.2.0", "wrap-loader": "^0.2.0",
"xregexp": "4.0.0" "xregexp": "4.0.0"
}, },
"gitHead": "0fac47b62d100213ce48c29dd9d0a335f8a00264" "gitHead": "10184426b19cb76e01c93fb25c982c72887557e8"
} }
{ {
"name": "@dcloudio/webpack-uni-mp-loader", "name": "@dcloudio/webpack-uni-mp-loader",
"version": "2.0.0-23020190919001", "version": "2.0.0-23320190923002",
"description": "webpack-uni-mp-loader", "description": "webpack-uni-mp-loader",
"main": "index.js", "main": "index.js",
"repository": { "repository": {
...@@ -16,5 +16,5 @@ ...@@ -16,5 +16,5 @@
}, },
"author": "fxy060608", "author": "fxy060608",
"license": "Apache-2.0", "license": "Apache-2.0",
"gitHead": "0fac47b62d100213ce48c29dd9d0a335f8a00264" "gitHead": "10184426b19cb76e01c93fb25c982c72887557e8"
} }
...@@ -67,6 +67,8 @@ module.exports = function(pagesJson, userManifestJson) { ...@@ -67,6 +67,8 @@ module.exports = function(pagesJson, userManifestJson) {
navigationBarTextStyle = 'white', navigationBarTextStyle = 'white',
navigationBarBackgroundColor = '#000000' navigationBarBackgroundColor = '#000000'
} = appJson['window'] || {} } = appJson['window'] || {}
const TABBAR_HEIGHT = 50
let manifestJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, './manifest.json'), 'utf8')) let manifestJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, './manifest.json'), 'utf8'))
...@@ -302,6 +304,30 @@ module.exports = function(pagesJson, userManifestJson) { ...@@ -302,6 +304,30 @@ module.exports = function(pagesJson, userManifestJson) {
manifestJson.plus.popGesture = 'close' manifestJson.plus.popGesture = 'close'
} }
// 检查原生混淆选项
const confusion = manifestJson.plus.confusion
if (confusion && confusion.resources) {
const resources = {}
for (const key in confusion.resources) {
if (!/\.nvue$/.test(key)) {
throw new Error(`原生混淆仅支持 nvue 页面,错误的页面路径:${key}`)
} else {
resources[key.replace(/\.nvue$/, '.js')] = confusion.resources[key]
}
if (!Object.keys(appJson.nvue.pages).find(path => {
const subNVues = appJson.nvue.pages[path].window.subNVues || []
return path.replace(/\.html$/, '.nvue') === key || subNVues.find(({ path }) => path === key.replace(/\.nvue$/, ''))
}) && !pagesJson.pages.find(({ style = {} }) => {
style = Object.assign(style, style['app-plus'])
const subNVues = style.subNVues || []
return subNVues.find(({ path }) => path === key.replace(/\.nvue$/, ''))
})) {
throw new Error(`原生混淆页面未在项目内使用,错误的页面路径:${key}`)
}
}
confusion.resources = resources
}
// uni-app // uni-app
const uniApp = require('../../../package.json')['uni-app'] const uniApp = require('../../../package.json')['uni-app']
manifestJson.plus['uni-app'] = uniApp manifestJson.plus['uni-app'] = uniApp
...@@ -334,6 +360,13 @@ module.exports = function(pagesJson, userManifestJson) { ...@@ -334,6 +360,13 @@ module.exports = function(pagesJson, userManifestJson) {
if (conditionPagePath && isNVueEntryPage) { if (conditionPagePath && isNVueEntryPage) {
isNVueEntryPage = appJson.nvue.entryPagePath === conditionPagePath isNVueEntryPage = appJson.nvue.entryPagePath === conditionPagePath
} }
manifestJson.plus.useragent.value = 'uni-app'
manifestJson.launch_path = '__uniappview.html'
Object.assign(manifestJson.plus.launchwebview, {
id: '1',
kernel: 'WKWebview',
'uni-app': 'auto'
})
if (process.env.UNI_USING_NATIVE) { if (process.env.UNI_USING_NATIVE) {
appJson.entryPagePath = appJson.nvue.entryPagePath appJson.entryPagePath = appJson.nvue.entryPagePath
// networkTimeout // networkTimeout
...@@ -349,50 +382,33 @@ module.exports = function(pagesJson, userManifestJson) { ...@@ -349,50 +382,33 @@ module.exports = function(pagesJson, userManifestJson) {
}) })
delete appJson.nvue delete appJson.nvue
// 纯 nvue 带 tab delete manifestJson.plus.launchwebview.kernel
if (pagesJson.tabBar && pagesJson.tabBar.list && pagesJson.tabBar.list.length) { manifestJson.launch_path = ''
manifestJson.launch_path = '__uniapptabbar.html' Object.assign(manifestJson.plus.launchwebview, {
manifestJson.plus.secondwebview = { uniNView: {
id: '1', path: appJson.entryPagePath
mode: 'child', }
top: 0, })
bottom: 56, } else if (isNVueEntryPage) {
uniNView: { // 非纯 nvue 项目首页为 nvue 页面
path: appJson.pages[0] manifestJson.plus.launchwebview.id = '2'
} }
// 带 tab
if (pagesJson.tabBar && pagesJson.tabBar.list && pagesJson.tabBar.list.length) {
const tabBar = manifestJson.plus.tabBar = Object.assign({}, pagesJson.tabBar)
tabBar.borderStyle = tabBar.borderStyle === 'white' ? '#ffffff' : '#c6c6c6'
tabBar.height = `${parseFloat(tabBar.height) || TABBAR_HEIGHT}px`
// 非纯 nvue 项目首页为 nvue 页面
if (!process.env.UNI_USING_NATIVE && isNVueEntryPage) {
manifestJson.plus.launchwebview.id = '2'
} else {
// 首页是 tabBar 页面
const item = tabBar.list.find(page => page.pagePath === (process.env.UNI_USING_NATIVE ? appJson.entryPagePath : entryPagePath))
if (item) {
tabBar.child = ['lauchwebview']
tabBar.selected = tabBar.list.indexOf(item)
} }
} else { // 纯 nvue 不带 tab
manifestJson.launch_path = ''
Object.assign(manifestJson.plus.launchwebview, {
id: '1',
uniNView: {
path: appJson.pages[0]
}
})
}
} else if (isNVueEntryPage) { // 临时 tabBar 页面
manifestJson.launch_path = '__uniapptabbar.html'
} else if (pagesJson.tabBar && pagesJson.tabBar.list && pagesJson.tabBar.list.length) {
manifestJson.launch_path = '__uniapptabbar.html'
manifestJson.plus.secondwebview = {
id: '1'
}
manifestJson.plus.secondwebview.launch_path = '__uniappview.html'
if (!manifestJson.plus.secondwebview.kernel) {
manifestJson.plus.secondwebview.kernel = 'WKWebview'
}
// 首页是 tabBar 页面
if (pagesJson.tabBar.list.find(page => page.pagePath === entryPagePath)) {
manifestJson.plus.secondwebview.mode = 'child'
manifestJson.plus.secondwebview.top = 0
manifestJson.plus.secondwebview.bottom = 56
}
} else { // 无 tabbar 的页面,launchWebview 的 id 为1
manifestJson.launch_path = '__uniappview.html'
manifestJson.plus.launchwebview.id = '1'
if (!manifestJson.plus.launchwebview.kernel) {
manifestJson.plus.launchwebview.kernel = 'WKWebview'
} }
} }
} }
...@@ -443,4 +459,4 @@ module.exports = function(pagesJson, userManifestJson) { ...@@ -443,4 +459,4 @@ module.exports = function(pagesJson, userManifestJson) {
return [manifest, parseV3Config(appJson)] return [manifest, parseV3Config(appJson)]
} }
return [app, manifest] return [app, manifest]
} }
{ {
"name": "@dcloudio/webpack-uni-pages-loader", "name": "@dcloudio/webpack-uni-pages-loader",
"version": "2.0.0-23020190919001", "version": "2.0.0-23320190923002",
"description": "uni-app pages.json loader", "description": "uni-app pages.json loader",
"main": "lib/index.js", "main": "lib/index.js",
"repository": { "repository": {
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
"strip-json-comments": "^2.0.1" "strip-json-comments": "^2.0.1"
}, },
"uni-app": { "uni-app": {
"compilerVersion": "2.3.0" "compilerVersion": "2.3.3"
}, },
"gitHead": "0fac47b62d100213ce48c29dd9d0a335f8a00264" "gitHead": "10184426b19cb76e01c93fb25c982c72887557e8"
} }
...@@ -158,9 +158,7 @@ if (pixelRatio !== 1) { ...@@ -158,9 +158,7 @@ if (pixelRatio !== 1) {
} }
export function wrapper (canvas) { export function wrapper (canvas) {
canvas.style.height = canvas.height + 'px' canvas.width = canvas.offsetWidth * pixelRatio
canvas.style.width = canvas.width + 'px' canvas.height = canvas.offsetHeight * pixelRatio
canvas.width *= pixelRatio
canvas.height *= pixelRatio
canvas.getContext('2d').__hidpi__ = true canvas.getContext('2d').__hidpi__ = true
} }
import { import {
isFn isFn
} from 'uni-shared' } from 'uni-shared'
const todoApis = Object.create(null) const todoApis = Object.create(null)
const TODOS = [ const TODOS = [
'onTabBarMidButtonTap',
'subscribePush', 'subscribePush',
'unsubscribePush', 'unsubscribePush',
'onPush', 'onPush',
...@@ -29,4 +30,4 @@ TODOS.forEach(function (name) { ...@@ -29,4 +30,4 @@ TODOS.forEach(function (name) {
todoApis[name] = createTodoApi(name) todoApis[name] = createTodoApi(name)
}) })
export default todoApis export default todoApis
import { import {
onMethod,
invokeMethod invokeMethod
} from '../../platform' } from '../../platform'
import {
invoke
} from 'uni-core/service/bridge'
export function removeTabBarBadge ({ export function removeTabBarBadge ({
index index
}) { }) {
...@@ -20,4 +25,16 @@ export function showTabBarRedDot ({ ...@@ -20,4 +25,16 @@ export function showTabBarRedDot ({
}) })
} }
export const hideTabBarRedDot = removeTabBarBadge export const hideTabBarRedDot = removeTabBarBadge
const callbacks = []
onMethod('onTabBarMidButtonTap', res => {
callbacks.forEach(callbackId => {
invoke(callbackId, res)
})
})
export function onTabBarMidButtonTap (callbackId) {
callbacks.push(callbackId)
}
import {
hasOwn
} from 'uni-shared'
export default function createPage (pageVm) { export default function createPage (pageVm) {
const $route = pageVm.$route const $route = pageVm.$route
pageVm.route = $route.meta.pagePath pageVm.route = $route.meta.pagePath
const id = hasOwn($route.params, '__id__') ? $route.params.__id__ : $route.meta.id
pageVm.__page__ = { pageVm.__page__ = {
id: $route.params.__id__, id,
path: $route.path, path: $route.path,
route: $route.meta.pagePath, route: $route.meta.pagePath,
meta: Object.assign({}, $route.meta) meta: Object.assign({}, $route.meta)
} }
// 兼容 mpvue // 兼容 mpvue
pageVm.$vm = pageVm pageVm.$vm = pageVm
pageVm.$root = pageVm pageVm.$root = pageVm
pageVm.$holder = pageVm.$parent.$parent pageVm.$holder = pageVm.$parent.$parent
// 补充 mp 相关属性 // 补充 mp 相关属性
pageVm.$mp = { pageVm.$mp = {
...@@ -19,4 +25,4 @@ export default function createPage (pageVm) { ...@@ -19,4 +25,4 @@ export default function createPage (pageVm) {
// 暂不支持 // 暂不支持
status: '' status: ''
} }
} }
...@@ -21,6 +21,7 @@ import { ...@@ -21,6 +21,7 @@ import {
} from 'uni-mixins' } from 'uni-mixins'
import { import {
pixelRatio,
wrapper wrapper
} from 'uni-helpers/hidpi' } from 'uni-helpers/hidpi'
...@@ -106,16 +107,8 @@ export default { ...@@ -106,16 +107,8 @@ export default {
method(data) method(data)
} }
}, },
_resize ({ _resize () {
width, wrapper(this.$refs.canvas)
height
}) {
var canvas = this.$refs.canvas
if (canvas.style.width !== (width + 'px') || canvas.style.height !== (height + 'px')) {
canvas.width = width
canvas.height = height
wrapper(canvas)
}
}, },
_touchmove (event) { _touchmove (event) {
event.preventDefault() event.preventDefault()
...@@ -381,22 +374,44 @@ export default { ...@@ -381,22 +374,44 @@ export default {
} }
}, },
getImageData ({ getImageData ({
x, x = 0,
y, y = 0,
width, width,
height, height,
destWidth,
destHeight,
hidpi = true,
callbackId callbackId
}) { }) {
var imgData var imgData
var canvas = this.$refs.canvas var canvas = this.$refs.canvas
if (!width) { if (!width) {
width = canvas.width width = canvas.offsetWidth - x
} }
if (!height) { if (!height) {
height = canvas.height height = canvas.offsetHeight - y
} }
try { try {
imgData = canvas.getContext('2d').getImageData(x, y, width, height) const newCanvas = document.createElement('canvas')
if (!hidpi) {
if (!destWidth && !destHeight) {
destWidth = Math.round(width * pixelRatio)
destHeight = Math.round(height * pixelRatio)
} else if (!destWidth) {
destWidth = Math.round(width / height * destHeight)
} else if (!destHeight) {
destHeight = Math.round(height / width * destWidth)
}
} else {
destWidth = width
destHeight = height
}
newCanvas.width = destWidth
newCanvas.height = destHeight
const context = newCanvas.getContext('2d')
context.__hidpi__ = true
context.drawImageByCanvas(canvas, x, y, width, height, 0, 0, destWidth, destHeight, false)
imgData = context.getImageData(0, 0, destWidth, destHeight)
} catch (error) { } catch (error) {
UniViewJSBridge.publishHandler('onCanvasMethodCallback', { UniViewJSBridge.publishHandler('onCanvasMethodCallback', {
callbackId, callbackId,
...@@ -411,8 +426,8 @@ export default { ...@@ -411,8 +426,8 @@ export default {
data: { data: {
errMsg: 'canvasGetImageData:ok', errMsg: 'canvasGetImageData:ok',
data: [...imgData.data], data: [...imgData.data],
width, width: destWidth,
height height: destHeight
} }
}, this.$page.id) }, this.$page.id)
}, },
...@@ -428,8 +443,12 @@ export default { ...@@ -428,8 +443,12 @@ export default {
if (!height) { if (!height) {
height = Math.round(data.length / 4 / width) height = Math.round(data.length / 4 / width)
} }
this.$refs.canvas.getContext('2d').putImageData(new ImageData(new Uint8ClampedArray(data), width, const canvas = document.createElement('canvas')
height), x, y) canvas.width = width
canvas.height = height
const context = canvas.getContext('2d')
context.putImageData(new ImageData(new Uint8ClampedArray(data), width, height), 0, 0)
this.$refs.canvas.getContext('2d').drawImage(canvas, x, y, width, height)
} catch (error) { } catch (error) {
UniViewJSBridge.publishHandler('onCanvasMethodCallback', { UniViewJSBridge.publishHandler('onCanvasMethodCallback', {
callbackId, callbackId,
......
...@@ -108,7 +108,7 @@ class ComponentDescriptor { ...@@ -108,7 +108,7 @@ class ComponentDescriptor {
const clsIndex = addWxsClsArr.findIndex(oldCls => oldCls === cls) const clsIndex = addWxsClsArr.findIndex(oldCls => oldCls === cls)
if (clsIndex !== -1) { // 在 addWxsClass 中 if (clsIndex !== -1) { // 在 addWxsClass 中
addWxsClsArr.splice(clsIndex, 1) addWxsClsArr.splice(clsIndex, 1)
} }
removeWxsClsArr.push(cls) removeWxsClsArr.push(cls)
}) })
this.$el.__wxsRemoveClass = removeWxsClsArr this.$el.__wxsRemoveClass = removeWxsClsArr
...@@ -147,6 +147,10 @@ class ComponentDescriptor { ...@@ -147,6 +147,10 @@ class ComponentDescriptor {
} }
export function createComponentDescriptor (vm) { export function createComponentDescriptor (vm) {
if (vm && vm.$options.name && vm.$options.name.indexOf('VUni') === 0) {
// 内置组件需要使用父 vm
vm = vm.$parent
}
if (vm && vm.$el) { if (vm && vm.$el) {
if (!vm.$el.__wxsComponentDescriptor) { if (!vm.$el.__wxsComponentDescriptor) {
vm.$el.__wxsComponentDescriptor = new ComponentDescriptor(vm) vm.$el.__wxsComponentDescriptor = new ComponentDescriptor(vm)
......
import {
isFn
} from 'uni-shared'
class NodesRef {
constructor (selectorQuery, component, selector, single) {
this._selectorQuery = selectorQuery
this._component = component
this._selector = selector
this._single = single
}
boundingClientRect (callback) {
this._selectorQuery._push(
this._selector,
this._component,
this._single, {
id: true,
dataset: true,
rect: true,
size: true
},
callback)
return this._selectorQuery
}
fields (fields, callback) {
this._selectorQuery._push(
this._selector,
this._component,
this._single,
fields,
callback
)
return this._selectorQuery
}
scrollOffset (callback) {
this._selectorQuery._push(
this._selector,
this._component,
this._single, {
id: true,
dataset: true,
scrollOffset: true
},
callback
)
return this._selectorQuery
}
}
function processDataset (attr) {
const dataset = {}
Object.keys(attr || {}).forEach(key => {
if (key.indexOf('data') === 0) {
let str = key.replace('data', '')
str = str.charAt(0).toLowerCase() + str.slice(1)
dataset[str] = attr[key]
}
})
return dataset
}
function findAttrs (ids, elm, result) {
let nodes = elm.children
if (!Array.isArray(nodes)) {
return false
}
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i]
if (node.attr) {
let index = ids.indexOf(node.attr.id)
if (index >= 0) {
result[index] = {
id: ids[index],
ref: node.ref,
dataset: processDataset(node.attr)
}
if (ids.length === 1) {
break
}
}
}
if (node.children) {
findAttrs(ids, node, result)
}
}
}
function getSelectors (queue) {
let ids = []
for (let i = 0; i < queue.length; i++) {
const selector = queue[i].selector
if (selector.indexOf('#') === 0) {
ids.push(selector.substring(1))
}
}
return ids
}
function getComponentRectAll (dom, attrs, index, result, callback) {
const attr = attrs[index]
dom.getComponentRect(attr.ref, option => {
option.size.id = attr.id
option.size.dataset = attr.dataset
result.push(option.size)
index += 1
if (index < attrs.length) {
getComponentRectAll(dom, attrs, index, result, callback)
} else {
callback(result)
}
})
}
function requestComponentInfo (dom, component, queue, callback) {
const selectors = getSelectors(queue)
let outAttrs = new Array(selectors.length)
findAttrs(selectors, component.$el, outAttrs)
getComponentRectAll(dom, outAttrs, 0, [], (result) => {
callback(result)
})
}
class SelectorQuery {
constructor (pageId) {
this.pageId = pageId
this._queue = []
this._queueCb = []
}
exec (callback) {
if (!this._component) {
return
}
this._dom = this._component._$weex.requireModule('dom')
requestComponentInfo(this._dom, this._component, this._queue, res => {
const queueCbs = this._queueCb
res.forEach((result, index) => {
const queueCb = queueCbs[index]
if (isFn(queueCb)) {
queueCb.call(this, result)
}
})
isFn(callback) && callback.call(this, res)
})
}
['in'] (component) {
if (!component) {
return console.warn('uni.createSelectorQuery 必须传入当前 vm 对象(this)')
}
this._component = component
return this
}
select (selector) {
return new NodesRef(this, this._component, selector, true)
}
selectAll (selector) {
return new NodesRef(this, this._component, selector, false)
}
selectViewport () {
return new NodesRef(this, 0, '', true)
}
_push (selector, component, single, fields, callback) {
this._queue.push({
component,
selector,
single,
fields
})
this._queueCb.push(callback)
}
}
export function createSelectorQuery () {
return new SelectorQuery()
}
...@@ -13,5 +13,6 @@ export { ...@@ -13,5 +13,6 @@ export {
export * from '../../../service/api/context/map' export * from '../../../service/api/context/map'
export * from '../../../service/api/context/video' export * from '../../../service/api/context/video'
export * from '../../../service/api/context/live-pusher' export * from '../../../service/api/context/live-pusher'
export * from 'uni-core/service/api/base/interceptor' export * from '../../../service/api/ui/create-selector-query'
export * from 'uni-core/service/api/base/interceptor'
...@@ -5,7 +5,6 @@ import { ...@@ -5,7 +5,6 @@ import {
} from '../util' } from '../util'
import { import {
TABBAR_HEIGHT,
TITLEBAR_HEIGHT TITLEBAR_HEIGHT
} from '../../constants' } from '../../constants'
...@@ -50,7 +49,7 @@ export function getSystemInfo () { ...@@ -50,7 +49,7 @@ export function getSystemInfo () {
safeAreaInsets = ios ? plus.navigator.getSafeAreaInsets() : getSafeAreaInsets() safeAreaInsets = ios ? plus.navigator.getSafeAreaInsets() : getSafeAreaInsets()
} }
var windowHeight = Math.min(screenHeight - (titleNView ? (statusBarHeight + TITLEBAR_HEIGHT) var windowHeight = Math.min(screenHeight - (titleNView ? (statusBarHeight + TITLEBAR_HEIGHT)
: 0) - (isTabBarPage() && tabBar.visible ? TABBAR_HEIGHT : 0), screenHeight) : 0) - (isTabBarPage() && tabBar.visible ? tabBar.height : 0), screenHeight)
var windowWidth = screenWidth var windowWidth = screenWidth
var safeArea = { var safeArea = {
left: safeAreaInsets.left, left: safeAreaInsets.left,
......
...@@ -79,7 +79,7 @@ export function navigateBack ({ ...@@ -79,7 +79,7 @@ export function navigateBack ({
animationType, animationType,
animationDuration animationDuration
}) { }) {
const pages = getCurrentPages() const pages = getCurrentPages()
const currentPage = pages[pages.length - 1] const currentPage = pages[pages.length - 1]
if ( if (
...@@ -91,11 +91,15 @@ export function navigateBack ({ ...@@ -91,11 +91,15 @@ export function navigateBack ({
}) })
) { ) {
return return
} }
uni.hideToast() // 后退时,关闭 toast,loading uni.hideToast() // 后退时,关闭 toast,loading
currentPage.$page.meta.isQuit currentPage.$page.meta.isQuit
? quit() ? quit()
: back(delta, animationType, animationDuration) : back(delta, animationType, animationDuration)
}
return {
errMsg: 'navigateBack:ok'
}
}
...@@ -7,7 +7,8 @@ import { ...@@ -7,7 +7,8 @@ import {
} from './util' } from './util'
import { import {
setStatusBarStyle setStatusBarStyle,
invoke
} from '../../bridge' } from '../../bridge'
import { import {
...@@ -18,7 +19,7 @@ import tabBar from '../../../../app-plus/service/framework/tab-bar' ...@@ -18,7 +19,7 @@ import tabBar from '../../../../app-plus/service/framework/tab-bar'
export function reLaunch ({ export function reLaunch ({
url url
}) { }, callbackId) {
const urls = url.split('?') const urls = url.split('?')
const path = urls[0] const path = urls[0]
...@@ -39,7 +40,12 @@ export function reLaunch ({ ...@@ -39,7 +40,12 @@ export function reLaunch ({
openType: 'reLaunch' openType: 'reLaunch'
}), }),
'none', 'none',
0 0,
() => {
invoke(callbackId, {
errMsg: 'reLaunch:ok'
})
}
) )
pages.forEach(page => { pages.forEach(page => {
...@@ -48,4 +54,4 @@ export function reLaunch ({ ...@@ -48,4 +54,4 @@ export function reLaunch ({
}) })
setStatusBarStyle() setStatusBarStyle()
} }
...@@ -7,7 +7,8 @@ import { ...@@ -7,7 +7,8 @@ import {
} from './util' } from './util'
import { import {
setStatusBarStyle setStatusBarStyle,
invoke
} from '../../bridge' } from '../../bridge'
import { import {
...@@ -16,7 +17,7 @@ import { ...@@ -16,7 +17,7 @@ import {
export function redirectTo ({ export function redirectTo ({
url url
}) { }, callbackId) {
const urls = url.split('?') const urls = url.split('?')
const path = urls[0] const path = urls[0]
...@@ -37,8 +38,11 @@ export function redirectTo ({ ...@@ -37,8 +38,11 @@ export function redirectTo ({
0, 0,
() => { () => {
lastPage && lastPage.$getAppWebview().close('none') lastPage && lastPage.$getAppWebview().close('none')
invoke(callbackId, {
errMsg: 'redirectTo:ok'
})
} }
) )
setStatusBarStyle() setStatusBarStyle()
} }
...@@ -47,7 +47,7 @@ export function setTabBarStyle ({ ...@@ -47,7 +47,7 @@ export function setTabBarStyle ({
color, color,
selectedColor, selectedColor,
backgroundColor, backgroundColor,
borderStyle borderStyle: borderStyle === 'white' ? '#ffffff' : '#c6c6c6'
}) })
return { return {
errMsg: 'setTabBarStyle:ok' errMsg: 'setTabBarStyle:ok'
...@@ -80,4 +80,4 @@ export function showTabBar ({ ...@@ -80,4 +80,4 @@ export function showTabBar ({
return { return {
errMsg: 'showTabBar:ok' errMsg: 'showTabBar:ok'
} }
} }
...@@ -12,6 +12,8 @@ import { ...@@ -12,6 +12,8 @@ import {
perf perf
} from './perf' } from './perf'
import tabBar from '../framework/tab-bar'
const pages = [] const pages = []
export function getCurrentPages (returnAll) { export function getCurrentPages (returnAll) {
...@@ -52,6 +54,8 @@ export function registerPage ({ ...@@ -52,6 +54,8 @@ export function registerPage ({
if (openType === 'reLaunch' || pages.length === 0) { if (openType === 'reLaunch' || pages.length === 0) {
// pages.length===0 表示首页触发 redirectTo // pages.length===0 表示首页触发 redirectTo
routeOptions.meta.isQuit = true routeOptions.meta.isQuit = true
} else if (!routeOptions.meta.isTabBar) {
routeOptions.meta.isQuit = false
} }
if (!webview) { if (!webview) {
...@@ -70,8 +74,7 @@ export function registerPage ({ ...@@ -70,8 +74,7 @@ export function registerPage ({
} }
if (routeOptions.meta.isTabBar && webview.id !== '1') { if (routeOptions.meta.isTabBar && webview.id !== '1') {
const launchWebview = plus.webview.getLaunchWebview() tabBar.append(webview)
launchWebview && launchWebview.append(webview)
} }
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
......
import { import {
TABBAR_HEIGHT getRealPath
} from '../constants'
import {
getRealPath,
isTabBarPage
} from '../api/util' } from '../api/util'
import safeArea from './safe-area' import {
publish,
requireNativePlugin
} from '../bridge'
const IMAGE_TOP = 7 const TABBAR_HEIGHT = 56
const IMAGE_WIDTH = 24
const IMAGE_HEIGHT = 24
const TEXT_TOP = 36
const TEXT_SIZE = 12
const TEXT_NOICON_SIZE = 17
const TEXT_HEIGHT = 12
const IMAGE_ID = 'TABITEM_IMAGE_'
const TABBAR_VIEW_ID = 'TABBAR_VIEW_'
let view
let config let config
let winWidth
let itemWidth
let itemLength
let itemImageLeft
let itemRects = []
const itemIcons = []
const itemLayouts = []
const itemDot = []
const itemBadge = []
let itemClickCallback
let selected = 0
/** /**
* tabbar显示状态 * tabbar显示状态
*/ */
let visible = true let visible = true
const init = function () { let tabBar
const list = config.list
itemLength = config.list.length
calcItemLayout(list) // 计算选项卡布局
initBitmaps(list) // 初始化选项卡图标
initView()
}
let initView = function () {
const viewStyles = {
height: TABBAR_HEIGHT
}
if (config.position === 'top') {
viewStyles.top = 0
} else {
viewStyles.bottom = 0
viewStyles.height += safeArea.bottom
}
view = new plus.nativeObj.View(TABBAR_VIEW_ID, viewStyles, getDraws())
view.interceptTouchEvent(true)
view.addEventListener('click', (e) => {
if (!__uniConfig.__ready__) { // 未 ready,不允许点击
return
}
const x = e.clientX
const y = e.clientY
for (let i = 0; i < itemRects.length; i++) {
if (isCross(x, y, itemRects[i])) {
const draws = getSelectedDraws(i)
const drawTab = !!draws.length
itemClickCallback && itemClickCallback(config.list[i], i, drawTab)
if (drawTab) {
setTimeout(() => view.draw(draws))
}
break
}
}
})
plus.globalEvent.addEventListener('orientationchange', () => {
calcItemLayout(config.list)
if (config.position !== 'top') {
let height = TABBAR_HEIGHT + safeArea.bottom
view.setStyle({
height: height
})
if (visible) {
setWebviewPosition(height)
}
}
view.draw(getDraws())
})
if (!visible) {
view.hide()
}
}
let isCross = function (x, y, rect) {
if (x > rect.left && x < (rect.left + rect.width) && y > rect.top && y < (rect.top + rect.height)) {
return true
}
return false
}
let initBitmaps = function (list) {
for (let i = 0; i < list.length; i++) {
const item = list[i]
if (item.iconData) {
const bitmap = new plus.nativeObj.Bitmap(IMAGE_ID + i)
bitmap.loadBase64Data(item.iconData)
const selectedBitmap = new plus.nativeObj.Bitmap(`${IMAGE_ID}SELECTED_${i}`)
selectedBitmap.loadBase64Data(item.selectedIconData)
itemIcons[i] = {
icon: bitmap,
selectedIcon: selectedBitmap
}
} else if (item.iconPath) {
itemIcons[i] = {
icon: item.iconPath,
selectedIcon: item.selectedIconPath
}
} else {
itemIcons[i] = {
icon: false,
selectedIcon: false
}
}
}
}
let getDraws = function () {
const backgroundColor = config.backgroundColor
const borderHeight = Math.max(0.5, 1 / plus.screen.scale) // 高分屏最少0.5
const borderTop = config.position === 'top' ? (TABBAR_HEIGHT - borderHeight) : 0
const borderStyle = config.borderStyle === 'white' ? 'rgba(255,255,255,0.33)' : 'rgba(0,0,0,0.33)'
const draws = [{
id: `${TABBAR_VIEW_ID}BG`, // 背景色
tag: 'rect',
color: backgroundColor,
position: {
top: 0,
left: 0,
width: '100%',
height: TABBAR_HEIGHT + safeArea.bottom
}
}, {
id: `${TABBAR_VIEW_ID}BORDER`,
tag: 'rect',
color: borderStyle,
position: {
top: borderTop,
left: 0,
width: '100%',
height: `${borderHeight}px`
}
}]
for (let i = 0; i < itemLength; i++) {
const item = config.list[i]
if (i === selected) {
drawTabItem(draws, i, item.text, config.selectedColor, itemIcons[i].selectedIcon)
} else {
drawTabItem(draws, i, item.text, config.color, itemIcons[i].icon)
}
}
return draws
}
let getSelectedDraws = function (newSelected) {
if (selected === newSelected) {
return false
}
const draws = []
const lastSelected = selected
selected = newSelected
drawTabItem(draws, lastSelected)
drawTabItem(draws, selected)
return draws
}
/**
* 获取文字宽度(全角为1)
* @param {*} text
*/
function getFontWidth (text) {
// eslint-disable-next-line
return text.length - (text.match(/[\u0000-\u00ff]/g) || []).length / 2
}
let calcItemLayout = function () {
winWidth = plus.screen.resolutionWidth
itemWidth = winWidth / itemLength
itemImageLeft = (itemWidth - IMAGE_WIDTH) / 2
itemRects = []
let dotTop = 0
let dotLeft = 0
for (let i = 0; i < itemLength; i++) {
itemRects.push({
top: 0,
left: i * itemWidth,
width: itemWidth,
height: TABBAR_HEIGHT
})
}
for (let i = 0; i < itemLength; i++) {
const item = config.list[i]
let imgLeft = itemWidth * i + itemImageLeft
if ((item.iconData || item.iconPath) && item.text) { // 图文
itemLayouts[i] = {
text: {
top: TEXT_TOP,
left: `${i * itemWidth}px`,
width: `${itemWidth}px`,
height: TEXT_HEIGHT
},
img: {
top: IMAGE_TOP,
left: `${imgLeft}px`,
width: IMAGE_WIDTH,
height: IMAGE_HEIGHT
}
}
dotTop = IMAGE_TOP
dotLeft = imgLeft + IMAGE_WIDTH
} else if (!(item.iconData || item.iconPath) && item.text) { // 仅文字
let textLeft = i * itemWidth
itemLayouts[i] = {
text: {
top: 0,
left: `${textLeft}px`,
width: `${itemWidth}px`,
height: TABBAR_HEIGHT
}
}
dotTop = (44 - TEXT_NOICON_SIZE) / 2
dotLeft = textLeft + itemWidth * 0.5 + getFontWidth(item.text) * TEXT_NOICON_SIZE * 0.5
} else if ((item.iconData || item.iconPath) && !item.text) { // 仅图片
const diff = 10
let imgTop = (TABBAR_HEIGHT - IMAGE_HEIGHT - diff) / 2
let imgLeft = itemWidth * i + itemImageLeft - diff / 2
itemLayouts[i] = {
img: {
top: `${imgTop}px`,
left: `${imgLeft}px`,
width: IMAGE_WIDTH + diff,
height: IMAGE_HEIGHT + diff
}
}
dotTop = imgTop
dotLeft = imgLeft + IMAGE_WIDTH + diff
}
let height = itemBadge[i] ? 14 : 10
let badge = itemBadge[i] || '0'
let font = getFontWidth(badge) - 0.5
font = font > 1 ? 1 : font
let width = height + font * 12
width = width < height ? height : width
let itemLayout = itemLayouts[i]
if (itemLayout) {
itemLayout.doc = {
top: dotTop,
left: `${dotLeft - width * 0.382}px`,
width: `${width}px`,
height: `${height}px`
}
itemLayout.badge = {
top: dotTop,
left: `${dotLeft - width * 0.382}px`,
width: `${width}px`,
height: `${height}px`
}
}
}
}
let drawTabItem = function (draws, index) {
const layout = itemLayouts[index]
const item = config.list[index]
let color = config.color
let icon = itemIcons[index].icon
let dot = itemDot[index]
let badge = itemBadge[index] || ''
if (index === selected) {
color = config.selectedColor
icon = itemIcons[index].selectedIcon
}
if (icon) {
draws.push({
id: `${TABBAR_VIEW_ID}ITEM_${index}_ICON`,
tag: 'img',
position: layout.img,
src: icon
})
}
if (item.text) {
draws.push({
id: `${TABBAR_VIEW_ID}ITEM_${index}_TEXT`,
tag: 'font',
position: layout.text,
text: item.text,
textStyles: {
size: icon ? TEXT_SIZE : `${TEXT_NOICON_SIZE}px`,
color
}
})
}
const hiddenPosition = {
letf: 0,
top: 0,
width: 0,
height: 0
}
// 绘制小红点(角标背景)
draws.push({
id: `${TABBAR_VIEW_ID}ITEM_${index}_DOT`,
tag: 'rect',
position: (dot || badge) ? layout.doc : hiddenPosition,
rectStyles: {
color: '#ff0000',
radius: badge ? '7px' : '5px'
}
})
// 绘制角标文本
draws.push({
id: `${TABBAR_VIEW_ID}ITEM_${index}_BADGE`,
tag: 'font',
position: badge ? layout.badge : hiddenPosition,
text: badge || ' ',
textStyles: {
align: 'center',
verticalAlign: 'middle',
color: badge ? '#ffffff' : 'rgba(0,0,0,0)',
overflow: 'ellipsis',
size: '10px'
}
})
}
/**
* {
"color": "#7A7E83",
"selectedColor": "#3cc51f",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "page/component/index.html",
"iconData": "",
"selectedIconData": "",
"text": "组件"
}, {
"pagePath": "page/API/index.html",
"iconData": "",
"selectedIconData": "",
"text": "接口"
}],
"position":"bottom"//bottom|top
}
*/
/** /**
* 设置角标 * 设置角标
...@@ -371,112 +25,99 @@ let drawTabItem = function (draws, index) { ...@@ -371,112 +25,99 @@ let drawTabItem = function (draws, index) {
* @param {string} text * @param {string} text
*/ */
function setTabBarBadge (type, index, text) { function setTabBarBadge (type, index, text) {
if (!tabBar) {
return
}
if (type === 'none') { if (type === 'none') {
itemDot[index] = false tabBar.hideTabBarRedDot({
itemBadge[index] = '' index
})
tabBar.removeTabBarBadge({
index
})
} else if (type === 'text') { } else if (type === 'text') {
itemBadge[index] = text tabBar.setTabBarBadge({
index,
text
})
} else if (type === 'redDot') { } else if (type === 'redDot') {
itemDot[index] = true tabBar.showTabBarRedDot({
} index
if (view) { })
calcItemLayout(config.list)
view.draw(getDraws())
} }
} }
/** /**
* 动态设置 tabBar 某一项的内容 * 动态设置 tabBar 某一项的内容
*/ */
function setTabBarItem (index, text, iconPath, selectedIconPath) { function setTabBarItem (index, text, iconPath, selectedIconPath) {
if (iconPath || selectedIconPath) { const item = {}
let itemIcon = itemIcons[index] = itemIcons[index] || {} if (iconPath) {
if (iconPath) { item.iconPath = getRealPath(iconPath)
itemIcon.icon = getRealPath(iconPath)
}
if (selectedIconPath) {
itemIcon.selectedIcon = getRealPath(selectedIconPath)
}
} }
if (text) { if (selectedIconPath) {
config.list[index].text = text item.selectedIconPath = getRealPath(selectedIconPath)
} }
view.draw(getDraws()) tabBar && tabBar.setTabBarItem(Object.assign({
index,
text
}, item))
} }
/** /**
* 动态设置 tabBar 的整体样式 * 动态设置 tabBar 的整体样式
* @param {Object} style 样式 * @param {Object} style 样式
*/ */
function setTabBarStyle (style) { function setTabBarStyle (style) {
for (const key in style) { tabBar && tabBar.setTabBarStyle(style)
config[key] = style[key]
}
view.draw(getDraws())
}
/**
* 设置tab页底部或顶部距离
* @param {*} value 距离
*/
function setWebviewPosition (value) {
const position = config.position === 'top' ? 'top' : 'bottom'
plus.webview.all().forEach(webview => {
if (isTabBarPage(String(webview.__uniapp_route))) {
webview.setStyle({
[position]: value
})
}
})
} }
/** /**
* 隐藏 tabBar * 隐藏 tabBar
* @param {boolean} animation 是否需要动画效果 暂未支持 * @param {boolean} animation 是否需要动画效果 暂未支持
*/ */
function hideTabBar (animation) { function hideTabBar (animation) {
if (visible === false) {
return
}
visible = false visible = false
if (view) { tabBar && tabBar.hideTabBar({
view.hide() animation
setWebviewPosition(0) })
}
} }
/** /**
* 显示 tabBar * 显示 tabBar
* @param {boolean} animation 是否需要动画效果 暂未支持 * @param {boolean} animation 是否需要动画效果 暂未支持
*/ */
function showTabBar (animation) { function showTabBar (animation) {
if (visible === true) {
return
}
visible = true visible = true
if (view) { tabBar && tabBar.showTabBar({
view.show() animation
setWebviewPosition(TABBAR_HEIGHT + safeArea.bottom) })
}
} }
export default { export default {
init (options, clickCallback) { init (options, clickCallback) {
if (options && options.list.length) { if (options && options.list.length) {
selected = options.selected || 0
config = options config = options
config.position = 'bottom' // 暂时强制使用bottom
itemClickCallback = clickCallback
init()
return view
} }
try {
tabBar = requireNativePlugin('uni-tabview')
} catch (error) {
console.log(`uni.requireNativePlugin("uni-tabview") error ${error}`)
}
tabBar && tabBar.onClick(({ index }) => {
clickCallback(config.list[index], index, true)
})
tabBar && tabBar.onMidButtonClick(() => {
publish('onTabBarMidButtonTap', {})
})
}, },
switchTab (page) { switchTab (page) {
const itemLength = config.list.length
if (itemLength) { if (itemLength) {
for (let i = 0; i < itemLength; i++) { for (let i = 0; i < itemLength; i++) {
if ( if (
config.list[i].pagePath === page || config.list[i].pagePath === page ||
config.list[i].pagePath === `${page}.html` config.list[i].pagePath === `${page}.html`
) { ) {
const draws = getSelectedDraws(i) tabBar && tabBar.switchSelect({
if (draws.length) { index: i
view.draw(draws) })
}
return true return true
} }
} }
...@@ -488,7 +129,22 @@ export default { ...@@ -488,7 +129,22 @@ export default {
setTabBarStyle, setTabBarStyle,
hideTabBar, hideTabBar,
showTabBar, showTabBar,
append (webview) {
tabBar && tabBar.append({
id: webview.id
}, ({ code }) => {
if (code !== 0) {
// console.log('tab append error')
setTimeout(() => {
this.append(webview)
}, 20)
}
})
},
get visible () { get visible () {
return visible return visible
},
get height () {
return config && config.height ? parseFloat(config.height) : TABBAR_HEIGHT
} }
} }
...@@ -15,13 +15,32 @@ export function parseTitleNView (routeOptions) { ...@@ -15,13 +15,32 @@ export function parseTitleNView (routeOptions) {
) )
) { ) {
return false return false
}
const titleImage = windowOptions.titleImage || ''
const transparentTitle = windowOptions.transparentTitle || 'none'
const titleNViewTypeList = {
'none': 'default',
'auto': 'transparent',
'always': 'float'
} }
const ret = { const ret = {
autoBackButton: !routeOptions.meta.isQuit, autoBackButton: !routeOptions.meta.isQuit,
backgroundColor: windowOptions.navigationBarBackgroundColor || '#000000', titleText: titleImage === '' ? windowOptions.navigationBarTitleText || '' : '',
titleText: windowOptions.navigationBarTitleText || '', titleColor: windowOptions.navigationBarTextStyle === 'black' ? '#000000' : '#ffffff',
titleColor: windowOptions.navigationBarTextStyle === 'black' ? '#000000' : '#ffffff' type: titleNViewTypeList[transparentTitle],
backgroundColor: transparentTitle !== 'always' ? windowOptions.navigationBarBackgroundColor || '#000000' : 'rgba(0,0,0,0)',
tags: titleImage === '' ? [] : [{
'tag': 'img',
'src': titleImage,
'position': {
'left': 'auto',
'top': 'auto',
'width': 'auto',
'height': '26px'
}
}]
} }
routeOptions.meta.statusBarStyle = windowOptions.navigationBarTextStyle === 'black' ? 'dark' : 'light' routeOptions.meta.statusBarStyle = windowOptions.navigationBarTextStyle === 'black' ? 'dark' : 'light'
......
...@@ -6,10 +6,6 @@ import { ...@@ -6,10 +6,6 @@ import {
parsePullToRefresh parsePullToRefresh
} from './pull-to-refresh-parser' } from './pull-to-refresh-parser'
import {
TABBAR_HEIGHT
} from '../../../constants'
const WEBVIEW_STYLE_BLACKLIST = [ const WEBVIEW_STYLE_BLACKLIST = [
'navigationBarBackgroundColor', 'navigationBarBackgroundColor',
'navigationBarTextStyle', 'navigationBarTextStyle',
...@@ -73,10 +69,5 @@ export function parseWebviewStyle (id, path, routeOptions = {}) { ...@@ -73,10 +69,5 @@ export function parseWebviewStyle (id, path, routeOptions = {}) {
} }
} }
if (routeOptions.meta.isTabBar) {
webviewStyle.top = 0
webviewStyle.bottom = TABBAR_HEIGHT
}
return webviewStyle return webviewStyle
} }
import createCallbacks from 'uni-helpers/callbacks' import createCallbacks from 'uni-helpers/callbacks'
import { pixelRatio } from 'uni-helpers/hidpi'
const canvasEventCallbacks = createCallbacks('canvasEvent') const canvasEventCallbacks = createCallbacks('canvasEvent')
...@@ -808,8 +807,8 @@ export function canvasPutImageData ({ ...@@ -808,8 +807,8 @@ export function canvasPutImageData ({
} }
export function canvasToTempFilePath ({ export function canvasToTempFilePath ({
x, x = 0,
y, y = 0,
width, width,
height, height,
destWidth, destWidth,
...@@ -818,13 +817,7 @@ export function canvasToTempFilePath ({ ...@@ -818,13 +817,7 @@ export function canvasToTempFilePath ({
fileType, fileType,
qualit qualit
}, callbackId) { }, callbackId) {
if (typeof width !== 'undefined') { let pageId
width *= pixelRatio
}
if (typeof height !== 'undefined') {
height *= pixelRatio
}
var pageId
const app = getApp() const app = getApp()
if (app.$route && app.$route.params.__id__) { if (app.$route && app.$route.params.__id__) {
pageId = app.$route.params.__id__ pageId = app.$route.params.__id__
...@@ -834,37 +827,39 @@ export function canvasToTempFilePath ({ ...@@ -834,37 +827,39 @@ export function canvasToTempFilePath ({
}) })
return return
} }
var cId = canvasEventCallbacks.push(function (data) { const cId = canvasEventCallbacks.push(function ({
var imgData = data.data data,
if (!imgData || !imgData.length) { width,
height
}) {
if (!data || !data.length) {
invoke(callbackId, { invoke(callbackId, {
errMsg: 'canvasToTempFilePath:fail' errMsg: 'canvasToTempFilePath:fail'
}) })
return return
} }
let imgData
try { try {
imgData = new ImageData(new Uint8ClampedArray(imgData), data.width, data.height) imgData = new ImageData(new Uint8ClampedArray(data), width, height)
} catch (error) { } catch (error) {
invoke(callbackId, { invoke(callbackId, {
errMsg: 'canvasToTempFilePath:fail' errMsg: 'canvasToTempFilePath:fail'
}) })
return return
} }
var canvas = getTempCanvas() const canvas = getTempCanvas()
canvas.width = data.width canvas.width = width
canvas.height = data.height canvas.height = height
var c2d = canvas.getContext('2d') const c2d = canvas.getContext('2d')
c2d.putImageData(imgData, 0, 0) c2d.putImageData(imgData, 0, 0)
var base64 = canvas.toDataURL('image/png') let base64 = canvas.toDataURL('image/png')
var img = new Image() const img = new Image()
img.onload = function () { img.onload = function () {
var width = canvas.width = typeof destWidth === 'number' ? destWidth : imgData.width * pixelRatio
var height = canvas.height = typeof destHeight === 'number' ? destHeight : imgData.height * pixelRatio
if (fileType === 'jpeg') { if (fileType === 'jpeg') {
c2d.fillStyle = '#fff' c2d.fillStyle = '#fff'
c2d.fillRect(0, 0, width, height) c2d.fillRect(0, 0, width, width)
} }
c2d.drawImage(img, 0, 0, img.width, img.height, 0, 0, width, height) c2d.drawImage(img, 0, 0)
base64 = canvas.toDataURL(`image/${fileType}`, qualit) base64 = canvas.toDataURL(`image/${fileType}`, qualit)
invoke(callbackId, { invoke(callbackId, {
errMsg: 'canvasToTempFilePath:ok', errMsg: 'canvasToTempFilePath:ok',
...@@ -878,6 +873,9 @@ export function canvasToTempFilePath ({ ...@@ -878,6 +873,9 @@ export function canvasToTempFilePath ({
y, y,
width, width,
height, height,
destWidth,
destHeight,
hidpi: false,
callbackId: cId callbackId: cId
}) })
} }
...@@ -7934,6 +7934,10 @@ shebang-regex@^1.0.0: ...@@ -7934,6 +7934,10 @@ shebang-regex@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.npm.taobao.org/shebang-regex/download/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" resolved "https://registry.npm.taobao.org/shebang-regex/download/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
shell-exec@^1.0.2:
version "1.0.2"
resolved "https://registry.npm.taobao.org/shell-exec/download/shell-exec-1.0.2.tgz#2e9361b0fde1d73f476c4b6671fa17785f696756"
shell-quote@^1.6.1: shell-quote@^1.6.1:
version "1.6.1" version "1.6.1"
resolved "https://registry.npm.taobao.org/shell-quote/download/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" resolved "https://registry.npm.taobao.org/shell-quote/download/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册