需求:
实现一个全局的消息提示,比如在网络状态发生变化时,提示用户网络状态。
其实实现这个功能的方式很多,比如在监听网络状态时把网络的状态存储在store里面,然后再页面里面去监听store里的字段变化。emmm···想了一下实现倒是很就简单,但每个页面都需要去做这个监听或者都需要引入一个组件,还是比较麻烦。然后查看一下H5+的官方文档
效果:
可以点击右侧的关闭按钮或者上滑关闭提示。
实现:
先看代码
首先为了可扩展性,把这个写成了一个类,如下:
javascript
// 这是消息的图标
import img1 from '../static/icons/not-wifi.png'
// 这是关闭按钮的图标
import img2 from '../static/icons/close-icon2.png'
const {
statusBarHeight,
windowWidth
} = uni.getSystemInfoSync();
const viewWidth = uni.upx2px(800)
class NativeMsg {
// 整个区域的宽高
viewStyle = {
backgroundColor: "rgba(255, 255, 255, 0)",
top: "0rpx",
left: `${windowWidth / 2 - viewWidth / 2}`,
width: `${viewWidth}px`,
// 取图片的高度(带阴影的尺寸)
height: `${uni.upx2px(80)}px`
};
constructor(item, cb) {
// 记录内容信息,以供回调使用
this.item = item;
// 弹出、消失动画要用
this.offsetTop = -statusBarHeight - uni.upx2px(159);
// 上边界
this.startTop = -statusBarHeight - uni.upx2px(159);
// 下边界
this.endTop = statusBarHeight;
// 上滑关闭要用
this.clientY = 0;
// nativeObj.View 实例
this.view = null;
// 回调函数
this.cb = cb || null;
// 隐藏过程flag,防止重复执行
this.hiding = false;
// 标记当前弹窗状态
this.status = "active";
this.create();
}
// 创建区域以及背景
create() {
let _view = null;
// 创建 View区域
_view = new plus.nativeObj.View(`alarmMsg-${this.item.alarmId || "ins"}`, this.viewStyle);
// 拦截触摸事件: 开启后 区域内的触摸事件不会透传到下面
_view.interceptTouchEvent(true);
// 设置监听点击事件的区域为关闭图片的区域
_view.setTouchEventRect({
top: `${uni.upx2px(18)}px`,
left: '92%',
width: '22px',
height: '22px'
})
// 增加点击事件监听
_view.addEventListener("click", (e) => {
if (this.hiding) return;
this.hiding = true;
this.cb && this.cb({ type: "click", result: this.item });
this.animationHide();
});
// 触摸事件监听
_view.addEventListener("touchstart", res => {
this.clientY = res.clientY;
});
// 触摸事件监听
_view.addEventListener("touchmove", res => {
const {
clientY
} = res;
let offsetY = this.clientY - clientY;
if (offsetY > 25 && !this.hiding) {
this.hiding = true;
this.cb && this.cb({
type: "move",
result: this.item
});
this.animationHide();
}
});
// 保存
this.view = _view;
// 画内容
this.drawInfo();
// // 显示
// this.animationShow();
}
// 画内容
drawInfo() {
const {
message
} = this.item;
this.view.draw([{
tag: "rect",
id: "rectBox",
position: {
top: '0px',
left: `0px`,
width: `100%`,
height: `100%`
},
rectStyles: {
color: 'rgba(241, 58, 14, .6)',
radius: '8px',
}
},
{
tag: 'img',
id: 'imgBox1',
src: img1,
position: {
top: `${uni.upx2px(22)}px`,
left: '10px',
width: '26px',
height: '22px'
}
},
{
tag: 'img',
id: 'imgBox2',
src: img2,
position: {
top: `${uni.upx2px(18)}px`,
left: '92%',
width: '22px',
height: '22px'
}
},
{
tag: "font",
id: "mainFont",
text: message,
textStyles: {
size: `${uni.upx2px(28)}px`,
color: "#feebeb",
align: "left"
},
position: {
top: `${uni.upx2px(28)}px`,
left: `10%`,
height: "wrap_content"
}
},
]);
}
// 简易向下出现动画
animationShow() {
this.view.show();
this.view.setStyle({
...this.viewStyle,
top: `${this.offsetTop++}px`
});
if (this.offsetTop >= this.endTop) {
this.status = "active";
return;
}
setTimeout(() => {
this.animationShow();
}, 0);
}
// 简易向上消失动画
animationHide() {
this.view.setStyle({
...this.viewStyle,
top: `${this.offsetTop--}px`
});
if (this.offsetTop <= this.startTop) {
this.view.hide();
this.hiding = false;
this.status = "close";
return;
}
setTimeout(() => {
this.animationHide();
}, 0);
}
// 获取当前状态
getStatus() {
return this.status;
}
// 不用动画,直接消失
hide() {
this.view.hide();
}
close() {
this.view.close();
}
}
// 对外暴露一个创建实例的方法
export function createAlarm(item, cb) {
return new NativeMsg(item, cb);
}
方法封装好了,大概解释一下
1. 设置位置
因为这里的效果是类似于element-ui中message提示组件,是从屏幕的中间位置弹出的,所以这里会先去指定位置信息。这里的样式是整个盒子的样式,后面所有的内容都是写在这个盒子里面的。这个位置的属性需要参照H5+ 里面的文档来进行设置,就不过多解释了。
javascript
// 整个区域的宽高
viewStyle = {
backgroundColor: "rgba(255, 255, 255, 0)",
top: "0rpx",
left: `${windowWidth / 2 - viewWidth / 2}`,
width: `${viewWidth}px`,
// 取图片的高度(带阴影的尺寸)
height: `${uni.upx2px(80)}px`
};
2. 创建控件
new plus.nativeObj.View(id, styles, tags)
这个方法就是创建一个原生控件对象的方法,有三个参数:
id
View控件对象的id,这个id按逻辑上来讲是全局唯一的,后面有需要是可以通过 plus.nativeObj.View.getViewById()
方法来获取这个对象,就相当于我们设置div
的id
是一样的道理
styles
View控件的样式,设置控件的位置、大小等信息。可以设置这些样式:
tags
View控件初始绘制内容,可设置绘制图片、矩形区域、文本等内容。
3. 创建内容
create
方法是去创建了控件并给控件添加了点击事件和触摸事件。最后保存实例。
javascript
// 创建区域以及背景
create() {
let _view = null;
// 创建 View区域
_view = new plus.nativeObj.View(`alarmMsg-${this.item.alarmId || "ins"}`, this.viewStyle);
// 拦截触摸事件: 开启后 区域内的触摸事件不会透传到下面
_view.interceptTouchEvent(true);
// 设置监听点击事件的区域为关闭图片的区域
_view.setTouchEventRect({
top: `${uni.upx2px(18)}px`,
left: '92%',
width: '22px',
height: '22px'
})
// 增加点击事件监听
_view.addEventListener("click", (e) => {
if (this.hiding) return;
this.hiding = true;
this.cb && this.cb({ type: "click", result: this.item });
this.animationHide();
});
// 触摸事件监听
_view.addEventListener("touchstart", res => {
this.clientY = res.clientY;
});
// 触摸事件监听
_view.addEventListener("touchmove", res => {
const {
clientY
} = res;
let offsetY = this.clientY - clientY;
if (offsetY > 25 && !this.hiding) {
this.hiding = true;
this.cb && this.cb({
type: "move",
result: this.item
});
this.animationHide();
}
});
// 保存
this.view = _view;
// 画内容
this.drawInfo();
// // 显示
// this.animationShow();
}
然后封装drawInfo
方法:
javascript
// 画内容
drawInfo() {
const {
message
} = this.item;
this.view.draw([{
tag: "rect",
id: "rectBox",
position: {
top: '0px',
left: `0px`,
width: `100%`,
height: `100%`
},
rectStyles: {
color: 'rgba(241, 58, 14, .6)',
radius: '8px',
}
},
{
tag: 'img',
id: 'imgBox1',
src: img1,
position: {
top: `${uni.upx2px(22)}px`,
left: '10px',
width: '26px',
height: '22px'
}
},
{
tag: 'img',
id: 'imgBox2',
src: img2,
position: {
top: `${uni.upx2px(18)}px`,
left: '92%',
width: '22px',
height: '22px'
}
},
{
tag: "font",
id: "mainFont",
text: message,
textStyles: {
size: `${uni.upx2px(28)}px`,
color: "#feebeb",
align: "left"
},
position: {
top: `${uni.upx2px(28)}px`,
left: `10%`,
height: "wrap_content"
}
},
]);
}
这里的this.view
就是刚刚创建的控件实例,draw
是原生控件对象的方法,详情请查看官方文档
里面就把我们需要的内容都添加到数组里面就行了,比较麻烦的是这个位置,只能使用绝对定位的方式去控制,所以调试比较费时,其他的看文档就可以了。
4. 使用示例
app.vue
文件
vue
<script>
import { createAlarm } from '@/common/nativeObj.js';
export default {
onLaunch() {
// 先创建一个消息的实例
this._netAlertMsgIns = createAlarm({
alarmId: new Date().getTime(),
message: '当前无法连接网络,可检查网络设置是否正常'
});
// 监听网络状态
uni.onNetworkStatusChange(this.netWorkCallback);
// 获取网络状态
uni.getNetworkType({
success: (res) => {
let result = res.networkType !== 'none';
if (result) {
this._netAlertMsgIns.hide();
} else {
// 没网
this._netAlertMsgIns.animationShow();
}
},
fail: () => {
this._netAlertMsgIns.animationShow();
}
});
},
onUnload: function () {
uni.offNetworkStatusChange(this.netWorkCallback);
},
methods: {
netWorkCallback(res) {
if (res.isConnected) {
this._netAlertMsgIns.animationHide();
} else {
// 当网络断开后的逻辑
this._netAlertMsgIns.animationShow();
}
}
}
}
</script>
现在就可以很便捷的实现全局的一个网络状态改变时的消息提示了,不需要在页面去重复引入组件之类的操作,只需在软件的入口实现这个方法就可以了。
拓展
其实还可以通过配置来实现和element-ui
中message
提示组件一样的效果,后面看有时间去做成一个插件吧。
下班!