uniapp 全局消息通知弹窗(App端)

实现效果

实现一个顶部全局消息通知,并且可以常驻,除非手动关闭

效果图如下

在这里插入图片描述

在这里插入图片描述

收到告警通知 弹窗顶部向下弹出,可点击跳转到对应页面,可上滑关闭弹窗弹窗出现期间不能阻塞其他操作切换页面、点击其他按钮…)…

  1. 全局通知
  2. 可以常驻,不影响其他操作(页面跳转、点击事件
  3. 点击消失触发回调)、向上滑动消失触发回调)
  4. 带点动画

实现

思索与翻找文档良久,未得容易的实现方式,转而向 HTML5+求解答,得一解决方案,遂记之,以留痕迹便与他人。

plus.nativeObj.View 原生控件对象

文档传送门

文档说明

原生控件对象可用于在屏幕绘制图片文本内容,后显示覆盖显示的; 调用Webview窗口对象append方法添加到Webview中,显示在父窗口所有子Webview的上面;不添加到Webview窗口对象显示所有Webview的上面。

注意事项

  1. 这东西无法写阴影box-shadow

不过可以用图片替代

  1. 自带startAnimation 没啥效果(也许是我用错了)

可以通过定时器自己实现动画效果

代码展示

内含详细注释

const { statusBarHeight } = uni.getSystemInfoSync();
class NativeMsg {
	// 整个区域宽高
	viewStyle = {
		backgroundColor: "rgba(255,255,255,0)",
		top: "0px",
		left: "0px",
		width: "100%",
		// 取图片高度(带阴影尺寸
		height: `${uni.upx2px(239)}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.bgBitmap = null;
		// 回调函数
		this.cb = cb || null;
		// 隐藏过程flag,防止重复执行
		this.hiding = false;
		// 标记当前弹窗状态
		this.status = "active";
		this.create();
	}
	// 创建区域以及背景
	create() {
		this.loadBg().then(() => {
			let _view = null;
			// 创建 View区域
			_view = new plus.nativeObj.View(`alarmMsg-${this.item.alarmId || "ins"}`, this.viewStyle);
			// 画背景
			_view.drawBitmap(
				this.bitmap,
				{},
				{ width: this.viewStyle.width, height: this.viewStyle.height, left: 0, top: 0 },
				"alarm-bg"
			);
			// 拦截触摸事件: 开启区域内的触摸事件不会透传到下面
			_view.interceptTouchEvent(true);
			// 增加点击事件监听
			_view.addEventListener("click", () => {
				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();
		});
	}
	// 加载背景图片
	loadBg() {
		// 创建Bitmap图片
		this.bitmap = new plus.nativeObj.Bitmap("nativeMsg-bg");
		// 以Promise方式封装 图片加载过程
		return new Promise((resolve, reject) => {
			// 加载图片, 路径需要注意
			this.bitmap.load(
				"_www/static/tpt/alarm-bg.png",
				() => {
					resolve();
				},
				error => {
					console.log(" ====> error", error);
					reject();
				}
			);
		});
	}
	// 画内容
	drawInfo() {
		const { warningTypeStr, projectName, description } = this.item;
		this.view.draw([
			{
				tag: "font",
				id: "mainFont",
				text: warningTypeStr,
				textStyles: { size: `${uni.upx2px(36)}px`, color: "#262626", weight: "bold", align: "left" },
				position: { top: `${uni.upx2px(60)}px`, left: `${uni.upx2px(80)}px`, height: "wrap_content" }
			},
			{
				tag: "font",
				id: "projectFont",
				text: projectName,
				textStyles: { size: `${uni.upx2px(24)}px`, color: "#7B7B7B", align: "right", overflow: "ellipsis" },
				position: {
					top: `${uni.upx2px(60)}px`,
					left: `50%`,
					width: `${uni.upx2px(750 / 2 - 40 - 20)}px`,
					height: "wrap_content"
				}
			},
			{
				tag: "font",
				id: "infoFont",
				text: description,
				textStyles: { size: `${uni.upx2px(28)}px`, color: "#7B7B7B", align: "left", overflow: "ellipsis" },
				position: {
					top: `${uni.upx2px(117)}px`,
					left: `${uni.upx2px(80)}px`,
					width: `${uni.upx2px(670 - 40 - 10)}px`,
					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.close();
			this.hiding = false;
			this.status = "close";
			return;
		}
		setTimeout(() => {
			this.animationHide();
		}, 0);
	}
	// 获取当前状态
	getStatus() {
		return this.status;
	}
	// 不用动画直接消失
	hide() {
		this.view.hide();
		this.view.close();
	}
}

// 对外暴露一个创建实例方法
export function createAlarm(item, cb) {
	return new NativeMsg(item, cb);
}

简单描述过程

createAlarm 方法创建返回一个 NativeMsg实例

NativeMsg创建弹窗过程

  1. 生成Bitmap背景图对象 ,并加载。 方法: loadBg异步
  2. 加载完成背景图片的回调里面创建弹窗区域以及把背景图画上去方法: create
  3. 区域背景画好之后根据传递来的 item 画内容,以及绑定点击 click 事件滑动 touch 事件
    1. 点击事件
      1. 点击设置 this.hiding 状态为真,防止重复触发
      2. 执行回调 this.cb
      3. 关闭弹窗 animationHide 向上消失效果
    2. 滑动事件
      1. touchstart 记录当前 this.clientY = res.clientY;
      2. touchmove 对比当前 clientYthis.clientY 之间距离,达到一定距离触发收起事件,和点击事件差不多,区别在于回调传递type 不同
  4. 根据UI设计把内容话上去 drawInfo
  5. 动画的方式显示弹窗 animationShow

使用

import { createAlarm } from "@/utils/nativeMsg";
// ...
// 接收到消息
function onReceived(data) {
		// #ifdef APP-PLUS
		// 调用
			showAlarmMsg(message);
		// #endif
}

// 定义一个全局变量保存 NativeMsg 实例
let alarmMsgInstance = null;
function showAlarmMsg(msg) {
	console.log(" ====> msg", msg);
	// #ifdef APP-PLUS
  //	业务需求保留一个弹窗, 当存在时候要干掉
	if (alarmMsgInstance &amp;&amp; alarmMsgInstance.getStatus() === "active") {
		let _oldIns = alarmMsgInstance;
		alarmMsgInstance = null;
  //延迟300 等新的弹窗出来后再消失,视觉比较好看
		setTimeout(() => {
			_oldIns.hide();
		}, 300);
	}
	// 创建一个新的弹窗(新的会覆盖在旧的上面)
	alarmMsgInstance = createAlarm(item, res => {
		// 这里是点击或者滑动的回调 点击type是‘click’,滑动是‘move’
    // result传递过去的msg,原封不动的返回过来了。
		const { type, result } = res;
		if (type === "move") return;
		uni.navigateTo({
			url: `/pages/xxxxxx?id=${result.id}`
		});
	});
	// #endif
	// #ifndef APP-PLUS
	console.log(" ====> 收到消息", msg);
	// #endif
}

为什么不做成单例模式

一开始是打算弄成单例模式,之后发现第二条消息来的时候动画效果不太满意,所以改成现在这种可以创建多个,等第二个出来之后在把底下那个干掉。

SubNvues原生窗体

需要挂载某个页面下,场景不太适合

真机效果

Android 真机实测无啥问题

Ios 受限于设备问题暂无测试


2023年5月17日
特意做了个demo gitHub传送门

原文地址:https://blog.csdn.net/weixin_39182097/article/details/129890011

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_48586.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注