抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

简介

游戏中经常会需要红点来提醒玩家有新内容,每个部分单独控制红点显然不是个好办法,因此需要红点系统来统一管理红点。

原理

所有红点根据层级关系组织成红点树,每次有数据变化就从红点树的叶子结点开始向上更新。

红点树

属性

红点数有_children 来保存所有下属节点,有_parent 来回溯上游节点,并且每个节点都有_path 来记录红点在红点树中的路径。

_dotNum 表示当前红点节点的数据,_isShow 用来控制当前红点节点是否需要显示红点。

红点_type 分为两类,一类是 Eternal,只有红点数量归零的时候才不显示;一类是 Once,一旦红点数量变少就不再显示。

当红点数量发生变化的时候会调用_onRedDotChangedCallback 方法执行对应的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export enum RedDotType {
Eternal,
Once,
}

export default class RedDot {
/** 父节点 */
private _parent: RedDot | null;
/** 子节点 */
private _children;
/** 红点节点路径 */
public _path: string;
/** 红点数量 */
private _dotNum;
/** 是否展示红点 */
private _isShow;
/** 红点类型 */
private _type: RedDotType;

private _onRedDotChangedCallback: Function | null;

//----- function start -----
}

方法

添加红点子节点

先获取需要添加的红点节点的路径,判断是否已添加。未添加的情况下我们设置当前红点节点的子节点为需要添加的节点,设置需要添加节点的父节点为当前节点,并且走一遍红点变化的逻辑,防止出现 bug。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 添加红点子节点
* @param child
*/
public addChild(child: RedDot) {
let path = child._path;
if (!this._children[path]) {
this._children[path] = child;
child._parent = this;
//子节点有展示红点 则该节点必展示红点
if (child._isShow) {
this._isShow = true;
}
//改变红点数量
this.changeDotNum(child.getDotNum());
} else {
console.warn("红点节点重复添加:", path);
}
}
改变红点数量

首先我们保存当前节点的红点数量,然后根据传入的变化数量对当前节点红点数量进行改变。

改变之后会发生两种情况:

  1. 红点数量小于等于 0,这时候我们置红点为不显示。
  2. 红点数量大于 0,如果传入的变化量大于 0,则红点显示;否则根据红点类型判断,如果是 Once,则红点数量归零,并且置为不显示。

处理完当前节点的红点数量后,我们回溯父节点,对父节点进行同样的操作。不过传入的变化量需要根据当前节点是否显示红点来判断,如果是不显示红点,则我们传入负的之前保存的节点红点数量;显示红点,则传入该方法传入的参数。

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
/**
* 改变红点数量
* @param num
*/
public changeDotNum(num: number) {
let defDotNum = this._dotNum;
this._dotNum += num;
//判断是否展示红点
if (this._dotNum <= 0) {
this._dotNum = 0;
this._isShow = false;
} else {
if(num > 0){
this._isShow = true;
}else{
if(this._type == RedDotType.Once){
this._dotNum = 0;
this._isShow = false;
}
}
}
//触发红点变化回调
this._onRedDotChangedCallback && this._onRedDotChangedCallback(this._isShow);
//父节点改变红点数量
this._parent && this._parent.changeDotNum(this._isShow ? num : -defDotNum);
}
其他

基础功能,根据函数内容即可得知作用。

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
/**
* 获得所有子节点
* @returns
*/
public getChildren(){
return this._children;
}
/**
* 是否展示红点
* @returns
*/
public checkShow() {
this._onRedDotChangedCallback && this._onRedDotChangedCallback(this._isShow);
}
/**
* 获得红点数量
* @returns
*/
public getDotNum() {
return this._dotNum;
}
/**
* 设置红点变化回调
* @param callback
*/
public onRedDotChanged(callback) {
this._onRedDotChangedCallback = callback;
}

/**
* 移除红点变化回调
*/
public removeRedDotChange(){
this._onRedDotChangedCallback = null;
}

红点管理类

管理类维护一个红点字典_redDotDic,用于快速查找对应路径的红点。

初始化

初始化一个 root 红点,该红点是所有红点的根节点。

1
2
3
4
5
6
7
8
/**
* 初始化红点树
*/
public init() {
let root = new RedDot(null, RedDotType.Eternal);
root._path = "root";
this._redDotDic["root"] = root;
}

注册红点

用于注册红点的方法。先判断红点是否已注册,未注册的情况下执行注册方法:

  1. 裁剪红点路径,根据”/“分开。
  2. 根据红点路径长度循环判断是否注册过红点,未注册则新建红点并保存到红点字典,红点路径每次也会根据红点层级拼接。如果新建的红点是最终路径,则返回该红点。
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
/**
* 注册红点
* @param redDot
*/
public register(path: string, type: RedDotType = RedDotType.Eternal): RedDot {
let leaf = this.getRedDot(path);

if (!leaf) {
let redDotPath = path.split("/");
let index = 0;
let curDic = this._redDotDic["root"] as RedDot;
let subPath = "";

while (index < redDotPath.length) {
let child = curDic.getChildren()[redDotPath[index]];
subPath += (index == 0 ? "" : "/") + redDotPath[index];
if (!child) {
let newRedDot = new RedDot(null, type);
newRedDot._path = subPath;
curDic.addChild(newRedDot);
if (index == redDotPath.length - 1) {
leaf = newRedDot;
}
child = newRedDot;
this._redDotDic[subPath] = child;
}

curDic = child;
index++;
}
}
return leaf;
}

其他

基础功能,根据函数内容即可得知作用。

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
/**
* 改变红点数据
* @param path
* @param num
*/
public changeDotNum(path, num) {
let redDot = this.getRedDot(path);
if (redDot) {
redDot.changeDotNum(num);
} else {
console.warn("不存在该红点", path);
}
}

/**
* 设置红点回调
* @param path
* @param callback
*/
public setRedDotCallback(path,callback){
let redDot = this.getRedDot(path);
if(redDot){
redDot.onRedDotChanged(callback);
}
}

/**
* 移除红点回调
* @param path
*/
public removeRedDotCallback(path){
let redDot = this.getRedDot(path);
if(redDot){
redDot.removeRedDotChange();
}
}

/**
* 获得红点
* @param path
* @returns
*/
private getRedDot(path: string): RedDot {
return this._redDotDic[path];
}

public getRoot() {
return this._redDotDic["root"];
}

代码

RedDot
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import { RedDotType } from "./RedDotManager";

export default class RedDot {
/** 父节点 */
private _parent: RedDot | null;
/** 子节点 */
private _children;
/** 红点节点路径 */
public _path: string;
/** 红点数量 */
private _dotNum;
/** 是否展示红点 */
private _isShow;
/** 红点类型 */
private _type: RedDotType;

private _onRedDotChangedCallback: Function | null;

public constructor(parent = null, type = RedDotType.Eternal) {
this._parent = parent;
this._children = {};
this._dotNum = 0;
this._isShow = false;
this._type = type;
}

/**
* 添加红点子节点
* @param child
*/
public addChild(child: RedDot) {
let path = child._path;
if (!this._children[path]) {
this._children[path] = child;
child._parent = this;
//子节点有展示红点 则该节点必展示红点
if (child._isShow) {
this._isShow = true;
}
//改变红点数量
this.changeDotNum(child.getDotNum());
} else {
console.warn("红点节点重复添加:", path);
}
}

/**
* 获得所有子节点
* @returns
*/
public getChildren() {
return this._children;
}

/**
* 是否展示红点
* @returns
*/
public checkShow() {
this._onRedDotChangedCallback &&
this._onRedDotChangedCallback(this._isShow);
}

/**
* 改变红点数量
* @param num
*/
public changeDotNum(num: number) {
let defDotNum = this._dotNum;
this._dotNum += num;
//判断是否展示红点
if (this._dotNum <= 0) {
this._dotNum = 0;
this._isShow = false;
} else {
if (num > 0) {
this._isShow = true;
} else {
if (this._type == RedDotType.Once) {
this._dotNum = 0;
this._isShow = false;
}
}
}
//触发红点变化回调
this._onRedDotChangedCallback &&
this._onRedDotChangedCallback(this._isShow);
//父节点改变红点数量
this._parent && this._parent.changeDotNum(this._isShow ? num : -defDotNum);
}

/**
* 获得红点数量
* @returns
*/
public getDotNum() {
return this._dotNum;
}
/**
* 设置红点变化回调
* @param callback
*/
public onRedDotChanged(callback) {
this._onRedDotChangedCallback = callback;
}

/**
* 移除红点变化回调
*/
public removeRedDotChange() {
this._onRedDotChangedCallback = null;
}
}
RedDotManager
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import RedDot from "./RedDot";
export enum RedDotType {
Eternal,
Once,
}

export default class RedDotManager {
private static _instance: RedDotManager | null = null;
/** 红点字典 */
private _redDotDic = {};

public static getInst(): RedDotManager {
if (!this._instance) {
this._instance = new RedDotManager();
}
return this._instance;
}

/**
* 初始化红点树
*/
public init() {
let root = new RedDot(null, RedDotType.Eternal);
root._path = "root";
this._redDotDic["root"] = root;
}

/**
* 注册红点
* @param redDot
*/
public register(path: string, type: RedDotType = RedDotType.Eternal): RedDot {
let leaf = this.getRedDot(path);

if (!leaf) {
let redDotPath = path.split("/");
let index = 0;
let curDic = this._redDotDic["root"] as RedDot;
let subPath = "";

while (index < redDotPath.length) {
let child = curDic.getChildren()[redDotPath[index]];
subPath += (index == 0 ? "" : "/") + redDotPath[index];
if (!child) {
let newRedDot = new RedDot(null, type);
newRedDot._path = subPath;
curDic.addChild(newRedDot);
if (index == redDotPath.length - 1) {
leaf = newRedDot;
}
child = newRedDot;
this._redDotDic[subPath] = child;
}

curDic = child;
index++;
}
}
return leaf;
}

/**
* 改变红点数据
* @param path
* @param num
*/
public changeDotNum(path, num) {
let redDot = this.getRedDot(path);
if (redDot) {
redDot.changeDotNum(num);
} else {
console.warn("不存在该红点", path);
}
}

/**
* 设置红点回调
* @param path
* @param callback
*/
public setRedDotCallback(path, callback) {
let redDot = this.getRedDot(path);
if (redDot) {
redDot.onRedDotChanged(callback);
}
}

/**
* 移除红点回调
* @param path
*/
public removeRedDotCallback(path) {
let redDot = this.getRedDot(path);
if (redDot) {
redDot.removeRedDotChange();
}
}

/**
* 获得红点
* @param path
* @returns
*/
private getRedDot(path: string): RedDot {
return this._redDotDic[path];
}

public getRoot() {
return this._redDotDic["root"];
}
}

评论