mirror of
https://github.com/caochao/cocos_creator_proj_base.git
synced 2026-05-06 15:20:42 +08:00
更新代码
This commit is contained in:
322
README.md
322
README.md
@@ -3,130 +3,232 @@
|
||||
|
||||
使用说明
|
||||
|
||||
1.ListView, 循环滚动列表,固定尺寸item, 屏幕可见范围外item会回收等待下次复用。支持横向,竖向,多行多列。
|
||||
1.ListView, 循环滚动列表,固定尺寸item, 屏幕可见范围外item会回收等待下次复用。支持横向,竖向,多行多列。支持追加删除数据。支持上右下左padding, 支持设置item锚点。
|
||||
|
||||
* 初始化,传入item模板节点(cc.Node),设置各种回调函数
|
||||
* 初始化,传入各种参数,设置item class
|
||||
```
|
||||
@property(cc.ScrollView)
|
||||
scrollview: cc.ScrollView = null;
|
||||
|
||||
@property(cc.Node)
|
||||
mask: cc.Node = null;
|
||||
scrollView: cc.Node = null;
|
||||
|
||||
@property(cc.Node)
|
||||
content: cc.Node = null;
|
||||
|
||||
@property(cc.Node)
|
||||
item_tpl:cc.Node = null;
|
||||
|
||||
private list:ListView.ListView;
|
||||
private _listView:ListView;
|
||||
|
||||
on_show(...params)
|
||||
{
|
||||
this.list = new ListView.ListView({
|
||||
scrollview:this.scrollview,
|
||||
mask:this.mask,
|
||||
content:this.content,
|
||||
item_tpl:this.item_tpl,
|
||||
cb_host:this,
|
||||
item_setter:this.list_item_setter,
|
||||
select_cb:this.list_item_onselect,
|
||||
recycle_cb:this.list_item_onrecycle,
|
||||
const maskNode = this.scrollView.getChildByName("mask");
|
||||
const content = maskNode.getChildByName("content");
|
||||
const item_tpl = content.getChildByName("item");
|
||||
const scrollview = this.scrollView.getComponent(cc.ScrollView);
|
||||
const mask = maskNode.getComponent(cc.Mask);
|
||||
this._listView = new ListView({
|
||||
scrollview,
|
||||
mask,
|
||||
content,
|
||||
item_tpl,
|
||||
item_class:UnlockNewSongItem,
|
||||
column:1,
|
||||
gap_y:10,
|
||||
direction:ListView.ListViewDir.Vertical,
|
||||
gap_y:8,
|
||||
item_anchorX:0.5,
|
||||
item_anchorY:0.5,
|
||||
direction:ListViewDir.Vertical,
|
||||
});
|
||||
this.list.set_data(Consts.AllStages);
|
||||
this._listView.set_data(this._songs);
|
||||
}
|
||||
```
|
||||
* 设置item回调函数
|
||||
* 实现列表item回调方法
|
||||
```
|
||||
list_item_setter(item:cc.Node, desc:Consts.StageDesc, index:number):void
|
||||
{
|
||||
const isOpen = appdata.getStageOpenState(desc.stage, desc.unlockcond, desc.total);
|
||||
const isPassed = appdata.isStagePassed(desc.stage, desc.total);
|
||||
|
||||
const cond = item.getChildByName("cond");
|
||||
const txt_cond = cond.getChildByName("txt_cond");
|
||||
const txt_progress = item.getChildByName("txt_progress");
|
||||
const btn_share = item.getChildByName("btn_share");
|
||||
const img_star = item.getChildByName("img_star");
|
||||
const gold_star = img_star.getChildByName("gold_star");
|
||||
//省略
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
2.ScrollView, 循环滚动列表,支持不定尺寸的item, 屏幕可见范围外item会回收等待下次复用。支持横向,竖向, 但不支持多行多列。
|
||||
|
||||
* 初始化,传入item模板节点(cc.Node)列表,设置各种回调函数
|
||||
```
|
||||
const templates:ScrollItemTemplate[] = [
|
||||
{key:MsgType.ROUND_START.toString(), node:this.item_roundstart},
|
||||
{key:MsgType.LEFT_REDPACK.toString(), node:this.item_leftpack},
|
||||
{key:MsgType.RIGHT_REDPACK.toString(), node:this.item_rightpack},
|
||||
{key:MsgType.GRAB_NOTIFY.toString(), node:this.item_grab},
|
||||
{key:MsgType.ROUND_END.toString(), node:this.item_common},
|
||||
{key:MsgType.ROUND_RESULT.toString(), node:this.item_roundresult},
|
||||
{key:MsgType.LEFT_CHAT.toString(), node:this.item_leftchat},
|
||||
{key:MsgType.RIGHT_CHAT.toString(), node:this.item_rightchat},
|
||||
{key:MsgType.JOIN_ROOM_NOTIFY.toString(), node:this.item_common},
|
||||
{key:MsgType.QUIT_ROOM_NOTIFY.toString(), node:this.item_common},
|
||||
{key:MsgType.DISMISS_ROOM_NOTIFY.toString(), node:this.item_common},
|
||||
{key:MsgType.ROUND_TIMEOUT_NOTICE.toString(), node:this.item_common},
|
||||
];
|
||||
this.scview = new ScrollView({
|
||||
scrollview:this.scrollview,
|
||||
mask:this.mask,
|
||||
content:this.content,
|
||||
item_templates:templates,
|
||||
cb_host:this,
|
||||
item_setter:this.item_setter,
|
||||
recycle_cb:this.list_item_recyle,
|
||||
gap_y:10,
|
||||
auto_scrolling:true,
|
||||
direction:ScrollDirection.Vertical,
|
||||
});
|
||||
```
|
||||
* 设置item回调内部根据传入的key及data为对应item节点设置数据
|
||||
```
|
||||
item_setter(item:cc.Node, key:string, data:any, index:number):[number, number]
|
||||
export class UnlockNewSongItem extends ListViewItem
|
||||
{
|
||||
const enum_key:number = parseInt(key);
|
||||
switch(enum_key)
|
||||
{
|
||||
case MsgType.ROUND_START:
|
||||
item.getComponent(cc.Label).string = format.sprintf("第%d轮开始", data);
|
||||
return [item.width, item.height];
|
||||
private _bg:cc.Sprite;
|
||||
private _txtSongName:cc.Label;
|
||||
private _txtAuthor:cc.Label;
|
||||
private _listenView:TrialListenView;
|
||||
|
||||
onInit()
|
||||
{
|
||||
const node = this.node;
|
||||
this._txtSongName = node.getChildByName("songName").getComponent(cc.Label);
|
||||
this._txtAuthor = node.getChildByName("author").getComponent(cc.Label);
|
||||
|
||||
this._listenView = new TrialListenView();
|
||||
this._listenView.addTo(node, 223, -151);
|
||||
}
|
||||
|
||||
onUnInit()
|
||||
{
|
||||
this._listenView.destroy();
|
||||
this._listenView = null;
|
||||
}
|
||||
|
||||
onSetData(songId:number, index:number)
|
||||
{
|
||||
const songCfg = appData.songLibrary.get(songId);
|
||||
if(!songCfg) return;
|
||||
|
||||
//songname
|
||||
this._txtSongName.string = songCfg.name;
|
||||
this._txtAuthor.string = songCfg.author;
|
||||
|
||||
this._likeView.setVisible(true);
|
||||
this._likeView.setLike(localData.songInfo.isFavoriteSong(songId));
|
||||
|
||||
//试听
|
||||
if(songCfg.hasMp3) {
|
||||
const listenState = Mp3Player.getInst().getListenState(songId);
|
||||
this._listenView.setState(listenState || SongListenState.Idle);
|
||||
}
|
||||
else {
|
||||
this._listenView.setState(SongListenState.None);
|
||||
}
|
||||
}
|
||||
|
||||
onRecycle(songId:number)
|
||||
{
|
||||
this._listenView.setState(SongListenState.None);
|
||||
}
|
||||
|
||||
onTouchEnd(touchPos:cc.Vec2, songId:number, index:number)
|
||||
{
|
||||
if(this._listenView.handleTouchEnd(touchPos, songId)) {
|
||||
return;
|
||||
}
|
||||
if(this._likeView.handleTouchEnd(touchPos, songId, source)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
2.ScrollView, 循环滚动列表,支持不定尺寸的item, 屏幕可见范围外item会回收等待下次复用。支持横向,竖向, 但不支持多行多列。支持追加删除数据。支持上右下左padding, 支持设置item锚点。
|
||||
|
||||
case MsgType.LEFT_REDPACK:
|
||||
case MsgType.RIGHT_REDPACK:
|
||||
return this.set_pack_item(item, data);
|
||||
|
||||
case MsgType.ROUND_RESULT:
|
||||
let node_names:cc.Node = item.getChildByName("names");
|
||||
node_names.getComponent(cc.Label).string = data;
|
||||
item.height = node_names.height - node_names.y;
|
||||
return [item.width, item.height];
|
||||
|
||||
case MsgType.LEFT_CHAT:
|
||||
case MsgType.RIGHT_CHAT:
|
||||
return this.set_chat_item(item, data);
|
||||
|
||||
case MsgType.GRAB_NOTIFY:
|
||||
return this.set_grab_item(item, data);
|
||||
|
||||
case MsgType.DISMISS_ROOM_NOTIFY:
|
||||
case MsgType.ROUND_TIMEOUT_NOTICE:
|
||||
case MsgType.JOIN_ROOM_NOTIFY:
|
||||
case MsgType.QUIT_ROOM_NOTIFY:
|
||||
case MsgType.ROUND_END:
|
||||
item.getComponent(cc.Label).string = data as string;
|
||||
return [item.width, item.height];
|
||||
|
||||
default:
|
||||
return [0, 0];
|
||||
}
|
||||
* 初始化,传入各种参数,设置item class
|
||||
```
|
||||
@property(cc.Node)
|
||||
scrollView:cc.Node = null;
|
||||
|
||||
private initSongsView()
|
||||
{
|
||||
const maskNode = this.scrollView.getChildByName("mask");
|
||||
const content = maskNode.getChildByName("content");
|
||||
const item_tpl = content.getChildByName("item");
|
||||
const scrollview = this.scrollView.getComponent(cc.ScrollView);
|
||||
const mask = maskNode.getComponent(cc.Mask);
|
||||
const templates:ScrollItemTemplate[] = [
|
||||
{key:PushViewItemType.PushSongItem, node:item_tpl, item_class:LatestSongPushItem},
|
||||
];
|
||||
this._songsView = new ScrollPage({
|
||||
scrollview,
|
||||
mask,
|
||||
content,
|
||||
item_templates:templates,
|
||||
gap_x:-30,
|
||||
padding_left:124,
|
||||
padding_right:124,
|
||||
padding_top:200,
|
||||
direction:ScrollDirection.Horizontal,
|
||||
cb_host:this,
|
||||
on_turning:this.onTurning,
|
||||
on_scrolling:this.onScrolling,
|
||||
item_anchorX:0.5,
|
||||
item_anchorY:0.5,
|
||||
});
|
||||
const itemDatas:ScrollItemData[] = this._songs.map(data => {
|
||||
return {
|
||||
key:PushViewItemType.PushSongItem,
|
||||
data,
|
||||
}
|
||||
});
|
||||
this._songsView.set_data(itemDatas);
|
||||
}
|
||||
```
|
||||
* 实现列表item回调方法
|
||||
```
|
||||
export class LatestSongPushItem extends ScrollViewItem
|
||||
{
|
||||
private _defaultCover:cc.Node;
|
||||
private _coverSprite:cc.Sprite;
|
||||
private _coverSpriteFrame:cc.SpriteFrame;
|
||||
private _listenView:TrialListenView;
|
||||
|
||||
onInit(key:PushViewItemType)
|
||||
{
|
||||
const node = this.node;
|
||||
this._defaultCover = node.getChildByName("defaultCover");
|
||||
this._coverSprite = node.getChildByName("cover").getComponent(cc.Sprite);
|
||||
|
||||
this._coverSpriteFrame = new cc.SpriteFrame();
|
||||
this._coverSpriteFrame.name = "LatestSongPushItemSpriteFrame";
|
||||
|
||||
this._listenView = new TrialListenView({
|
||||
prefabPath:"prefabs/misc/trialListen2",
|
||||
downloadingTexure:"xinge_tanchuang_shiting_02",
|
||||
listeningDbPath:"main/xinge_shiting_dh",
|
||||
listeningDbArmature:"xinge_shiting_dh",
|
||||
listeningDbAnim:"xinge_shiting_dh",
|
||||
listeningDbPosX:0,
|
||||
listeningDbPosY:0,
|
||||
});
|
||||
this._listenView.addTo(node, 89, -331);
|
||||
}
|
||||
|
||||
onUnInit(key:PushViewItemType)
|
||||
{
|
||||
this._listenView.destroy();
|
||||
this._listenView = null;
|
||||
|
||||
this._coverSpriteFrame.clearTexture();
|
||||
// this._coverSpriteFrame.destroy();
|
||||
this._coverSpriteFrame = null;
|
||||
}
|
||||
|
||||
private _currSongId:number;
|
||||
onSetData(key:PushViewItemType, songId:number, index:number, is_mesure:boolean):[number, number]
|
||||
{
|
||||
if(is_mesure) {
|
||||
return [this.node.width, this.node.height];
|
||||
}
|
||||
|
||||
const songCfg = appData.songLibrary.get(songId);
|
||||
if(!songCfg) return [this.node.width, this.node.height];
|
||||
|
||||
this._defaultCover.active = !songCfg.cover;
|
||||
this._coverSprite.node.active = !!songCfg.cover;
|
||||
if(songCfg.cover) {
|
||||
this._currSongId = songId;
|
||||
ImageUtil.getInst().setExternalSpriteFrame(songCfg.cover, songId, (tag, tex) => {
|
||||
if(tag == this._currSongId && cc.isValid(this.node) && cc.isValid(this._coverSpriteFrame)) {
|
||||
this._coverSpriteFrame.setTexture(tex);
|
||||
this._coverSprite.spriteFrame = this._coverSpriteFrame;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//试听
|
||||
if(songCfg.hasMp3) {
|
||||
const listenState = Mp3Player.getInst().getListenState(songId);
|
||||
this._listenView.setState(listenState || SongListenState.Idle);
|
||||
}
|
||||
else {
|
||||
this._listenView.setState(SongListenState.None);
|
||||
}
|
||||
|
||||
return [this.node.width, this.node.height];
|
||||
}
|
||||
|
||||
onRecycle(key:PushViewItemType, songId:number, is_mesure:boolean)
|
||||
{
|
||||
if(is_mesure)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this._coverSprite.spriteFrame = null;
|
||||
this._listenView.setState(SongListenState.None);
|
||||
}
|
||||
|
||||
onTouchEnd(key:PushViewItemType, songId:number, touchPos:cc.Vec2, index:number)
|
||||
{
|
||||
this._listenView.handleTouchEnd(touchPos, songId);
|
||||
}
|
||||
}
|
||||
```
|
||||
* 追加数据, 传入key及item数据
|
||||
|
||||
@@ -1,278 +1,279 @@
|
||||
import {loader_mgr} from "../loader/loader_mgr"
|
||||
import * as utils from "../util"
|
||||
import * as Consts from "../../consts"
|
||||
import * as wxapi from "../wxapi/wxstorage"
|
||||
|
||||
const MUSIC_PATH = "sounds/music/{0}";
|
||||
const SOUND_PATH = "sounds/sound/{0}";
|
||||
|
||||
export class AudioPlayer
|
||||
{
|
||||
private static inst:AudioPlayer;
|
||||
private clip_cache:Map<string, cc.AudioClip>;
|
||||
private loading_map:Map<string, boolean>;
|
||||
|
||||
private curr_music:string;
|
||||
private music_id:number;
|
||||
private music_volume:number;
|
||||
private music_mute:boolean;
|
||||
|
||||
private sound_ids:number[];
|
||||
private sound_volume:number;
|
||||
private sound_mute:boolean;
|
||||
|
||||
private constructor()
|
||||
{
|
||||
this.music_id = -1;
|
||||
this.sound_ids = [];
|
||||
this.clip_cache = new Map();
|
||||
this.loading_map = new Map();
|
||||
}
|
||||
|
||||
static getInst()
|
||||
{
|
||||
if(!this.inst)
|
||||
{
|
||||
this.inst = new AudioPlayer();
|
||||
}
|
||||
return this.inst;
|
||||
}
|
||||
|
||||
init()
|
||||
{
|
||||
let sound_vol_str = wxapi.wxStorage.get(Consts.Game.SoundVol);
|
||||
let sound_mute_str = wxapi.wxStorage.get(Consts.Game.SoundMute);
|
||||
let music_vol_str = wxapi.wxStorage.get(Consts.Game.MusicVol);
|
||||
let sound_vol = sound_vol_str ? parseFloat(sound_vol_str) : 1;
|
||||
let music_vol = music_vol_str ? parseFloat(music_vol_str) : 1;
|
||||
let mute_int = sound_mute_str ? parseInt(sound_mute_str) : 0;
|
||||
let is_mute = mute_int == 1;
|
||||
this.set_music_mute(is_mute);
|
||||
this.set_music_volumn(music_vol);
|
||||
this.set_sound_mute(is_mute);
|
||||
this.set_sound_volumn(sound_vol);
|
||||
}
|
||||
|
||||
flush()
|
||||
{
|
||||
wxapi.wxStorage.set(Consts.Game.SoundMute, this.sound_mute ? "1" : "0");
|
||||
}
|
||||
|
||||
//同时只能播放一个
|
||||
play_music(name:string)
|
||||
{
|
||||
if(this.music_id >= 0)
|
||||
{
|
||||
this.stop_music();
|
||||
}
|
||||
|
||||
let path = utils.strfmt(MUSIC_PATH, name);
|
||||
this.curr_music = name;
|
||||
|
||||
if(this.music_mute)
|
||||
{
|
||||
cc.info("music is mute");
|
||||
return;
|
||||
}
|
||||
let clip = this.clip_cache.get(path);
|
||||
if(clip)
|
||||
{
|
||||
this.play_clip(clip, this.music_volume, true, AudioType.Music);
|
||||
}
|
||||
else
|
||||
{
|
||||
let task:AudioPlayTask = {type:AudioType.Music, name:name, path:path, volume:this.music_volume, loop:true};
|
||||
this.load_task(task);
|
||||
}
|
||||
}
|
||||
|
||||
stop_music()
|
||||
{
|
||||
if(this.music_id < 0)
|
||||
{
|
||||
cc.info("no music is playing");
|
||||
return;
|
||||
}
|
||||
cc.audioEngine.stop(this.music_id);
|
||||
this.music_id = -1;
|
||||
}
|
||||
|
||||
get_music_mute()
|
||||
{
|
||||
return this.music_mute;
|
||||
}
|
||||
|
||||
set_music_mute(is_mute:boolean)
|
||||
{
|
||||
this.music_mute = is_mute;
|
||||
if(this.music_id < 0)
|
||||
{
|
||||
if(!is_mute && this.curr_music)
|
||||
{
|
||||
this.play_music(this.curr_music);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(is_mute)
|
||||
{
|
||||
cc.audioEngine.pause(this.music_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
cc.audioEngine.resume(this.music_id);
|
||||
}
|
||||
}
|
||||
|
||||
//0~1
|
||||
set_music_volumn(volume:number)
|
||||
{
|
||||
this.music_volume = volume;
|
||||
if(this.music_id >= 0)
|
||||
{
|
||||
cc.audioEngine.setVolume(this.music_id, volume);
|
||||
}
|
||||
}
|
||||
|
||||
private load_task(task:AudioPlayTask)
|
||||
{
|
||||
let path = task.path;
|
||||
if(this.loading_map.get(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.loading_map.set(path, true);
|
||||
loader_mgr.get_inst().loadRawAsset(path, utils.gen_handler(this.on_clip_loaded, this, task));
|
||||
}
|
||||
|
||||
private on_clip_loaded(task:AudioPlayTask, clip:cc.AudioClip)
|
||||
{
|
||||
this.clip_cache.set(task.path, clip);
|
||||
if(task.type == AudioType.Music && task.name != this.curr_music)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.play_clip(clip, task.volume, task.loop, task.type, task.cb);
|
||||
}
|
||||
|
||||
private play_clip(clip:cc.AudioClip, volume:number, loop:boolean, type:AudioType, cb?:utils.handler)
|
||||
{
|
||||
let aid = cc.audioEngine.play(clip, loop, volume);
|
||||
if(type == AudioType.Music)
|
||||
{
|
||||
this.music_id = aid;
|
||||
}
|
||||
else if(type == AudioType.Sound)
|
||||
{
|
||||
this.sound_ids.push(aid);
|
||||
cc.audioEngine.setFinishCallback(aid, () => {
|
||||
this.on_sound_finished(aid);
|
||||
cb && cb.exec();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private on_sound_finished(aid:number)
|
||||
{
|
||||
let idx = this.sound_ids.findIndex((id) => {
|
||||
return id == aid;
|
||||
});
|
||||
if(idx != -1)
|
||||
{
|
||||
this.sound_ids.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
//可同时播放多个
|
||||
play_sound(name:string, cb?:utils.handler)
|
||||
{
|
||||
if(this.sound_mute)
|
||||
{
|
||||
cc.info("sound is mute");
|
||||
return;
|
||||
}
|
||||
let path = utils.strfmt(SOUND_PATH, name);
|
||||
let clip = this.clip_cache.get(path);
|
||||
if(clip)
|
||||
{
|
||||
this.play_clip(clip, this.sound_volume, false, AudioType.Sound, cb);
|
||||
}
|
||||
else
|
||||
{
|
||||
let task:AudioPlayTask = {type:AudioType.Sound, name:name, path:path, volume:this.sound_volume, loop:false, cb};
|
||||
this.load_task(task);
|
||||
}
|
||||
}
|
||||
|
||||
get_sound_mute()
|
||||
{
|
||||
return this.sound_mute;
|
||||
}
|
||||
|
||||
set_sound_mute(is_mute:boolean)
|
||||
{
|
||||
this.sound_mute = is_mute;
|
||||
this.sound_ids.forEach((sid) => {
|
||||
if(is_mute)
|
||||
{
|
||||
cc.audioEngine.pause(sid);
|
||||
}
|
||||
else
|
||||
{
|
||||
cc.audioEngine.resume(sid);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//0~1
|
||||
set_sound_volumn(volume:number)
|
||||
{
|
||||
this.sound_volume = volume;
|
||||
this.sound_ids.forEach((sid) => {
|
||||
cc.audioEngine.setVolume(sid, volume);
|
||||
});
|
||||
}
|
||||
|
||||
stop_sound()
|
||||
{
|
||||
this.sound_ids.forEach((sid) => {
|
||||
cc.audioEngine.stop(sid);
|
||||
});
|
||||
this.sound_ids.length = 0;
|
||||
}
|
||||
|
||||
clear_cache()
|
||||
{
|
||||
this.clip_cache.forEach((clip, key) => {
|
||||
loader_mgr.get_inst().release(clip);
|
||||
});
|
||||
this.clip_cache.clear();
|
||||
this.loading_map.clear();
|
||||
cc.audioEngine.uncacheAll();
|
||||
}
|
||||
}
|
||||
|
||||
enum AudioType
|
||||
{
|
||||
Music = 1,
|
||||
Sound = 2,
|
||||
}
|
||||
|
||||
interface AudioPlayTask
|
||||
{
|
||||
type:AudioType;
|
||||
name:string;
|
||||
path:string;
|
||||
volume:number;
|
||||
loop:boolean;
|
||||
cb?:utils.handler;
|
||||
}
|
||||
|
||||
export const AUDIO_CONFIG = {
|
||||
Audio_Btn:"audio_btn",
|
||||
Audio_Hint:"audio_hint",
|
||||
Audio_Vitory:"audio_victory",
|
||||
Audio_Coin:"audio_chest_open",
|
||||
Audio_XuanZhen:"audio_xuanzhe",
|
||||
Audio_Unlock:"unlock",
|
||||
Audio_Bgm:"bgm",
|
||||
import {loader_mgr} from "../loader/loader_mgr"
|
||||
import * as utils from "../util"
|
||||
import { LocalStorage } from "../misc/storage";
|
||||
import { Consts } from "../../const";
|
||||
|
||||
const MUSIC_PATH = "sounds/music/{0}";
|
||||
const SOUND_PATH = "sounds/sound/{0}";
|
||||
|
||||
export class AudioPlayer
|
||||
{
|
||||
private static inst:AudioPlayer;
|
||||
private loading_map:Map<string, boolean>;
|
||||
|
||||
private curr_music:string;
|
||||
private curr_volume:number;
|
||||
private music_id:number;
|
||||
private music_volume:number;
|
||||
private music_mute:boolean;
|
||||
|
||||
private sound_ids:number[];
|
||||
private sound_volume:number;
|
||||
private sound_mute:boolean;
|
||||
private storage:LocalStorage;
|
||||
|
||||
private constructor()
|
||||
{
|
||||
this.music_id = -1;
|
||||
this.sound_ids = [];
|
||||
this.loading_map = new Map();
|
||||
this.storage = LocalStorage.getInst();
|
||||
}
|
||||
|
||||
static getInst()
|
||||
{
|
||||
if(!this.inst)
|
||||
{
|
||||
this.inst = new AudioPlayer();
|
||||
}
|
||||
return this.inst;
|
||||
}
|
||||
|
||||
init()
|
||||
{
|
||||
let sound_vol = this.storage.getFloat(Consts.SoundVol, 1);
|
||||
let music_vol = this.storage.getFloat(Consts.MusicVol, 1);
|
||||
let is_mute = this.storage.getBool(Consts.SoundMute);
|
||||
this.set_music_mute(is_mute);
|
||||
this.set_music_volumn(music_vol);
|
||||
this.set_sound_mute(is_mute);
|
||||
this.set_sound_volumn(sound_vol);
|
||||
}
|
||||
|
||||
flush()
|
||||
{
|
||||
// this.storage.setBool(Consts.SoundMute, this.sound_mute);
|
||||
}
|
||||
|
||||
//同时只能播放一个
|
||||
play_music(name:string, pVolume:number = null)
|
||||
{
|
||||
if(this.music_id >= 0)
|
||||
{
|
||||
this.stop_music();
|
||||
}
|
||||
|
||||
const path = utils.strfmt(MUSIC_PATH, name);
|
||||
this.curr_music = name;
|
||||
this.curr_volume = pVolume;
|
||||
|
||||
if(this.music_mute)
|
||||
{
|
||||
cc.log("music is mute");
|
||||
return;
|
||||
}
|
||||
const volume = pVolume || this.music_volume;
|
||||
const task:AudioPlayTask = {type:AudioType.Music, name, path, volume, loop:true};
|
||||
this.load_task(task);
|
||||
}
|
||||
|
||||
stop_music()
|
||||
{
|
||||
if(this.music_id < 0)
|
||||
{
|
||||
cc.log("no music is playing");
|
||||
return;
|
||||
}
|
||||
cc.audioEngine.stop(this.music_id);
|
||||
this.music_id = -1;
|
||||
}
|
||||
|
||||
get_music_mute()
|
||||
{
|
||||
return this.music_mute;
|
||||
}
|
||||
|
||||
set_music_mute(is_mute:boolean)
|
||||
{
|
||||
this.music_mute = is_mute;
|
||||
if(this.music_id < 0)
|
||||
{
|
||||
if(!is_mute && this.curr_music)
|
||||
{
|
||||
const music_id = this.curr_music;
|
||||
const volume = this.curr_volume;
|
||||
this.curr_music = null;
|
||||
this.curr_volume = null;
|
||||
this.play_music(music_id, volume);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(is_mute)
|
||||
{
|
||||
cc.audioEngine.pause(this.music_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
cc.audioEngine.resume(this.music_id);
|
||||
}
|
||||
}
|
||||
|
||||
//0~1
|
||||
set_music_volumn(volume:number)
|
||||
{
|
||||
this.music_volume = volume;
|
||||
if(this.music_id >= 0)
|
||||
{
|
||||
cc.audioEngine.setVolume(this.music_id, volume);
|
||||
}
|
||||
}
|
||||
|
||||
private load_task(task:AudioPlayTask)
|
||||
{
|
||||
const path = task.path;
|
||||
if(this.loading_map.get(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.loading_map.set(path, true);
|
||||
if(task.external)
|
||||
{
|
||||
loader_mgr.get_inst().loadExternalAsset(path, utils.gen_handler(this.on_clip_loaded, this, task));
|
||||
}
|
||||
else
|
||||
{
|
||||
loader_mgr.get_inst().loadRes(path, utils.gen_handler(this.on_clip_loaded, this, task));
|
||||
}
|
||||
}
|
||||
|
||||
private on_clip_loaded(task:AudioPlayTask, clip:cc.AudioClip)
|
||||
{
|
||||
this.loading_map.delete(task.path);
|
||||
if(task.type == AudioType.Music && task.name != this.curr_music)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.play_clip(clip, task.volume, task.loop, task.type, task.cb);
|
||||
}
|
||||
|
||||
private play_clip(clip:cc.AudioClip, volume:number, loop:boolean, type:AudioType, cb?:utils.handler)
|
||||
{
|
||||
let aid = cc.audioEngine.play(clip, loop, volume);
|
||||
if(type == AudioType.Music)
|
||||
{
|
||||
this.music_id = aid;
|
||||
}
|
||||
else if(type == AudioType.Sound)
|
||||
{
|
||||
this.sound_ids.push(aid);
|
||||
cc.audioEngine.setFinishCallback(aid, () => {
|
||||
this.on_sound_finished(aid);
|
||||
cb && cb.exec();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private on_sound_finished(aid:number)
|
||||
{
|
||||
let idx = this.sound_ids.findIndex((id) => {
|
||||
return id == aid;
|
||||
});
|
||||
if(idx != -1)
|
||||
{
|
||||
this.sound_ids.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
//可同时播放多个
|
||||
play_sound(name:string, loop = false, pVolume:number = null, cb?:utils.handler)
|
||||
{
|
||||
if(this.sound_mute)
|
||||
{
|
||||
cc.log("sound is mute");
|
||||
return;
|
||||
}
|
||||
const path = utils.strfmt(SOUND_PATH, name);
|
||||
const volume = pVolume || this.sound_volume;
|
||||
const task:AudioPlayTask = {type:AudioType.Sound, name, path, volume, loop, cb};
|
||||
this.load_task(task);
|
||||
}
|
||||
|
||||
play_external_sound(path:string, loop = false, pVolume:number = null, cb?:utils.handler)
|
||||
{
|
||||
if(this.sound_mute)
|
||||
{
|
||||
cc.log("sound is mute");
|
||||
return;
|
||||
}
|
||||
const volume = pVolume || this.sound_volume;
|
||||
const task:AudioPlayTask = {type:AudioType.Sound, name:path, path, volume, loop, external:true, cb};
|
||||
this.load_task(task);
|
||||
}
|
||||
|
||||
get_sound_mute()
|
||||
{
|
||||
return this.sound_mute;
|
||||
}
|
||||
|
||||
set_sound_mute(is_mute:boolean)
|
||||
{
|
||||
this.sound_mute = is_mute;
|
||||
this.sound_ids.forEach((sid) => {
|
||||
if(is_mute)
|
||||
{
|
||||
cc.audioEngine.pause(sid);
|
||||
}
|
||||
else
|
||||
{
|
||||
cc.audioEngine.resume(sid);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//0~1
|
||||
set_sound_volumn(volume:number)
|
||||
{
|
||||
this.sound_volume = volume;
|
||||
this.sound_ids.forEach((sid) => {
|
||||
cc.audioEngine.setVolume(sid, volume);
|
||||
});
|
||||
}
|
||||
|
||||
stop_sound()
|
||||
{
|
||||
this.sound_ids.forEach((sid) => {
|
||||
cc.audioEngine.stop(sid);
|
||||
});
|
||||
this.sound_ids.length = 0;
|
||||
}
|
||||
|
||||
clear_cache()
|
||||
{
|
||||
this.stop_music();
|
||||
this.loading_map.clear();
|
||||
cc.audioEngine.uncacheAll();
|
||||
}
|
||||
}
|
||||
|
||||
enum AudioType
|
||||
{
|
||||
Music = 1,
|
||||
Sound = 2,
|
||||
}
|
||||
|
||||
interface AudioPlayTask
|
||||
{
|
||||
type:AudioType;
|
||||
name:string;
|
||||
path:string;
|
||||
volume:number;
|
||||
loop:boolean;
|
||||
external?:boolean;
|
||||
cb?:utils.handler;
|
||||
}
|
||||
|
||||
export const AUDIO_CONFIG = {
|
||||
SE_01:"SE_01",
|
||||
SE_02:"SE_02",
|
||||
SE_03:"SE_03",
|
||||
}
|
||||
@@ -1,17 +1,40 @@
|
||||
//item及父节点锚点都为(0,1)
|
||||
export class LayoutUtil
|
||||
{
|
||||
static vertical_layout(index:number, item_width:number, item_height:number, column:number = 1, gap_x:number = 0, gap_y:number = 0):[number, number]
|
||||
{
|
||||
let x:number = (index % column) * (item_width + gap_x);
|
||||
let y:number = -Math.floor(index / column) * (item_height + gap_y);
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
static horizontal_layout(index:number, item_width:number, item_height:number, row:number = 1, gap_x:number = 0, gap_y:number = 0):[number, number]
|
||||
{
|
||||
let x:number = Math.floor(index / row) * (item_width + gap_x);
|
||||
let y:number = -(index % row) * (item_height + gap_y);
|
||||
return [x, y];
|
||||
}
|
||||
//item及父节点锚点都为(0,1)
|
||||
export class LayoutUtil
|
||||
{
|
||||
static vertical_layout(index:number, item_width:number, item_height:number, column = 1, gap_x = 0, gap_y = 0, padding_left = 0, padding_top = 0):[number, number]
|
||||
{
|
||||
const x = (index % column) * (item_width + gap_x) + padding_left;
|
||||
const y = -Math.floor(index / column) * (item_height + gap_y) - padding_top;
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
static horizontal_layout(index:number, item_width:number, item_height:number, row = 1, gap_x = 0, gap_y = 0, padding_left = 0, padding_top = 0):[number, number]
|
||||
{
|
||||
const x = Math.floor(index / row) * (item_width + gap_x) + padding_left;
|
||||
const y = -(index % row) * (item_height + gap_y) - padding_top;
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
static set_pivot_smart(node:cc.Node, ax:number, ay:number, recursive = false)
|
||||
{
|
||||
const deltaAx = ax - node.anchorX;
|
||||
const deltaAy = ay - node.anchorY;
|
||||
node.anchorX = ax;
|
||||
node.anchorY = ay;
|
||||
|
||||
//改变节点锚点,位置值相对锚点不变。需要调整节点至新位置才能保持一致视觉效果
|
||||
const deltaX = deltaAx * node.width;
|
||||
const deltaY = deltaAy * node.height;
|
||||
node.x += deltaX;
|
||||
node.y += deltaY;
|
||||
|
||||
node.children.forEach(child => {
|
||||
//父节点锚点改变,子节点相对父节点位置值不变,需要调整子节点至新位置才能保持一致视觉效果
|
||||
child.x -= deltaX;
|
||||
child.y -= deltaY;
|
||||
if(recursive) {
|
||||
LayoutUtil.set_pivot_smart(child, ax, ay);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
55
components/listviewitem.ts
Normal file
55
components/listviewitem.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
const {ccclass, property} = cc._decorator;
|
||||
@ccclass
|
||||
export class ListViewItem extends cc.Component
|
||||
{
|
||||
// constructor()
|
||||
// {
|
||||
// super();
|
||||
// }
|
||||
|
||||
onInit()
|
||||
{
|
||||
// cc.log("item初如化");
|
||||
}
|
||||
|
||||
onUnInit()
|
||||
{
|
||||
// cc.log("item析构");
|
||||
}
|
||||
|
||||
onSetData(data:any, index:number)
|
||||
{
|
||||
// cc.log("item设置数据");
|
||||
}
|
||||
|
||||
onSetSelect(is_select:boolean, index:number)
|
||||
{
|
||||
// cc.log("item选中状态改变");
|
||||
}
|
||||
|
||||
onRecycle(data:any)
|
||||
{
|
||||
// cc.log("item被回收");
|
||||
}
|
||||
|
||||
onSelected(data:any, index:number)
|
||||
{
|
||||
// cc.log("item被选中");
|
||||
}
|
||||
|
||||
onTouchEnd(touchPos:cc.Vec2, data:any, index:number)
|
||||
{
|
||||
// cc.log("item非滑动状态下被点击");
|
||||
}
|
||||
|
||||
onBecameInvisible()
|
||||
{
|
||||
// cc.log("item从父节点移除或不可见");
|
||||
}
|
||||
|
||||
setLeftTop(left:number, top:number)
|
||||
{
|
||||
this.node.x = left + this.node.anchorX * this.node.width;
|
||||
this.node.y = top - (1 - this.node.anchorY) * this.node.height;
|
||||
}
|
||||
}
|
||||
115
components/page_indicator.ts
Normal file
115
components/page_indicator.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
const {ccclass, property} = cc._decorator;
|
||||
@ccclass
|
||||
export class PageIndicator extends cc.Component {
|
||||
|
||||
@property({type: cc.SpriteFrame})
|
||||
selectedSpriteFrame: cc.SpriteFrame = null;
|
||||
|
||||
@property({type: cc.SpriteFrame})
|
||||
unSelectedSpriteFrame: cc.SpriteFrame = null;
|
||||
|
||||
@property
|
||||
spacing = 0;
|
||||
|
||||
@property
|
||||
cellSize = cc.size(10, 10);
|
||||
|
||||
@property({tooltip: "1 for horizontal, 2 for vertical"})
|
||||
direction = 1;
|
||||
|
||||
private _comps:cc.Sprite[];
|
||||
private _compPool:cc.Sprite[];
|
||||
private _compsCnt:number;
|
||||
private _selectedIndex:number = -1;
|
||||
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
onDestroy()
|
||||
{
|
||||
this._comps = null;
|
||||
this._compPool = null;
|
||||
}
|
||||
|
||||
setPageCnt(value:number)
|
||||
{
|
||||
if(value == this._compsCnt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this._comps)
|
||||
{
|
||||
this._comps = [];
|
||||
this._compPool = [];
|
||||
this._compsCnt = 0;
|
||||
}
|
||||
|
||||
for(let i = this._compsCnt; i < value; i++)
|
||||
{
|
||||
let comp = this._compPool.pop();
|
||||
if(!comp)
|
||||
{
|
||||
const node = new cc.Node();
|
||||
node.width = this.cellSize.width;
|
||||
node.height = this.cellSize.height;
|
||||
comp = node.addComponent(cc.Sprite);
|
||||
comp.spriteFrame = this.unSelectedSpriteFrame;
|
||||
}
|
||||
comp.node.parent = this.node;
|
||||
this._comps[i] = comp;
|
||||
}
|
||||
for(let i = this._compsCnt - 1; i >= value; i--)
|
||||
{
|
||||
const comp = this._comps.pop();
|
||||
comp.node.removeFromParent();
|
||||
this._compPool.push(comp);
|
||||
}
|
||||
this._compsCnt = value;
|
||||
|
||||
this.updateLayout();
|
||||
}
|
||||
|
||||
private updateLayout()
|
||||
{
|
||||
//以锚点(0.5, 0.5)计算父容器和子节点位置
|
||||
if(this.direction == 1)
|
||||
{
|
||||
const deltaX = this.cellSize.width + this.spacing;
|
||||
this.node.width = (this._compsCnt - 1) * deltaX;
|
||||
this.node.height = this.cellSize.height;
|
||||
const halfWidth = this.node.width / 2;
|
||||
this._comps.forEach((comp, idx) => {
|
||||
comp.node.x = idx * deltaX - halfWidth;
|
||||
comp.node.y = 0;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
const deltaY = this.cellSize.height + this.spacing;
|
||||
this.node.width = this.cellSize.width;
|
||||
this.node.height = (this._compsCnt - 1) * deltaY;
|
||||
const halfHeight = this.node.height / 2;
|
||||
this._comps.forEach((comp, idx) => {
|
||||
comp.node.x = 0;
|
||||
comp.node.y = idx * deltaY - halfHeight;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
turningTo(pageIdx:number)
|
||||
{
|
||||
if(pageIdx == this._selectedIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(this._selectedIndex != -1 && this._selectedIndex < this._compsCnt)
|
||||
{
|
||||
this._comps[this._selectedIndex].spriteFrame = this.unSelectedSpriteFrame;
|
||||
}
|
||||
this._selectedIndex = pageIdx;
|
||||
this._comps[pageIdx].spriteFrame = this.selectedSpriteFrame;
|
||||
}
|
||||
}
|
||||
213
components/scrollpage.ts
Normal file
213
components/scrollpage.ts
Normal file
@@ -0,0 +1,213 @@
|
||||
import { ScrollView, ScrollViewParams, ScrollDirection } from "./scrollview";
|
||||
import { Action1 } from "../../const";
|
||||
import { TimerMgr } from "../timer/timer_mgr";
|
||||
import { gen_handler } from "../util";
|
||||
|
||||
//参考自engine/CCPageView
|
||||
export class ScrollPage extends ScrollView
|
||||
{
|
||||
private _curPageIdx:number;
|
||||
private _lastPageIdx:number;
|
||||
private _pageTurningSpeed:number;
|
||||
private _scrollThreshold:number;
|
||||
private _autoPageTurningThreshold:number;
|
||||
private _scrollCenterOffsetX:number[]; // 每一个页面居中时需要的偏移量(X)
|
||||
private _scrollCenterOffsetY:number[]; // 每一个页面居中时需要的偏移量(Y)
|
||||
private _onTurning:(pageIndex:number) => void;
|
||||
private _onScrolling:Action1;
|
||||
private _timer:number;
|
||||
|
||||
constructor(params:ScrollPageParams)
|
||||
{
|
||||
super(params);
|
||||
this._onTurning = params.on_turning;
|
||||
this._onScrolling = params.on_scrolling;
|
||||
this._curPageIdx = 0;
|
||||
this._lastPageIdx = -1;
|
||||
this._pageTurningSpeed = 0.3;
|
||||
this._scrollThreshold = 0.5;
|
||||
this._autoPageTurningThreshold = 100;
|
||||
this._scrollCenterOffsetX = [];
|
||||
this._scrollCenterOffsetY = [];
|
||||
}
|
||||
|
||||
scroll_to_page(idx:number, time = 0, delayTurning = false)
|
||||
{
|
||||
if (idx < 0 || idx >= this.packItems.length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this._curPageIdx = idx;
|
||||
this.scrollview.scrollToOffset(this._moveOffsetValue(idx), time, true);
|
||||
this.render();
|
||||
//用以在onTurning事件回调中获取scrollViewItem
|
||||
if(delayTurning) {
|
||||
this._timer = TimerMgr.getInst().once(0, gen_handler(this._dispatchPageTurningEvent, this), "ScrollPage, scroll_to_page timer", this.scrollview.node);
|
||||
}
|
||||
else {
|
||||
this._dispatchPageTurningEvent();
|
||||
}
|
||||
}
|
||||
|
||||
get page_index()
|
||||
{
|
||||
return this._curPageIdx;
|
||||
}
|
||||
|
||||
protected on_scrolling()
|
||||
{
|
||||
super.on_scrolling();
|
||||
if(this._onScrolling) {
|
||||
this._onScrolling.call(this.cb_host);
|
||||
}
|
||||
}
|
||||
|
||||
protected handle_release_logic()
|
||||
{
|
||||
super.handle_release_logic();
|
||||
|
||||
let bounceBackStarted = this.scrollview._startBounceBackIfNeeded();
|
||||
let moveOffset = this._touchBeganPosition.sub(this._touchEndPosition);
|
||||
let dragDirection = this.get_drag_direction(moveOffset);
|
||||
|
||||
//点击无滑动
|
||||
if(dragDirection === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this.packItems || !this.packItems.length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (bounceBackStarted) {
|
||||
if (dragDirection === 0) {
|
||||
return;
|
||||
}
|
||||
if (dragDirection > 0) {
|
||||
this._curPageIdx = this.packItems.length - 1;
|
||||
}
|
||||
else {
|
||||
this._curPageIdx = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
let index = this._curPageIdx, nextIndex = index + dragDirection;
|
||||
let timeInSecond = this._pageTurningSpeed * Math.abs(index - nextIndex);
|
||||
if (nextIndex >= 0 && nextIndex < this.packItems.length) {
|
||||
if (this._isScrollable(moveOffset, index, nextIndex)) {
|
||||
this.scroll_to_page(nextIndex, timeInSecond);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
let touchMoveVelocity = this.scrollview._calculateTouchMoveVelocity();
|
||||
if (this._isQuicklyScrollable(touchMoveVelocity)) {
|
||||
this.scroll_to_page(nextIndex, timeInSecond);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.scroll_to_page(index, timeInSecond);
|
||||
}
|
||||
}
|
||||
|
||||
private _dispatchPageTurningEvent() {
|
||||
if (this._lastPageIdx === this._curPageIdx) return;
|
||||
this._lastPageIdx = this._curPageIdx;
|
||||
|
||||
if(this._onTurning)
|
||||
{
|
||||
this._onTurning.call(this.cb_host, this._curPageIdx);
|
||||
}
|
||||
}
|
||||
|
||||
// 是否超过自动滚动临界值
|
||||
private _isScrollable(offset, index, nextIndex) {
|
||||
let curPageCenter, nextPageCenter;
|
||||
if (this.dir === ScrollDirection.Horizontal) {
|
||||
curPageCenter = this._scrollCenterOffsetX[index];
|
||||
nextPageCenter = this._scrollCenterOffsetX[nextIndex];
|
||||
return Math.abs(offset.x) >= Math.abs(curPageCenter - nextPageCenter) * this._scrollThreshold;
|
||||
}
|
||||
else if (this.dir === ScrollDirection.Vertical) {
|
||||
curPageCenter = this._scrollCenterOffsetY[index];
|
||||
nextPageCenter = this._scrollCenterOffsetY[nextIndex];
|
||||
return Math.abs(offset.y) >= Math.abs(curPageCenter - nextPageCenter) * this._scrollThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
// 快速滑动
|
||||
private _isQuicklyScrollable(touchMoveVelocity) {
|
||||
if (this.dir === ScrollDirection.Horizontal) {
|
||||
if (Math.abs(touchMoveVelocity.x) > this._autoPageTurningThreshold) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (this.dir === ScrollDirection.Vertical) {
|
||||
if (Math.abs(touchMoveVelocity.y) > this._autoPageTurningThreshold) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 通过 idx 获取偏移值数值
|
||||
private _moveOffsetValue(idx) {
|
||||
let offset = cc.v2(0, 0);
|
||||
if (this.dir === ScrollDirection.Horizontal) {
|
||||
offset.x = this._scrollCenterOffsetX[idx];
|
||||
}
|
||||
else if (this.dir === ScrollDirection.Vertical) {
|
||||
offset.y = this._scrollCenterOffsetY[idx];
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
protected layout_items(start:number)
|
||||
{
|
||||
super.layout_items(start);
|
||||
|
||||
const scrollWidth = this.width;
|
||||
const scrollHeight = this.height;
|
||||
for (let i = 0, len = this.packItems.length; i < len; ++i) {
|
||||
//居中显示每个page
|
||||
if (this.dir === ScrollDirection.Horizontal) {
|
||||
this._scrollCenterOffsetX[i] = Math.abs(this.packItems[i].x) - (Math.abs(scrollWidth - this.packItems[i].width) >> 1);
|
||||
}
|
||||
else {
|
||||
this._scrollCenterOffsetY[i] = Math.abs(this.packItems[i].y) - (Math.abs(scrollHeight - this.packItems[i].height) >> 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remove_data(index:number, count:number = 1)
|
||||
{
|
||||
super.remove_data(index, count);
|
||||
|
||||
//选中项被删除
|
||||
const maxPageIdx = this.packItems.length - 1;
|
||||
if(this._curPageIdx > maxPageIdx)
|
||||
{
|
||||
this.scroll_to_page(maxPageIdx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
destroy()
|
||||
{
|
||||
this._onTurning = null;
|
||||
this._onScrolling = null;
|
||||
this._scrollCenterOffsetX = null;
|
||||
this._scrollCenterOffsetY = null;
|
||||
if(this._timer) {
|
||||
TimerMgr.getInst().remove(this._timer);
|
||||
this._timer = null;
|
||||
}
|
||||
super.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
export interface ScrollPageParams extends ScrollViewParams
|
||||
{
|
||||
on_turning?:(pageIndex:number) => void;
|
||||
on_scrolling?:Action1;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
96
components/scrollviewitem.ts
Normal file
96
components/scrollviewitem.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
const {ccclass, property} = cc._decorator;
|
||||
@ccclass
|
||||
export class ScrollViewItem extends cc.Component
|
||||
{
|
||||
// constructor()
|
||||
// {
|
||||
// super();
|
||||
// }
|
||||
|
||||
onInit(key:string)
|
||||
{
|
||||
// cc.log("item初如化");
|
||||
}
|
||||
|
||||
onUnInit(key:string)
|
||||
{
|
||||
// cc.log("item析构");
|
||||
}
|
||||
|
||||
onSetData(key:string, data:any, index:number, is_measure:boolean):[number, number]
|
||||
{
|
||||
// cc.log("item设置数据");
|
||||
return [0, 0];
|
||||
}
|
||||
|
||||
onSetSelect(key:string, is_select:boolean, index:number)
|
||||
{
|
||||
// cc.log("item选中状态改变");
|
||||
}
|
||||
|
||||
onRecycle(key:string, data:any, is_measure:boolean)
|
||||
{
|
||||
// cc.log("item被回收");
|
||||
}
|
||||
|
||||
onSelected(key:string, data:any, index:number)
|
||||
{
|
||||
// cc.log("item被选中");
|
||||
}
|
||||
|
||||
onTouchEnd(key:string, data:any, touchPos:cc.Vec2, index:number)
|
||||
{
|
||||
// cc.log("item非滑动状态下被点击");
|
||||
}
|
||||
|
||||
onBecameInvisible()
|
||||
{
|
||||
// cc.log("item从父节点移除或不可见");
|
||||
}
|
||||
|
||||
// get top()
|
||||
// {
|
||||
// return this.node.y + (1 - this.node.anchorY) * this.node.height;
|
||||
// }
|
||||
|
||||
// get bottom()
|
||||
// {
|
||||
// return this.node.y - this.node.anchorY * this.node.height;
|
||||
// }
|
||||
|
||||
// get left()
|
||||
// {
|
||||
// return this.node.x - this.node.anchorX * this.node.width;
|
||||
// }
|
||||
|
||||
// get right()
|
||||
// {
|
||||
// return this.node.x + (1 - this.node.anchorX) * this.node.width;
|
||||
// }
|
||||
|
||||
// set top(value:number)
|
||||
// {
|
||||
// this.node.y = value - (1 - this.node.anchorY) * this.node.height;
|
||||
// }
|
||||
|
||||
// set bottom(value:number)
|
||||
// {
|
||||
// this.node.y = value + this.node.anchorY * this.node.height;
|
||||
// }
|
||||
|
||||
// set left(value:number)
|
||||
// {
|
||||
// this.node.x = value + this.node.anchorX * this.node.width;
|
||||
// }
|
||||
|
||||
// set right(value:number)
|
||||
// {
|
||||
// this.node.x = value - (1 - this.node.anchorX) * this.node.width;
|
||||
// }
|
||||
|
||||
setLeftTop(left:number, top:number)
|
||||
{
|
||||
this.node.x = left + this.node.anchorX * this.node.width;
|
||||
this.node.y = top - (1 - this.node.anchorY) * this.node.height;
|
||||
}
|
||||
}
|
||||
164
components/star_rank.ts
Normal file
164
components/star_rank.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
type StarOnTouchEnd = (index:number) => void;
|
||||
|
||||
const {ccclass, property} = cc._decorator;
|
||||
@ccclass
|
||||
export class StarRank extends cc.Component {
|
||||
|
||||
@property({type: cc.SpriteFrame})
|
||||
lightStarSpriteFrame: cc.SpriteFrame = null;
|
||||
|
||||
@property({type: cc.SpriteFrame})
|
||||
darkStarSpriteFrame: cc.SpriteFrame = null;
|
||||
|
||||
@property
|
||||
spacing = 0;
|
||||
|
||||
@property
|
||||
cellSize = cc.size(10, 10);
|
||||
|
||||
@property({tooltip: "1 for horizontal, 2 for vertical"})
|
||||
direction = 1;
|
||||
|
||||
private _comps:cc.Sprite[];
|
||||
private _compPool:cc.Sprite[];
|
||||
private _totalCnt:number;
|
||||
private _lightCnt:number;
|
||||
private _touchCb:StarOnTouchEnd;
|
||||
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
// onLoad()
|
||||
// {
|
||||
// this.setTotalCnt(5);
|
||||
// this.setLightCnt(1);
|
||||
// cc.log(this.node.x, this.node.y)
|
||||
// }
|
||||
|
||||
onDestroy()
|
||||
{
|
||||
this._comps = null;
|
||||
this._compPool = null;
|
||||
this._touchCb = null;
|
||||
}
|
||||
|
||||
setSpacing(value:number)
|
||||
{
|
||||
this.spacing = value;
|
||||
}
|
||||
|
||||
setDirection(value:number)
|
||||
{
|
||||
this.direction = value;
|
||||
}
|
||||
|
||||
setTotalCnt(value:number)
|
||||
{
|
||||
if(value < 0)
|
||||
{
|
||||
cc.log(`invalid totalStar value`);
|
||||
return;
|
||||
}
|
||||
if(value == this._totalCnt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this._comps)
|
||||
{
|
||||
this._comps = [];
|
||||
this._compPool = [];
|
||||
this._totalCnt = 0;
|
||||
}
|
||||
|
||||
for(let i = this._totalCnt; i < value; i++)
|
||||
{
|
||||
let comp = this._compPool.pop();
|
||||
if(!comp)
|
||||
{
|
||||
const node = new cc.Node();
|
||||
node.width = this.cellSize.width;
|
||||
node.height = this.cellSize.height;
|
||||
comp = node.addComponent(cc.Sprite);
|
||||
}
|
||||
comp.node.parent = this.node;
|
||||
this._comps[i] = comp;
|
||||
comp.node.on(cc.Node.EventType.TOUCH_END, event => {
|
||||
if(this._touchCb) {
|
||||
this._touchCb(i);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
for(let i = this._totalCnt - 1; i >= value; i--)
|
||||
{
|
||||
const comp = this._comps.pop();
|
||||
comp.node.targetOff(this);
|
||||
comp.node.removeFromParent();
|
||||
this._compPool.push(comp);
|
||||
}
|
||||
this._totalCnt = value;
|
||||
|
||||
this.updateLayout();
|
||||
}
|
||||
|
||||
private updateLayout()
|
||||
{
|
||||
//以锚点(0.5, 0.5)计算父容器和子节点位置
|
||||
if(this.direction == 1)
|
||||
{
|
||||
const deltaX = this.cellSize.width + this.spacing;
|
||||
this.node.width = this._totalCnt * deltaX;
|
||||
this.node.height = this.cellSize.height;
|
||||
this._comps.forEach((comp, idx) => {
|
||||
comp.node.x = idx * deltaX - 0.5 * (this._totalCnt - 1) * deltaX;
|
||||
comp.node.y = 0;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
const deltaY = this.cellSize.height + this.spacing;
|
||||
this.node.width = this.cellSize.width;
|
||||
this.node.height = this._totalCnt * deltaY;
|
||||
this._comps.forEach((comp, idx) => {
|
||||
comp.node.x = 0;
|
||||
comp.node.y = idx * deltaY - 0.5 * (this._totalCnt - 1) * deltaY;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setLightCnt(value:number)
|
||||
{
|
||||
if(value < 0 || value > this._totalCnt)
|
||||
{
|
||||
cc.log(`invalid lightStar value`);
|
||||
return;
|
||||
}
|
||||
if(value == this._lightCnt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this._lightCnt = value;
|
||||
|
||||
for(let i = 0; i < this._totalCnt; i++)
|
||||
{
|
||||
this._comps[i].spriteFrame = i < value ? this.lightStarSpriteFrame : this.darkStarSpriteFrame;
|
||||
}
|
||||
}
|
||||
|
||||
getLightCnt()
|
||||
{
|
||||
return this._lightCnt || 0;
|
||||
}
|
||||
|
||||
setTouchCb(cb:StarOnTouchEnd)
|
||||
{
|
||||
this._touchCb = cb;
|
||||
}
|
||||
|
||||
getComp(index:number)
|
||||
{
|
||||
return this._comps[index];
|
||||
}
|
||||
}
|
||||
75
components/tabpage.ts
Normal file
75
components/tabpage.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { EventTool } from "../event/event_tool";
|
||||
|
||||
export class TabPage extends EventTool
|
||||
{
|
||||
protected _node:cc.Node;
|
||||
protected _parent:cc.Node;
|
||||
protected _isSelected:boolean;
|
||||
|
||||
constructor(node:cc.Node)
|
||||
{
|
||||
super();
|
||||
this._node = node;
|
||||
this.onInit();
|
||||
}
|
||||
|
||||
onInit()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
setParent(parent:cc.Node)
|
||||
{
|
||||
this._parent = parent;
|
||||
}
|
||||
|
||||
alignToParent()
|
||||
{
|
||||
this._node.setPosition(0, 0);
|
||||
}
|
||||
|
||||
setSelected(value:boolean)
|
||||
{
|
||||
if(this._isSelected == value) {
|
||||
return;
|
||||
}
|
||||
this._isSelected = value;
|
||||
|
||||
if(value && !this._node.parent) {
|
||||
this._node.parent = this._parent;
|
||||
this.onBecameVisible();
|
||||
}
|
||||
else if(!value && this._node.parent) {
|
||||
this._node.removeFromParent(false);
|
||||
this.onBecameInvisible();
|
||||
}
|
||||
}
|
||||
|
||||
protected onBecameVisible()
|
||||
{
|
||||
}
|
||||
|
||||
protected onBecameInvisible()
|
||||
{
|
||||
}
|
||||
|
||||
get selected()
|
||||
{
|
||||
return this._isSelected;
|
||||
}
|
||||
|
||||
destroy()
|
||||
{
|
||||
this.onDestroy();
|
||||
this._parent = null;
|
||||
if(cc.isValid(this._node)) {
|
||||
this._node.destroy();
|
||||
this._node = null;
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
202
components/tabview.ts
Normal file
202
components/tabview.ts
Normal file
@@ -0,0 +1,202 @@
|
||||
import { loader_mgr } from "../loader/loader_mgr";
|
||||
import { TabPage } from "./tabpage";
|
||||
import { gen_handler } from "../util";
|
||||
|
||||
export class TabView
|
||||
{
|
||||
private _tabContainer:cc.Node;
|
||||
private _pageContainer:cc.Node;
|
||||
private _tabBg:cc.Node;
|
||||
private _pageParamMap:Map<string, PageParams>;
|
||||
private _btnMap:Map<string, cc.Node>;
|
||||
private _pageMap:Map<string, TabPage>;
|
||||
private _loadingMap:Map<string, boolean>;
|
||||
private _selectedTab:string;
|
||||
private _onTabSelected:TabSelectedCb;
|
||||
|
||||
constructor(params:TabParams)
|
||||
{
|
||||
this._onTabSelected = params.onTabSelected;
|
||||
this._pageParamMap = new Map();
|
||||
this._btnMap = new Map();
|
||||
this._pageMap = new Map();
|
||||
this._loadingMap = new Map();
|
||||
|
||||
this._tabContainer = params.tabContainer;
|
||||
this._pageContainer = params.pageContainer;
|
||||
this._tabBg = params.tabBg;
|
||||
params.pages.forEach(page => {
|
||||
const btn = this._tabContainer.getChildByName(page.btnPath);
|
||||
const normal = btn.getChildByName("normal");
|
||||
const select = btn.getChildByName("select");
|
||||
normal.active = true;
|
||||
select.active = false;
|
||||
this._btnMap.set(page.key, btn);
|
||||
this._pageParamMap.set(page.key, page);
|
||||
|
||||
normal.on(cc.Node.EventType.TOUCH_END, this.select.bind(this, page.key), this);
|
||||
if(page.btnTxt) {
|
||||
normal.getChildByName("txt").getComponent(cc.Label).string = page.btnTxt;
|
||||
select.getChildByName("txt").getComponent(cc.Label).string = page.btnTxt;
|
||||
}
|
||||
});
|
||||
|
||||
if(params.initTab) {
|
||||
this.select(params.initTab);
|
||||
}
|
||||
}
|
||||
|
||||
select(key:string)
|
||||
{
|
||||
if(key == this._selectedTab) {
|
||||
return;
|
||||
}
|
||||
|
||||
const param = this._pageParamMap.get(key);
|
||||
if(!param) {
|
||||
cc.log(`TabView, select, invalid tabKey=${key}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if(this._selectedTab) {
|
||||
this.setBtnSelected(this._selectedTab, false);
|
||||
this.setPageSelected(this._selectedTab, false);
|
||||
}
|
||||
this.setBtnSelected(key, true);
|
||||
this._selectedTab = key;
|
||||
if(this._onTabSelected) {
|
||||
this._onTabSelected(key);
|
||||
}
|
||||
|
||||
//adjust tab btns z-index
|
||||
if(this._tabBg) {
|
||||
this.adjustOrder();
|
||||
}
|
||||
|
||||
//load tabpage
|
||||
const page = this._pageMap.get(key);
|
||||
if(page) {
|
||||
page.setSelected(true);
|
||||
}
|
||||
else {
|
||||
const isLoading = this._loadingMap.get(key);
|
||||
if(isLoading) {
|
||||
return;
|
||||
}
|
||||
this._loadingMap.set(key, true);
|
||||
loader_mgr.get_inst().loadRes(param.resPath, gen_handler((pKey:string, res:cc.Prefab) => {
|
||||
//已经destroy了
|
||||
if(!this._loadingMap) {
|
||||
return;
|
||||
}
|
||||
this._loadingMap.delete(pKey);
|
||||
if(this._selectedTab != pKey) {
|
||||
cc.log(`TabView, loadPage done, selectedTab=${this._selectedTab}, argTab=${pKey}`);
|
||||
return;
|
||||
}
|
||||
const node = cc.instantiate(res);
|
||||
const pageInst = new param.clazz(node);
|
||||
pageInst.setParent(this._pageContainer);
|
||||
pageInst.alignToParent();
|
||||
pageInst.setSelected(true);
|
||||
this._pageMap.set(pKey, pageInst);
|
||||
}, this, key));
|
||||
}
|
||||
}
|
||||
|
||||
getPage(key:string)
|
||||
{
|
||||
return this._pageMap.get(key);
|
||||
}
|
||||
|
||||
getSelectedPage()
|
||||
{
|
||||
if(this._selectedTab) {
|
||||
return this._pageMap.get(this._selectedTab);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private setBtnSelected(key:string, selected:boolean)
|
||||
{
|
||||
const btn = this._btnMap.get(key);
|
||||
const normal = btn.getChildByName("normal");
|
||||
const select = btn.getChildByName("select");
|
||||
normal.active = !selected;
|
||||
select.active = selected;
|
||||
}
|
||||
|
||||
private setPageSelected(key:string, selected:boolean)
|
||||
{
|
||||
const page = this._pageMap.get(key);
|
||||
if(page) {
|
||||
page.setSelected(selected);
|
||||
}
|
||||
}
|
||||
|
||||
private adjustOrder()
|
||||
{
|
||||
const selectedBtn = this._btnMap.get(this._selectedTab);
|
||||
const childCnt = this._tabContainer.childrenCount;
|
||||
selectedBtn.setSiblingIndex(childCnt - 1);
|
||||
this._tabBg.setSiblingIndex(childCnt - 2);
|
||||
}
|
||||
|
||||
updateBtnTxts(values:{key:string, txt:string}[])
|
||||
{
|
||||
values.forEach(v => {
|
||||
const btn = this._btnMap.get(v.key);
|
||||
if(btn) {
|
||||
btn.getChildByName("normal").getChildByName("txt").getComponent(cc.Label).string = v.txt;
|
||||
btn.getChildByName("select").getChildByName("txt").getComponent(cc.Label).string = v.txt;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
destroy()
|
||||
{
|
||||
this._pageParamMap.clear();
|
||||
this._pageParamMap = null;
|
||||
|
||||
this._btnMap.forEach(btn => {
|
||||
const normal = btn.getChildByName("normal");
|
||||
normal.off(cc.Node.EventType.TOUCH_END);
|
||||
});
|
||||
this._btnMap.clear();
|
||||
this._btnMap = null;
|
||||
|
||||
this._pageMap.forEach(page => {
|
||||
page.destroy();
|
||||
});
|
||||
this._pageMap.clear();
|
||||
this._pageMap = null;
|
||||
|
||||
this._loadingMap.clear();
|
||||
this._loadingMap = null;
|
||||
|
||||
this._tabContainer = null;
|
||||
this._pageContainer = null;
|
||||
this._tabBg = null;
|
||||
this._selectedTab = null;
|
||||
this._onTabSelected = null;
|
||||
}
|
||||
}
|
||||
|
||||
export type TabSelectedCb = (key:string) => void;
|
||||
|
||||
type TabParams = {
|
||||
tabContainer:cc.Node;
|
||||
pageContainer:cc.Node;
|
||||
tabBg?:cc.Node;
|
||||
pages:PageParams[];
|
||||
initTab?:string;
|
||||
onTabSelected?:TabSelectedCb;
|
||||
}
|
||||
|
||||
type PageParams = {
|
||||
key:string;
|
||||
btnPath:string;
|
||||
btnTxt?:string;
|
||||
resPath:string;
|
||||
clazz:new(node:cc.Node) => TabPage;
|
||||
}
|
||||
361
effect/dragonbone_factory.ts
Normal file
361
effect/dragonbone_factory.ts
Normal file
@@ -0,0 +1,361 @@
|
||||
import { loader_mgr } from "../loader/loader_mgr";
|
||||
import { gen_handler } from "../util";
|
||||
|
||||
type DB_EVENT_HANDLER = (event:dragonBones.EventObject, state:dragonBones.AnimationState) => void;
|
||||
const EVENT_DB_ASSET_LOADED = "EVENT_DB_ASSET_LOADED";
|
||||
const BasePathForSke = (path:string) => {
|
||||
return `dragonbones/${path}_ske`;
|
||||
};
|
||||
const BasePathForTex = (path:string) => {
|
||||
return `dragonbones/${path}_tex`;
|
||||
};
|
||||
|
||||
export class DragonBoneFactory
|
||||
{
|
||||
private static _inst:DragonBoneFactory;
|
||||
private _loadingMap:Map<string, dragonBones.ArmatureDisplay[]>
|
||||
private _eventMap:Map<string, cc.EventTarget>;
|
||||
private _stateMap:Map<string, dragonBones.AnimationState>;
|
||||
private _compPool:dragonBones.ArmatureDisplay[];
|
||||
private _refMap:Map<string, number>;
|
||||
private _resMap:Map<string, string>;
|
||||
|
||||
private constructor()
|
||||
{
|
||||
this._compPool = [];
|
||||
this._loadingMap = new Map();
|
||||
this._eventMap = new Map();
|
||||
this._stateMap = new Map();
|
||||
this._resMap = new Map();
|
||||
this._refMap = new Map();
|
||||
}
|
||||
|
||||
static getInst()
|
||||
{
|
||||
if(!this._inst)
|
||||
{
|
||||
this._inst = new DragonBoneFactory();
|
||||
}
|
||||
return this._inst;
|
||||
}
|
||||
|
||||
buildDB(params:ArmatureDisplayParams)
|
||||
{
|
||||
let comp = this._compPool.pop();
|
||||
let node:cc.Node;
|
||||
if(comp)
|
||||
{
|
||||
node = comp.node;
|
||||
}
|
||||
else
|
||||
{
|
||||
node = new cc.Node();
|
||||
comp = node.addComponent(dragonBones.ArmatureDisplay);
|
||||
//save reference count for auto release
|
||||
const path = params.path;
|
||||
this._resMap.set(comp.uuid, path);
|
||||
const refCnt = (this._refMap.get(path) || 0) + 1;
|
||||
this._refMap.set(path, refCnt);
|
||||
cc.log(`DragonBoneFactory, increaseRef, path=${path}, ref=${refCnt}`);
|
||||
}
|
||||
if(params.parent)
|
||||
{
|
||||
node.parent = params.parent;
|
||||
node.x = params.x || 0;
|
||||
node.y = params.y || 0;
|
||||
}
|
||||
node.active = params.active != null ? params.active : true;
|
||||
|
||||
// cc.log("buildArmatureDisplay", node.getContentSize(), node.getPosition(), node.getAnchorPoint());
|
||||
const path = params.path;
|
||||
const skePath = BasePathForSke(path);
|
||||
const texPath = BasePathForTex(path);
|
||||
const dragonBonesAsset = cc.loader.getRes(skePath, dragonBones.DragonBonesAsset);
|
||||
const dragonBonesAtlasAsset = cc.loader.getRes(texPath, dragonBones.DragonBonesAtlasAsset);
|
||||
const texture2D = cc.loader.getRes(texPath, cc.Texture2D);
|
||||
if(dragonBonesAsset && dragonBonesAtlasAsset && texture2D)
|
||||
{
|
||||
this.attachAsset(comp, params, dragonBonesAsset, dragonBonesAtlasAsset, texture2D);
|
||||
}
|
||||
else
|
||||
{
|
||||
const assets:DBAsset[] = [
|
||||
{path:skePath, alias:"dragonBonesAsset", type:dragonBones.DragonBonesAsset},
|
||||
{path:texPath, alias:"dragonBonesAtlasAsset", type:dragonBones.DragonBonesAtlasAsset},
|
||||
{path:texPath, alias:"texture2D", type:cc.Texture2D},
|
||||
];
|
||||
this.loadAsset(comp, params, assets);
|
||||
}
|
||||
return comp;
|
||||
}
|
||||
|
||||
private loadAsset(comp:dragonBones.ArmatureDisplay, params:ArmatureDisplayParams, assets:DBAsset[])
|
||||
{
|
||||
const path = params.path;
|
||||
|
||||
//同一路径龙骨资源只加载一次
|
||||
let loadings = this._loadingMap.get(path);
|
||||
if(!loadings)
|
||||
{
|
||||
loadings = [];
|
||||
this._loadingMap.set(path, loadings);
|
||||
}
|
||||
loadings.push(comp)
|
||||
if(loadings.length > 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// cc.log(`DragonBoneFactory, loadAsset, path=${path}`);
|
||||
loader_mgr.get_inst().loadResArray(assets.map(a => a.path), gen_handler(res => {
|
||||
const dragonBonesAsset = res[assets[0].alias];
|
||||
const dragonBonesAtlasAsset = res[assets[1].alias];
|
||||
const texture2D = res[assets[2].alias];
|
||||
|
||||
const lds = this._loadingMap.get(path);
|
||||
if(lds) {
|
||||
lds.forEach(p => {
|
||||
if(!p || !cc.isValid(p.node)) {
|
||||
return;
|
||||
}
|
||||
if(dragonBonesAsset && dragonBonesAtlasAsset && texture2D) {
|
||||
this.attachAsset(p, params, dragonBonesAsset, dragonBonesAtlasAsset, texture2D);
|
||||
p.node.emit(EVENT_DB_ASSET_LOADED);
|
||||
}
|
||||
});
|
||||
lds.length = 0;
|
||||
this._loadingMap.delete(path);
|
||||
}
|
||||
}), assets.map(a => a.type), assets.map(a => a.alias));
|
||||
}
|
||||
|
||||
private attachAsset(comp:dragonBones.ArmatureDisplay, params:ArmatureDisplayParams, dragonBonesAsset:dragonBones.DragonBonesAsset, dragonBonesAtlasAsset:dragonBones.DragonBonesAtlasAsset, texture2D:cc.Texture2D)
|
||||
{
|
||||
comp.dragonAsset = dragonBonesAsset;
|
||||
dragonBonesAtlasAsset.texture = texture2D;
|
||||
comp.dragonAtlasAsset = dragonBonesAtlasAsset;
|
||||
|
||||
comp.armatureName = params.armatureName;
|
||||
comp.addEventListener(dragonBones.EventObject.COMPLETE, event => {
|
||||
// cc.log("dragonbone complete event", event, comp.uuid);
|
||||
const state = this._stateMap.get(comp.uuid);
|
||||
if(params.onComplete) {
|
||||
params.onComplete(event, state);
|
||||
}
|
||||
else {
|
||||
this.invokeDBEvent(comp, dragonBones.EventObject.COMPLETE, event, state);
|
||||
}
|
||||
});
|
||||
comp.addEventListener(dragonBones.EventObject.START, event => {
|
||||
// cc.log("dragonbone start event", event, comp.uuid);
|
||||
const state = this._stateMap.get(comp.uuid);
|
||||
if(params.onStart) {
|
||||
params.onStart(event, state);
|
||||
}
|
||||
else {
|
||||
this.invokeDBEvent(comp, dragonBones.EventObject.START, event, state);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private decreaseRef(comp:dragonBones.ArmatureDisplay)
|
||||
{
|
||||
const path = this._resMap.get(comp.uuid);
|
||||
if(path)
|
||||
{
|
||||
this._resMap.delete(comp.uuid);
|
||||
const refCnt = (this._refMap.get(path) || 0) - 1;
|
||||
this._refMap.set(path, refCnt);
|
||||
cc.log(`DragonBoneFactory, decreaseRef, path=${path}, ref=${refCnt}`);
|
||||
if(refCnt == 0)
|
||||
{
|
||||
this._refMap.delete(path);
|
||||
this.releaseAsset(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
releaseDB(comp:dragonBones.ArmatureDisplay, recycle = false)
|
||||
{
|
||||
const uuid = comp.uuid;
|
||||
this._stateMap.delete(uuid);
|
||||
comp.node.off(EVENT_DB_ASSET_LOADED);
|
||||
comp.removeEventListener(dragonBones.EventObject.COMPLETE);
|
||||
this.removeDBEvent(comp, dragonBones.EventObject.COMPLETE);
|
||||
comp.removeEventListener(dragonBones.EventObject.START);
|
||||
this.removeDBEvent(comp, dragonBones.EventObject.START);
|
||||
comp.stopAnimation("");
|
||||
comp.dragonAtlasAsset = null;
|
||||
comp.dragonAsset = null;
|
||||
comp.armatureName = null;
|
||||
comp.animationName = null;
|
||||
const node = comp.node;
|
||||
if(recycle)
|
||||
{
|
||||
node.removeFromParent();
|
||||
this._compPool.push(comp);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.decreaseRef(comp);
|
||||
node.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
playDB(comp:dragonBones.ArmatureDisplay, animationName:string, playTimes:number = 1, armatureName?:string)
|
||||
{
|
||||
const node = comp.node;
|
||||
if(!node)
|
||||
{
|
||||
cc.warn(`db has no associated node, animationName=${animationName}`);
|
||||
return;
|
||||
}
|
||||
if(!node.activeInHierarchy)
|
||||
{
|
||||
cc.warn(`db associated node is inactive, animationName=${animationName}`);
|
||||
return;
|
||||
}
|
||||
if(!comp.dragonAsset)
|
||||
{
|
||||
// cc.log(`DragonBoneFactory, playDB async, animationName=${animationName}`);
|
||||
//ArmatureDisplay资源加载完成后才能监听事件,所以这里必须要用node监听
|
||||
comp.node.on(EVENT_DB_ASSET_LOADED, () => {
|
||||
if(armatureName)
|
||||
{
|
||||
comp.armatureName = armatureName;
|
||||
}
|
||||
const state = comp.playAnimation(animationName, playTimes);
|
||||
this._stateMap.set(comp.uuid, state);
|
||||
// cc.log(`DragonBoneFactory, onAssetLoaded, playDB, animationName=${animationName}`);
|
||||
});
|
||||
// cc.log("dragonbone play before loaded");
|
||||
return;
|
||||
}
|
||||
if(armatureName)
|
||||
{
|
||||
comp.armatureName = armatureName;
|
||||
}
|
||||
const state = comp.playAnimation(animationName, playTimes);
|
||||
this._stateMap.set(comp.uuid, state);
|
||||
// cc.log(`DragonBoneFactory, playDB, animationName=${animationName}`);
|
||||
}
|
||||
|
||||
addStartEvent(comp:dragonBones.ArmatureDisplay, handler:DB_EVENT_HANDLER, target?:any)
|
||||
{
|
||||
this.addDBEvent(comp, dragonBones.EventObject.START, handler, target);
|
||||
}
|
||||
|
||||
onceStartEvent(comp:dragonBones.ArmatureDisplay, handler:DB_EVENT_HANDLER, target?:any)
|
||||
{
|
||||
this.onceDBEvent(comp, dragonBones.EventObject.START, handler, target);
|
||||
}
|
||||
|
||||
removeStartEvent(comp:dragonBones.ArmatureDisplay, handler?:DB_EVENT_HANDLER, target?:any)
|
||||
{
|
||||
this.removeDBEvent(comp, dragonBones.EventObject.START, handler, target);
|
||||
}
|
||||
|
||||
addCompleteEvent(comp:dragonBones.ArmatureDisplay, handler:DB_EVENT_HANDLER, target?:any)
|
||||
{
|
||||
this.addDBEvent(comp, dragonBones.EventObject.COMPLETE, handler, target);
|
||||
}
|
||||
|
||||
onceCompleteEvent(comp:dragonBones.ArmatureDisplay, handler:DB_EVENT_HANDLER, target?:any)
|
||||
{
|
||||
this.onceDBEvent(comp, dragonBones.EventObject.COMPLETE, handler, target);
|
||||
}
|
||||
|
||||
removeCompleteEvent(comp:dragonBones.ArmatureDisplay, handler?:DB_EVENT_HANDLER, target?:any)
|
||||
{
|
||||
this.removeDBEvent(comp, dragonBones.EventObject.COMPLETE, handler, target);
|
||||
}
|
||||
|
||||
//ArmatureDisplay资源加载完成前无法监听事件,用此方法可代替
|
||||
addDBEvent(comp:dragonBones.ArmatureDisplay, eventType:string, handler:DB_EVENT_HANDLER, target?:any)
|
||||
{
|
||||
const key = comp.uuid + "_" + eventType;
|
||||
let eventTarget = this._eventMap.get(key);
|
||||
if(!eventTarget)
|
||||
{
|
||||
eventTarget = new cc.EventTarget();
|
||||
this._eventMap.set(key, eventTarget);
|
||||
}
|
||||
return eventTarget.on(eventType, handler, target);
|
||||
}
|
||||
|
||||
onceDBEvent(comp:dragonBones.ArmatureDisplay, eventType:string, handler:DB_EVENT_HANDLER, target?:any)
|
||||
{
|
||||
const key = comp.uuid + "_" + eventType;
|
||||
let eventTarget = this._eventMap.get(key);
|
||||
if(!eventTarget)
|
||||
{
|
||||
eventTarget = new cc.EventTarget();
|
||||
this._eventMap.set(key, eventTarget);
|
||||
}
|
||||
return eventTarget.once(eventType, handler, target);
|
||||
}
|
||||
|
||||
removeDBEvent(comp:dragonBones.ArmatureDisplay, eventType:string, handler?:DB_EVENT_HANDLER, target?:any)
|
||||
{
|
||||
const key = comp.uuid + "_" + eventType;
|
||||
const eventTarget = this._eventMap.get(key);
|
||||
if(!eventTarget)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this._eventMap.delete(key);
|
||||
eventTarget.off(eventType, handler, target);
|
||||
}
|
||||
|
||||
private invokeDBEvent(comp:dragonBones.ArmatureDisplay, eventType:string, ...params)
|
||||
{
|
||||
const key = comp.uuid + "_" + eventType;
|
||||
const eventTarget = this._eventMap.get(key);
|
||||
if(!eventTarget)
|
||||
{
|
||||
return;
|
||||
}
|
||||
eventTarget.emit(eventType, ...params);
|
||||
}
|
||||
|
||||
releaseAsset(path:string)
|
||||
{
|
||||
const skePath = BasePathForSke(path);
|
||||
const texPath = BasePathForTex(path);
|
||||
cc.loader.releaseRes(skePath, dragonBones.DragonBonesAsset);
|
||||
cc.loader.releaseRes(texPath, dragonBones.DragonBonesAtlasAsset);
|
||||
cc.loader.releaseRes(texPath, cc.Texture2D);
|
||||
cc.log(`DragonBoneFactory, releaseAsset, path=${skePath}`);
|
||||
}
|
||||
|
||||
releaseAll()
|
||||
{
|
||||
this._loadingMap.clear();
|
||||
this._eventMap.clear();
|
||||
this._stateMap.clear();
|
||||
this._compPool.forEach(comp => {
|
||||
this.decreaseRef(comp);
|
||||
comp.destroy();
|
||||
});
|
||||
this._compPool.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
interface ArmatureDisplayParams
|
||||
{
|
||||
path:string; //龙骨导出文件路径
|
||||
armatureName:string; //骨架名称
|
||||
onComplete?:DB_EVENT_HANDLER;
|
||||
onStart?:DB_EVENT_HANDLER;
|
||||
parent?:cc.Node;
|
||||
x?:number;
|
||||
y?:number;
|
||||
active?:boolean;
|
||||
}
|
||||
|
||||
interface DBAsset
|
||||
{
|
||||
path:string;
|
||||
alias:string;
|
||||
type:typeof cc.Asset;
|
||||
}
|
||||
195
effect/particle_factory.ts
Normal file
195
effect/particle_factory.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
import { loader_mgr } from "../loader/loader_mgr";
|
||||
import { gen_handler } from "../util";
|
||||
|
||||
const EVENT_PARTICLE_ASSET_LOADED = "EVENT_PARTICLE_ASSET_LOADED";
|
||||
const BasePathForParticle = (path:string) => {
|
||||
return `particles/${path}`;
|
||||
};
|
||||
|
||||
export class ParticleFactory {
|
||||
private static _inst:ParticleFactory;
|
||||
private _compPool:cc.ParticleSystem[];
|
||||
private _loadingMap:Map<string, cc.ParticleSystem[]>;
|
||||
private _refMap:Map<string, number>;
|
||||
private _resMap:Map<string, string>;
|
||||
|
||||
private constructor() {
|
||||
this._compPool = [];
|
||||
this._loadingMap = new Map();
|
||||
this._resMap = new Map();
|
||||
this._refMap = new Map();
|
||||
}
|
||||
|
||||
static getInst() {
|
||||
if(!this._inst) {
|
||||
this._inst = new ParticleFactory();
|
||||
}
|
||||
return this._inst;
|
||||
}
|
||||
|
||||
buildParticle(params:ParticleParams) {
|
||||
let comp = this._compPool.pop();
|
||||
let node:cc.Node;
|
||||
if(comp) {
|
||||
node = comp.node;
|
||||
}
|
||||
else {
|
||||
node = new cc.Node();
|
||||
comp = node.addComponent(cc.ParticleSystem);
|
||||
//save reference count for auto release
|
||||
const path = params.path;
|
||||
this._resMap.set(comp.uuid, path);
|
||||
const refCnt = (this._refMap.get(path) || 0) + 1;
|
||||
this._refMap.set(path, refCnt);
|
||||
cc.log(`ParticleFactory, increaseRef, path=${path}, ref=${refCnt}`);
|
||||
}
|
||||
|
||||
comp.playOnLoad = false;
|
||||
comp.autoRemoveOnFinish = false;
|
||||
|
||||
if(params.parent) {
|
||||
node.parent = params.parent;
|
||||
node.x = params.x || 0;
|
||||
node.y = params.y || 0;
|
||||
}
|
||||
|
||||
const path = params.path;
|
||||
const basePath = BasePathForParticle(path);
|
||||
const particleAsset:cc.ParticleAsset = cc.loader.getRes(basePath, cc.ParticleAsset);
|
||||
const spriteFrame:cc.SpriteFrame = cc.loader.getRes(basePath, cc.SpriteFrame);
|
||||
if(particleAsset && spriteFrame) {
|
||||
this.attachAsset(comp, particleAsset, spriteFrame);
|
||||
}
|
||||
else {
|
||||
const assets:ParticleAsset[] = [
|
||||
{path:basePath, alias:"particleAsset", type:cc.ParticleAsset},
|
||||
{path:basePath, alias:"spriteFrame", type:cc.SpriteFrame},
|
||||
];
|
||||
this.loadAsset(comp, path, assets);
|
||||
}
|
||||
return comp;
|
||||
}
|
||||
|
||||
private loadAsset(comp:cc.ParticleSystem, path:string, assets:ParticleAsset[]) {
|
||||
//同一路径资源只加载一次
|
||||
let loadings = this._loadingMap.get(path);
|
||||
if(!loadings) {
|
||||
loadings = [];
|
||||
this._loadingMap.set(path, loadings);
|
||||
}
|
||||
loadings.push(comp);
|
||||
if(loadings.length > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
cc.log(`ParticleFactory, loadAsset, path=${assets[0].path}`);
|
||||
loader_mgr.get_inst().loadResArray(assets.map(a => a.path), gen_handler(res => {
|
||||
const particleAsset:cc.ParticleAsset = res[assets[0].alias];
|
||||
const spriteFrame:cc.SpriteFrame = res[assets[1].alias];
|
||||
|
||||
const lds = this._loadingMap.get(path);
|
||||
if(lds) {
|
||||
lds.forEach(p => {
|
||||
if(!p || !cc.isValid(p.node)) {
|
||||
return;
|
||||
}
|
||||
if(particleAsset && spriteFrame) {
|
||||
this.attachAsset(p, particleAsset, spriteFrame);
|
||||
p.node.emit(EVENT_PARTICLE_ASSET_LOADED);
|
||||
}
|
||||
});
|
||||
lds.length = 0;
|
||||
this._loadingMap.delete(path);
|
||||
}
|
||||
}), assets.map(a => a.type), assets.map(a => a.alias));
|
||||
}
|
||||
|
||||
private attachAsset(comp:cc.ParticleSystem, particleAsset:cc.ParticleAsset, spriteFrame:cc.SpriteFrame) {
|
||||
comp.file = particleAsset;
|
||||
comp.spriteFrame = spriteFrame;
|
||||
}
|
||||
|
||||
play(comp:cc.ParticleSystem) {
|
||||
const node = comp.node;
|
||||
if(!node) {
|
||||
cc.warn(`cc.ParticleSystem has no associated node`);
|
||||
return;
|
||||
}
|
||||
if(!node.activeInHierarchy) {
|
||||
cc.warn(`cc.ParticleSystem associated node is inactive`);
|
||||
return;
|
||||
}
|
||||
if(!comp.file) {
|
||||
cc.log(`ParticleSystem, play async`);
|
||||
comp.node.on(EVENT_PARTICLE_ASSET_LOADED, () => {
|
||||
comp.resetSystem();
|
||||
cc.log(`ParticleSystem, onAssetLoaded play`);
|
||||
});
|
||||
return;
|
||||
}
|
||||
comp.resetSystem();
|
||||
cc.log(`ParticleSystem, play`);
|
||||
}
|
||||
|
||||
private decreaseRef(comp:cc.ParticleSystem)
|
||||
{
|
||||
const path = this._resMap.get(comp.uuid);
|
||||
if(path)
|
||||
{
|
||||
this._resMap.delete(comp.uuid);
|
||||
const refCnt = (this._refMap.get(path) || 0) - 1;
|
||||
this._refMap.set(path, refCnt);
|
||||
cc.log(`ParticleFactory, decreaseRef, path=${path}, ref=${refCnt}`);
|
||||
if(refCnt == 0)
|
||||
{
|
||||
this._refMap.delete(path);
|
||||
this.releaseAsset(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
releaseParticle(comp:cc.ParticleSystem, recycle = false) {
|
||||
comp.node.off(EVENT_PARTICLE_ASSET_LOADED);
|
||||
comp.stopSystem();
|
||||
comp.file = null;
|
||||
comp.spriteFrame = null;
|
||||
const node = comp.node;
|
||||
if(recycle) {
|
||||
node.removeFromParent();
|
||||
this._compPool.push(comp);
|
||||
}
|
||||
else {
|
||||
this.decreaseRef(comp);
|
||||
node.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
releaseAsset(path:string) {
|
||||
const basePath = BasePathForParticle(path);
|
||||
cc.loader.releaseRes(basePath, cc.ParticleAsset);
|
||||
cc.loader.releaseRes(basePath, cc.SpriteFrame);
|
||||
cc.log(`ParticleFactory, releaseAsset, path=${basePath}`);
|
||||
}
|
||||
|
||||
releaseAll() {
|
||||
this._loadingMap.clear();
|
||||
this._compPool.forEach(comp => {
|
||||
this.decreaseRef(comp);
|
||||
comp.destroy();
|
||||
});
|
||||
this._compPool.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
interface ParticleParams {
|
||||
path:string; //plist文件路径
|
||||
parent?:cc.Node;
|
||||
x?:number;
|
||||
y?:number;
|
||||
}
|
||||
|
||||
interface ParticleAsset {
|
||||
path:string;
|
||||
alias:string;
|
||||
type:typeof cc.Asset;
|
||||
}
|
||||
@@ -1,84 +1,319 @@
|
||||
export class event_mgr
|
||||
{
|
||||
private static inst:event_mgr;
|
||||
private listeners:any; //Event_Name => cb[]
|
||||
|
||||
private constructor()
|
||||
{
|
||||
this.listeners = {};
|
||||
}
|
||||
|
||||
static get_inst():event_mgr
|
||||
{
|
||||
if(!this.inst)
|
||||
{
|
||||
this.inst = new event_mgr();
|
||||
}
|
||||
return this.inst;
|
||||
}
|
||||
|
||||
fire(event:Event_Name, ...params:any[]):void
|
||||
{
|
||||
let cbs:any[] = this.listeners[event];
|
||||
if(!cbs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for(let i:number = 0, len:number = cbs.length; i < len; i += 2)
|
||||
{
|
||||
let cb:any = cbs[i];
|
||||
let host:any = cbs[i+1];
|
||||
cb.call(host, ...params);
|
||||
}
|
||||
}
|
||||
|
||||
add(event:Event_Name, cb:Function, host:any = null):void
|
||||
{
|
||||
let cbs:any[] = this.listeners[event];
|
||||
if(!cbs)
|
||||
{
|
||||
this.listeners[event] = cbs = [];
|
||||
}
|
||||
cbs.push(cb, host);
|
||||
}
|
||||
|
||||
remove(event:Event_Name, cb:Function)
|
||||
{
|
||||
let cbs:any[] = this.listeners[event];
|
||||
if(!cbs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
let index:number = cbs.indexOf(cb);
|
||||
if(index < 0)
|
||||
{
|
||||
cc.warn("event_mgr remove", event, ", but cb not exists!");
|
||||
return;
|
||||
}
|
||||
cbs.splice(index, 2);
|
||||
}
|
||||
|
||||
clear()
|
||||
{
|
||||
for(let key in this.listeners)
|
||||
{
|
||||
this.listeners[key].length = 0;
|
||||
}
|
||||
this.listeners = {};
|
||||
}
|
||||
}
|
||||
|
||||
/**事件名称定义*/
|
||||
export enum Event_Name {
|
||||
USER_INFO_CHANGED,
|
||||
COIN_CHANGED,
|
||||
PASS_CHANGED,
|
||||
GRADE_CHANGED,
|
||||
SHAREHINT_CHANGED,
|
||||
GAME_LEVEL_CHANGED,
|
||||
UI_SHOW,
|
||||
UI_HIDE,
|
||||
LEVEL_DATA_LOADED,
|
||||
MUTE_MUSIC,
|
||||
UNMUTE_MUSIC,
|
||||
export type MyEvnetHandler = (...params) => void;
|
||||
|
||||
class MyEventListeners
|
||||
{
|
||||
public handlers:MyEvnetHandler[];
|
||||
public targets:any[];
|
||||
public isInvoking:boolean;
|
||||
private containCanceled:boolean;
|
||||
|
||||
constructor()
|
||||
{
|
||||
this.handlers = [];
|
||||
this.targets = [];
|
||||
this.isInvoking = false;
|
||||
this.containCanceled = false;
|
||||
}
|
||||
|
||||
add(handler:MyEvnetHandler, target)
|
||||
{
|
||||
this.handlers.push(handler);
|
||||
this.targets.push(target);
|
||||
}
|
||||
|
||||
remove(index:number)
|
||||
{
|
||||
this.handlers.splice(index, 1);
|
||||
this.targets.splice(index, 1);
|
||||
}
|
||||
|
||||
removeByTarget(target)
|
||||
{
|
||||
const targets = this.targets;
|
||||
const handlers = this.handlers;
|
||||
for(let i = targets.length - 1; i >= 0; i--) {
|
||||
if(targets[i] == target) {
|
||||
targets.splice(i, 1);
|
||||
handlers.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeByHandler(handler:MyEvnetHandler)
|
||||
{
|
||||
const handlers = this.handlers;
|
||||
const targets = this.targets;
|
||||
for(let i = handlers.length - 1; i >= 0; i--) {
|
||||
if(handlers[i] == handler) {
|
||||
handlers.splice(i, 1);
|
||||
targets.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeByHandlerTarget(handler:MyEvnetHandler, target)
|
||||
{
|
||||
const handlers = this.handlers;
|
||||
const targets = this.targets;
|
||||
for(let i = handlers.length - 1; i >= 0; i--) {
|
||||
if(handlers[i] == handler && targets[i] == target) {
|
||||
handlers.splice(i, 1);
|
||||
targets.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeAll()
|
||||
{
|
||||
this.handlers.length = 0;
|
||||
this.targets.length = 0;
|
||||
}
|
||||
|
||||
cancel(index:number)
|
||||
{
|
||||
this.handlers[index] = null;
|
||||
this.targets[index] = null;
|
||||
this.containCanceled = true;
|
||||
}
|
||||
|
||||
cancelByTarget(target)
|
||||
{
|
||||
const targets = this.targets;
|
||||
const handlers = this.handlers;
|
||||
for(let i = targets.length - 1; i >= 0; i--) {
|
||||
if(targets[i] == target) {
|
||||
targets[i] = null;
|
||||
handlers[i] = null;
|
||||
}
|
||||
}
|
||||
this.containCanceled = true;
|
||||
}
|
||||
|
||||
cancelByHandler(handler:MyEvnetHandler)
|
||||
{
|
||||
const handlers = this.handlers;
|
||||
const targets = this.targets;
|
||||
for(let i = handlers.length - 1; i >= 0; i--) {
|
||||
if(handlers[i] == handler) {
|
||||
handlers[i] = null;
|
||||
targets[i] = null;
|
||||
}
|
||||
}
|
||||
this.containCanceled = true;
|
||||
}
|
||||
|
||||
cancelByHandlerTarget(handler:MyEvnetHandler, target)
|
||||
{
|
||||
const handlers = this.handlers;
|
||||
const targets = this.targets;
|
||||
for(let i = handlers.length - 1; i >= 0; i--) {
|
||||
if(handlers[i] == handler && targets[i] == target) {
|
||||
handlers[i] = null;
|
||||
targets[i] = null;
|
||||
}
|
||||
}
|
||||
this.containCanceled = true;
|
||||
}
|
||||
|
||||
cancelAll()
|
||||
{
|
||||
const handlers = this.handlers;
|
||||
const targets = this.targets;
|
||||
for(let i = handlers.length - 1; i >= 0; i--) {
|
||||
handlers[i] = null;
|
||||
targets[i] = null;
|
||||
}
|
||||
this.containCanceled = true;
|
||||
}
|
||||
|
||||
has(handler:MyEvnetHandler, target)
|
||||
{
|
||||
const handlers = this.handlers;
|
||||
const targets = this.targets;
|
||||
for(let i = handlers.length - 1; i >= 0; i--) {
|
||||
if(handlers[i] == handler && targets[i] == target) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
purgeCanceled()
|
||||
{
|
||||
if(this.containCanceled) {
|
||||
this.removeByHandler(null);
|
||||
this.containCanceled = false;
|
||||
}
|
||||
}
|
||||
|
||||
isEmpty()
|
||||
{
|
||||
return this.handlers.length == 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class event_mgr
|
||||
{
|
||||
private static inst:event_mgr;
|
||||
private eventMap:Map<Event_Name, MyEventListeners>;
|
||||
|
||||
private constructor()
|
||||
{
|
||||
this.eventMap = new Map();
|
||||
}
|
||||
|
||||
static get_inst():event_mgr
|
||||
{
|
||||
if(!this.inst) {
|
||||
this.inst = new event_mgr();
|
||||
}
|
||||
return this.inst;
|
||||
}
|
||||
|
||||
fire(event:Event_Name, ...params)
|
||||
{
|
||||
// EventHelper.log(`EventMgr`, `fire event ${event}`);
|
||||
const listeners = this.eventMap.get(event);
|
||||
if(!listeners || listeners.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//事件处理函数中可能会删除事件,导致循环出错
|
||||
listeners.isInvoking = true;
|
||||
const handlers = listeners.handlers;
|
||||
const targets = listeners.targets;
|
||||
|
||||
for(let i = 0, len = handlers.length; i < len; i++) {
|
||||
const handler = handlers[i];
|
||||
const target = targets[i];
|
||||
if(!handler) {
|
||||
continue;
|
||||
}
|
||||
//如果target是cc.Component,则在其节点有效时才调用事件函数
|
||||
if(target && (<cc.Component>target).node) {
|
||||
const node = (target as cc.Component).node;
|
||||
if(cc.isValid(node)) {
|
||||
handler.call(target, ...params);
|
||||
}
|
||||
else {
|
||||
listeners.cancelByTarget(target);
|
||||
}
|
||||
}
|
||||
else {
|
||||
handler.call(target, ...params);
|
||||
}
|
||||
}
|
||||
//循环结束后再删除
|
||||
listeners.isInvoking = false;
|
||||
listeners.purgeCanceled();
|
||||
}
|
||||
|
||||
has(event:Event_Name, handler:MyEvnetHandler, target = null)
|
||||
{
|
||||
let listeners = this.eventMap.get(event);
|
||||
if(!listeners) {
|
||||
return false;
|
||||
}
|
||||
if(handler) {
|
||||
return listeners.has(handler, target);
|
||||
}
|
||||
//检查event是否有监听者
|
||||
if(listeners.isInvoking) {
|
||||
const handlers = listeners.handlers;
|
||||
for(let i = 0, len = handlers.length; i < len; i++) {
|
||||
if(handlers[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return !listeners.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
add(event:Event_Name, handler:MyEvnetHandler, target = null)
|
||||
{
|
||||
// EventHelper.log(`EventMgr`, `add, event = ${event}`);
|
||||
let listeners = this.eventMap.get(event);
|
||||
if(!listeners) {
|
||||
listeners = new MyEventListeners();
|
||||
this.eventMap.set(event, listeners);
|
||||
}
|
||||
listeners.add(handler, target);
|
||||
}
|
||||
|
||||
once(event:Event_Name, handler:MyEvnetHandler, target = null)
|
||||
{
|
||||
// EventHelper.log(`EventMgr`, `once, event = ${event}`);
|
||||
let wrapperCb:MyEvnetHandler;
|
||||
wrapperCb = (...params) => {
|
||||
this.remove(event, wrapperCb, target);
|
||||
handler.call(target, ...params);
|
||||
};
|
||||
this.add(event, wrapperCb, target);
|
||||
}
|
||||
|
||||
remove(event:Event_Name, handler:MyEvnetHandler, target = null)
|
||||
{
|
||||
const listeners = this.eventMap.get(event);
|
||||
if(!listeners || listeners.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if(target) {
|
||||
if(listeners.isInvoking) {
|
||||
listeners.cancelByHandlerTarget(handler, target);
|
||||
}
|
||||
else {
|
||||
listeners.removeByHandlerTarget(handler, target);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(listeners.isInvoking) {
|
||||
listeners.cancelByHandler(handler);
|
||||
}
|
||||
else {
|
||||
listeners.removeByHandler(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeByTarget(target) {
|
||||
this.eventMap.forEach((listeners, event) => {
|
||||
if(listeners.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if(listeners.isInvoking) {
|
||||
listeners.cancelByTarget(target);
|
||||
}
|
||||
else {
|
||||
listeners.removeByTarget(target);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
removeByEvent(event:Event_Name)
|
||||
{
|
||||
const listeners = this.eventMap.get(event);
|
||||
if(!listeners || listeners.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if(listeners.isInvoking) {
|
||||
listeners.cancelAll();
|
||||
}
|
||||
else {
|
||||
listeners.removeAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**事件名称定义*/
|
||||
export enum Event_Name {
|
||||
SCENE_CHANGED = "SCENE_CHANGED",
|
||||
UI_SHOW = "UI_SHOW",
|
||||
UI_HIDE = "UI_HIDE",
|
||||
LANGUAGE_CHANGED = "LANGUAGE_CHANGED",
|
||||
LANGUAGE_USED = "LANGUAGE_USED",
|
||||
REMOTE_ASSETS_UPDATED = "REMOTE_ASSETS_UPDATED",
|
||||
CONFIG_UPDATED = "CONFIG_UPDATED",
|
||||
}
|
||||
42
event/event_tool.ts
Normal file
42
event/event_tool.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Event_Name, MyEvnetHandler, event_mgr } from "../event/event_mgr";
|
||||
|
||||
export class EventTool
|
||||
{
|
||||
private _eventListeners:{event:Event_Name, handler:MyEvnetHandler}[];
|
||||
|
||||
constructor()
|
||||
{
|
||||
}
|
||||
|
||||
protected addEventListener(event:Event_Name, handler:MyEvnetHandler)
|
||||
{
|
||||
event_mgr.get_inst().add(event, handler, this);
|
||||
if(!this._eventListeners) {
|
||||
this._eventListeners = [];
|
||||
}
|
||||
this._eventListeners.push({event, handler});
|
||||
}
|
||||
|
||||
protected removeEventListener(event:Event_Name, handler:MyEvnetHandler)
|
||||
{
|
||||
event_mgr.get_inst().remove(event, handler, this);
|
||||
}
|
||||
|
||||
protected fireEvent(event:Event_Name, ...params)
|
||||
{
|
||||
event_mgr.get_inst().fire(event, ...params);
|
||||
}
|
||||
|
||||
protected clearEventListeners()
|
||||
{
|
||||
const eventListeners = this._eventListeners;
|
||||
if(eventListeners && eventListeners.length) {
|
||||
const eventMgr = event_mgr.get_inst();
|
||||
eventListeners.forEach(listener => {
|
||||
eventMgr.remove(listener.event, listener.handler, this);
|
||||
});
|
||||
}
|
||||
this._eventListeners = null;
|
||||
cc.game.targetOff(this);
|
||||
}
|
||||
}
|
||||
206
linklist.ts
206
linklist.ts
@@ -1,104 +1,104 @@
|
||||
export type LinkListNode<T> = {
|
||||
key:number;
|
||||
data:T;
|
||||
next:LinkListNode<T>;
|
||||
}
|
||||
|
||||
export class LinkList<T>
|
||||
{
|
||||
private pool:LinkListNode<T>[];
|
||||
private _head:LinkListNode<T>;
|
||||
private _tail:LinkListNode<T>;
|
||||
|
||||
constructor()
|
||||
{
|
||||
this._head = this._tail = null;
|
||||
this.pool = [];
|
||||
}
|
||||
|
||||
private spawn_node(key:number, data:T):LinkListNode<T>
|
||||
{
|
||||
let node:LinkListNode<T> = this.pool.pop();
|
||||
if(node)
|
||||
{
|
||||
node.key = key;
|
||||
node.data = data;
|
||||
node.next = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
node = {key:key, data:data, next:null};
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
get head():LinkListNode<T>
|
||||
{
|
||||
return this._head;
|
||||
}
|
||||
|
||||
get tail():LinkListNode<T>
|
||||
{
|
||||
return this._tail;
|
||||
}
|
||||
|
||||
append(key:number, data:T):number
|
||||
{
|
||||
let node:LinkListNode<T> = this.spawn_node(key, data);
|
||||
//将node加到linklist末尾
|
||||
if(this._tail)
|
||||
{
|
||||
this._tail.next = node;
|
||||
this._tail = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._head = this._tail = node;
|
||||
}
|
||||
return node.key;
|
||||
}
|
||||
|
||||
remove(key:number):LinkListNode<T>
|
||||
{
|
||||
if(!key)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if(!this._head)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
let prev:LinkListNode<T>;
|
||||
let curr:LinkListNode<T> = this._head;
|
||||
while(curr && curr.key != key)
|
||||
{
|
||||
prev = curr;
|
||||
curr = curr.next;
|
||||
}
|
||||
//没找到
|
||||
if(!curr)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if(!prev)
|
||||
{
|
||||
//curr为头节点(要区分curr是否同时为尾节点)
|
||||
this._head = curr.next;
|
||||
if(!curr.next)
|
||||
{
|
||||
this._tail = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//curr非头节点(要区分curr是否为尾节点)
|
||||
prev.next = curr.next;
|
||||
if(!curr.next)
|
||||
{
|
||||
this._tail = prev;
|
||||
}
|
||||
}
|
||||
this.pool.push(curr);
|
||||
return curr;
|
||||
}
|
||||
export type LinkListNode<T> = {
|
||||
key:number;
|
||||
data:T;
|
||||
next:LinkListNode<T>;
|
||||
}
|
||||
|
||||
export class LinkList<T>
|
||||
{
|
||||
private pool:LinkListNode<T>[];
|
||||
private _head:LinkListNode<T>;
|
||||
private _tail:LinkListNode<T>;
|
||||
|
||||
constructor()
|
||||
{
|
||||
this._head = this._tail = null;
|
||||
this.pool = [];
|
||||
}
|
||||
|
||||
private spawn_node(key:number, data:T):LinkListNode<T>
|
||||
{
|
||||
let node:LinkListNode<T> = this.pool.pop();
|
||||
if(node)
|
||||
{
|
||||
node.key = key;
|
||||
node.data = data;
|
||||
node.next = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
node = {key:key, data:data, next:null};
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
get head():LinkListNode<T>
|
||||
{
|
||||
return this._head;
|
||||
}
|
||||
|
||||
get tail():LinkListNode<T>
|
||||
{
|
||||
return this._tail;
|
||||
}
|
||||
|
||||
append(key:number, data:T):number
|
||||
{
|
||||
let node:LinkListNode<T> = this.spawn_node(key, data);
|
||||
//将node加到linklist末尾
|
||||
if(this._tail)
|
||||
{
|
||||
this._tail.next = node;
|
||||
this._tail = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._head = this._tail = node;
|
||||
}
|
||||
return node.key;
|
||||
}
|
||||
|
||||
remove(key:number):LinkListNode<T>
|
||||
{
|
||||
if(!key)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if(!this._head)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
let prev:LinkListNode<T>;
|
||||
let curr:LinkListNode<T> = this._head;
|
||||
while(curr && curr.key != key)
|
||||
{
|
||||
prev = curr;
|
||||
curr = curr.next;
|
||||
}
|
||||
//没找到
|
||||
if(!curr)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if(!prev)
|
||||
{
|
||||
//curr为头节点(要区分curr是否同时为尾节点)
|
||||
this._head = curr.next;
|
||||
if(!curr.next)
|
||||
{
|
||||
this._tail = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//curr非头节点(要区分curr是否为尾节点)
|
||||
prev.next = curr.next;
|
||||
if(!curr.next)
|
||||
{
|
||||
this._tail = prev;
|
||||
}
|
||||
}
|
||||
this.pool.push(curr);
|
||||
return curr;
|
||||
}
|
||||
}
|
||||
@@ -1,250 +1,452 @@
|
||||
import {handler, gen_handler} from "../util"
|
||||
|
||||
export class loader_mgr
|
||||
{
|
||||
private static inst:loader_mgr;
|
||||
private constructor(){};
|
||||
|
||||
static get_inst():loader_mgr
|
||||
{
|
||||
if(!loader_mgr.inst)
|
||||
{
|
||||
loader_mgr.inst = new loader_mgr();
|
||||
}
|
||||
return loader_mgr.inst;
|
||||
}
|
||||
|
||||
/**从远程url下载资源 */
|
||||
loadExternalAsset(url:string, cb:handler, type?:string)
|
||||
{
|
||||
const res = cc.loader.getRes(url);
|
||||
if(res)
|
||||
{
|
||||
// console.log("loadExternalAsset from cache");
|
||||
cb.exec(res);
|
||||
return;
|
||||
}
|
||||
cc.loader.load(type ? {url, type} : url, (err, res) => {
|
||||
if(err)
|
||||
{
|
||||
cc.warn("loadExternalAsset error", url);
|
||||
return;
|
||||
}
|
||||
cb.exec(res);
|
||||
});
|
||||
}
|
||||
|
||||
/**从远程url下载资源列表 */
|
||||
loadExternalAssets(urls:string[], cb:handler, types?:string[])
|
||||
{
|
||||
let loaded_res = {};
|
||||
let unloaded_urls:string[] = [];
|
||||
urls.forEach(url => {
|
||||
let res = cc.loader.getRes(url);
|
||||
if(res)
|
||||
{
|
||||
loaded_res[url] = res;
|
||||
}
|
||||
else
|
||||
{
|
||||
unloaded_urls.push(url);
|
||||
}
|
||||
});
|
||||
if(unloaded_urls.length == 0)
|
||||
{
|
||||
cb.exec(loaded_res);
|
||||
return;
|
||||
}
|
||||
|
||||
const loadTasks = [];
|
||||
unloaded_urls.forEach((url, i) => {
|
||||
types ? loadTasks.push({url, type:types[i]}) : loadTasks.push(url);
|
||||
})
|
||||
cc.loader.load(loadTasks, (errs, res) => {
|
||||
cc.info("loadExternalAssets from remote url");
|
||||
if(errs)
|
||||
{
|
||||
cc.warn("loadExternalAssets error", errs);
|
||||
return;
|
||||
}
|
||||
unloaded_urls.forEach(url => {
|
||||
loaded_res[url] = res.getContent(url);
|
||||
});
|
||||
cb.exec(loaded_res);
|
||||
});
|
||||
}
|
||||
|
||||
/**从resources目录加载rawasset,rawaaset是指cc.Texture2D, cc.AudioClip, cc.ParticleAsset*/
|
||||
loadRawAsset(url:string, cb:handler)
|
||||
{
|
||||
let res:any = cc.loader.getRes(url);
|
||||
if(res)
|
||||
{
|
||||
cb.exec(res);
|
||||
return;
|
||||
}
|
||||
cc.loader.loadRes(url, (err:any, res:any):void=>{
|
||||
if(err)
|
||||
{
|
||||
cc.warn("loadRawAsset error", url);
|
||||
return;
|
||||
}
|
||||
cb.exec(res);
|
||||
});
|
||||
}
|
||||
|
||||
/**从resources目录加载asset,asset是指cc.SpriteFrame, cc.AnimationClip, cc.Prefab*/
|
||||
loadAsset(url:string, cb:handler, asset_type:typeof cc.Asset):void
|
||||
{
|
||||
let res:any = cc.loader.getRes(url, asset_type);
|
||||
if(res)
|
||||
{
|
||||
cb.exec(res);
|
||||
return;
|
||||
}
|
||||
cc.loader.loadRes(url, asset_type, (err:any, res:any):void=>{
|
||||
if(err)
|
||||
{
|
||||
cc.warn("loadAsset error", url);
|
||||
return;
|
||||
}
|
||||
cb.exec(res);
|
||||
});
|
||||
}
|
||||
|
||||
/**从resources目录加载asset/rawasset列表,省略资源后缀*/
|
||||
loadResArray(urls:string[], cb:handler):void
|
||||
{
|
||||
let loaded_res:any = {};
|
||||
let unloaded_urls:string[] = [];
|
||||
urls.forEach((url:string):void=>{
|
||||
let res:any = cc.loader.getRes(url);
|
||||
if(res)
|
||||
{
|
||||
loaded_res[url] = res;
|
||||
}
|
||||
else
|
||||
{
|
||||
unloaded_urls.push(url);
|
||||
}
|
||||
});
|
||||
if(unloaded_urls.length == 0)
|
||||
{
|
||||
cb.exec(loaded_res);
|
||||
return;
|
||||
}
|
||||
cc.loader.loadResArray(unloaded_urls, (err:any, res_arr:any[]):void=>{
|
||||
if(err)
|
||||
{
|
||||
cc.warn("loadResArray error", unloaded_urls);
|
||||
return;
|
||||
}
|
||||
unloaded_urls.forEach((url:string):void=>{
|
||||
loaded_res[url] = cc.loader.getRes(url);
|
||||
});
|
||||
cb.exec(loaded_res);
|
||||
});
|
||||
}
|
||||
|
||||
/**从resources目录加载prefab(省略资源后缀),加载成功后生成prefab实例*/
|
||||
loadPrefabObj(url:string, cb:handler)
|
||||
{
|
||||
let res:any = cc.loader.getRes(url, cc.Prefab);
|
||||
if(res)
|
||||
{
|
||||
let node:cc.Node = cc.instantiate(res);
|
||||
cb.exec(node);
|
||||
return;
|
||||
}
|
||||
//err is typeof Error, err.message
|
||||
cc.loader.loadRes(url, cc.Prefab, (err:any, res:any):void=>{
|
||||
if(err)
|
||||
{
|
||||
cc.warn("loadPrefabObj error", url);
|
||||
return;
|
||||
}
|
||||
let node:cc.Node = cc.instantiate(res);
|
||||
cb.exec(node);
|
||||
});
|
||||
}
|
||||
|
||||
/**从resources目录加载prefab列表(省略资源后缀),加载成功后生成prefab实例*/
|
||||
loadPrefabObjArray(urls:string[], cb:handler):void
|
||||
{
|
||||
let loaded_obj:any = {};
|
||||
let unloaded_urls:string[] = [];
|
||||
urls.forEach((url:string):void=>{
|
||||
let res:any = cc.loader.getRes(url, cc.Prefab);
|
||||
if(res)
|
||||
{
|
||||
loaded_obj[url] = cc.instantiate(res);
|
||||
}
|
||||
else
|
||||
{
|
||||
unloaded_urls.push(url);
|
||||
}
|
||||
});
|
||||
if(unloaded_urls.length == 0)
|
||||
{
|
||||
cb.exec(loaded_obj);
|
||||
return;
|
||||
}
|
||||
cc.loader.loadResArray(unloaded_urls, cc.Prefab, (err:any, res_arr:any[]):void=>{
|
||||
if(err)
|
||||
{
|
||||
cc.warn("loadPrefabObjArray error", unloaded_urls);
|
||||
return;
|
||||
}
|
||||
unloaded_urls.forEach((url:string):void=>{
|
||||
loaded_obj[url] = cc.instantiate(cc.loader.getRes(url, cc.Prefab));
|
||||
});
|
||||
cb.exec(loaded_obj);
|
||||
});
|
||||
}
|
||||
|
||||
loadPrefabDir(dir_path:string, cb:handler):void
|
||||
{
|
||||
let map:any = {};
|
||||
cc.loader.loadResDir(dir_path, cc.Prefab, (err:any, res_arr:any[], urls:string[]):void=>{
|
||||
if(err)
|
||||
{
|
||||
cc.warn("loadPrefabObjDir error", dir_path);
|
||||
return;
|
||||
}
|
||||
urls.forEach((url) => {
|
||||
map[url] = cc.loader.getRes(url, cc.Prefab);
|
||||
});
|
||||
cb.exec(map);
|
||||
});
|
||||
}
|
||||
|
||||
loadPrefabObjDir(dir_path:string, cb:handler):void
|
||||
{
|
||||
let map:any = {};
|
||||
cc.loader.loadResDir(dir_path, cc.Prefab, (err:any, res_arr:any[], urls:string[]):void=>{
|
||||
if(err)
|
||||
{
|
||||
cc.warn("loadPrefabObjDir error", dir_path);
|
||||
return;
|
||||
}
|
||||
urls.forEach((url) => {
|
||||
map[url] = cc.instantiate(cc.loader.getRes(url, cc.Prefab));
|
||||
});
|
||||
cb.exec(map);
|
||||
});
|
||||
}
|
||||
|
||||
release(urlOrAssetOrNode:any):void
|
||||
{
|
||||
if(urlOrAssetOrNode instanceof cc.Node)
|
||||
{
|
||||
//释放节点,从场景上移除
|
||||
urlOrAssetOrNode.destroy();
|
||||
}
|
||||
else
|
||||
{
|
||||
//释放缓存引用和资源内容
|
||||
cc.loader.release(urlOrAssetOrNode);
|
||||
}
|
||||
}
|
||||
import {handler, gen_handler} from "../util"
|
||||
|
||||
export class loader_mgr
|
||||
{
|
||||
private static inst:loader_mgr;
|
||||
private _loadedRes:Map<string, boolean>;
|
||||
private _loadedExternalUrls:Map<string, boolean>;
|
||||
|
||||
private constructor()
|
||||
{
|
||||
this._loadedRes = new Map();
|
||||
this._loadedExternalUrls = new Map();
|
||||
}
|
||||
|
||||
static get_inst():loader_mgr
|
||||
{
|
||||
if(!this.inst)
|
||||
{
|
||||
this.inst = new loader_mgr();
|
||||
}
|
||||
return this.inst;
|
||||
}
|
||||
|
||||
setExternalSprite(sprite:cc.Sprite, url:string, reActive = false)
|
||||
{
|
||||
this.loadExternalAsset(url, gen_handler((tex:cc.Texture2D) => {
|
||||
if(!sprite || !cc.isValid(sprite.node))
|
||||
{
|
||||
cc.loader.release(url);
|
||||
return;
|
||||
}
|
||||
if(reActive)
|
||||
{
|
||||
sprite.node.active = true;
|
||||
}
|
||||
sprite.spriteFrame = new cc.SpriteFrame(tex);
|
||||
}));
|
||||
}
|
||||
|
||||
setExternalSpriteFrame(sprite:cc.Sprite, frame:cc.SpriteFrame, url:string, reActive = false)
|
||||
{
|
||||
this.loadExternalAsset(url, gen_handler((tex:cc.Texture2D) => {
|
||||
if(!sprite || !cc.isValid(sprite.node))
|
||||
{
|
||||
cc.loader.release(url);
|
||||
return;
|
||||
}
|
||||
if(reActive)
|
||||
{
|
||||
sprite.node.active = true;
|
||||
}
|
||||
if(cc.isValid(frame))
|
||||
{
|
||||
frame.setTexture(tex);
|
||||
sprite.spriteFrame = frame;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
setSprite(sprite:cc.Sprite, url:string, reActive = false)
|
||||
{
|
||||
this.loadRes(url, gen_handler((tex:cc.Texture2D) => {
|
||||
if(!sprite || !cc.isValid(sprite.node))
|
||||
{
|
||||
cc.loader.release(url);
|
||||
return;
|
||||
}
|
||||
if(reActive)
|
||||
{
|
||||
sprite.node.active = true;
|
||||
}
|
||||
sprite.spriteFrame = new cc.SpriteFrame(tex);
|
||||
}));
|
||||
}
|
||||
|
||||
setSpriteFrame(sprite:cc.Sprite, frame:cc.SpriteFrame, url:string, reActive = false)
|
||||
{
|
||||
this.loadRes(url, gen_handler((tex:cc.Texture2D) => {
|
||||
if(!sprite || !cc.isValid(sprite.node))
|
||||
{
|
||||
cc.loader.release(url);
|
||||
return;
|
||||
}
|
||||
if(reActive)
|
||||
{
|
||||
sprite.node.active = true;
|
||||
}
|
||||
if(cc.isValid(frame))
|
||||
{
|
||||
frame.setTexture(tex);
|
||||
sprite.spriteFrame = frame;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
setAtlasSprite(sprite:cc.Sprite, atlasUrl:string, spriteFrameName:string, reActive = false)
|
||||
{
|
||||
this.loadRes(atlasUrl, gen_handler((atlas:cc.SpriteAtlas) => {
|
||||
if(!sprite || !cc.isValid(sprite.node))
|
||||
{
|
||||
cc.loader.release(atlasUrl);
|
||||
return;
|
||||
}
|
||||
if(reActive)
|
||||
{
|
||||
sprite.node.active = true;
|
||||
}
|
||||
sprite.spriteFrame = atlas.getSpriteFrame(spriteFrameName);
|
||||
}), cc.SpriteAtlas);
|
||||
}
|
||||
|
||||
/**从远程url下载资源 */
|
||||
loadExternalAsset(url:string, cb:handler, type?:string)
|
||||
{
|
||||
const res = cc.loader.getRes(url);
|
||||
if(cc.isValid(res))
|
||||
{
|
||||
// console.log("loadExternalAsset from cache");
|
||||
cb.exec(res);
|
||||
return;
|
||||
}
|
||||
cc.loader.load(type ? {url, type} : url, (err, res) => {
|
||||
if(err)
|
||||
{
|
||||
cc.warn("loadExternalAsset error", url);
|
||||
return;
|
||||
}
|
||||
if(cc.isValid(res)) {
|
||||
this.cacheExternalAsset(url);
|
||||
cb.exec(res);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**从远程url下载资源列表 */
|
||||
loadExternalAssets(urls:string[], cb:handler, types?:string[])
|
||||
{
|
||||
let loaded_res = {};
|
||||
let unloaded_urls:string[] = [];
|
||||
urls.forEach(url => {
|
||||
let res = cc.loader.getRes(url);
|
||||
if(cc.isValid(res))
|
||||
{
|
||||
loaded_res[url] = res;
|
||||
}
|
||||
else
|
||||
{
|
||||
unloaded_urls.push(url);
|
||||
}
|
||||
});
|
||||
if(unloaded_urls.length == 0)
|
||||
{
|
||||
cb.exec(loaded_res);
|
||||
return;
|
||||
}
|
||||
|
||||
const loadTasks = [];
|
||||
unloaded_urls.forEach((url, i) => {
|
||||
types ? loadTasks.push({url, type:types[i]}) : loadTasks.push(url);
|
||||
})
|
||||
cc.loader.load(loadTasks, (errs, ress) => {
|
||||
// cc.log("loadExternalAssets from remote url");
|
||||
if(errs)
|
||||
{
|
||||
cc.warn("loadExternalAssets error", errs);
|
||||
return;
|
||||
}
|
||||
let isValid = true;
|
||||
unloaded_urls.forEach(url => {
|
||||
const res = ress.getContent(url);
|
||||
if(!cc.isValid(res)) {
|
||||
isValid = false;
|
||||
return;
|
||||
}
|
||||
loaded_res[url] = res;
|
||||
this.cacheExternalAsset(url);
|
||||
});
|
||||
if(isValid) {
|
||||
cb.exec(loaded_res);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**从resources目录加载asset*/
|
||||
loadRes(url:string, cb:handler, assetType?:typeof cc.Asset):void
|
||||
{
|
||||
let res = cc.loader.getRes(url, assetType);
|
||||
if(cc.isValid(res))
|
||||
{
|
||||
cb.exec(res);
|
||||
return;
|
||||
}
|
||||
cc.loader.loadRes(url, assetType, (err, res) => {
|
||||
if(err)
|
||||
{
|
||||
cc.warn("loadAsset error", url);
|
||||
return;
|
||||
}
|
||||
if(cc.isValid(res)) {
|
||||
this.cacheAsset(res);
|
||||
cb.exec(res);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**从resources目录加载asset列表,省略资源后缀*/
|
||||
loadResArray(urls:string[], cb:handler, assetTypes?:typeof cc.Asset[], alias?:string[])
|
||||
{
|
||||
//加载同名资源时,需要手动给出不同的别名
|
||||
let loaded_res = {};
|
||||
let unloaded_urls:string[] = [];
|
||||
let unloaded_alias:string[];
|
||||
let unloaded_types:typeof cc.Asset[];
|
||||
|
||||
if(alias)
|
||||
{
|
||||
unloaded_alias = [];
|
||||
}
|
||||
if(assetTypes)
|
||||
{
|
||||
unloaded_types = [];
|
||||
}
|
||||
urls.forEach((url, idx) => {
|
||||
const resType = assetTypes ? assetTypes[idx] : null;
|
||||
const resAlias = alias ? alias[idx] : null;
|
||||
const res = cc.loader.getRes(url, resType);
|
||||
if(cc.isValid(res))
|
||||
{
|
||||
loaded_res[resAlias || url] = res;
|
||||
}
|
||||
else
|
||||
{
|
||||
unloaded_urls.push(url);
|
||||
if(resType)
|
||||
{
|
||||
unloaded_types.push(resType);
|
||||
}
|
||||
if(resAlias)
|
||||
{
|
||||
unloaded_alias.push(resAlias);
|
||||
}
|
||||
}
|
||||
});
|
||||
if(unloaded_urls.length == 0)
|
||||
{
|
||||
cb.exec(loaded_res);
|
||||
return;
|
||||
}
|
||||
cc.loader.loadResArray(unloaded_urls, unloaded_types, err => {
|
||||
if(err)
|
||||
{
|
||||
cc.warn("loadResArray error", unloaded_urls);
|
||||
return;
|
||||
}
|
||||
let isValid = true;
|
||||
unloaded_urls.forEach((url, idx) => {
|
||||
const resType = unloaded_types ? unloaded_types[idx] : null;
|
||||
const resAlias = unloaded_alias ? unloaded_alias[idx] : null;
|
||||
const res = cc.loader.getRes(url, resType);
|
||||
if(!cc.isValid(res)) {
|
||||
isValid = false;
|
||||
return;
|
||||
}
|
||||
loaded_res[resAlias || url] = res;
|
||||
this.cacheAsset(res);
|
||||
});
|
||||
if(isValid) {
|
||||
cb.exec(loaded_res);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**从resources目录加载prefab(省略资源后缀),加载成功后生成prefab实例*/
|
||||
loadPrefabObj(url:string, cb:handler)
|
||||
{
|
||||
let res:cc.Prefab = cc.loader.getRes(url, cc.Prefab);
|
||||
if(cc.isValid(res))
|
||||
{
|
||||
let node:cc.Node = cc.instantiate(res);
|
||||
cb.exec(node);
|
||||
return;
|
||||
}
|
||||
//err is typeof Error, err.message
|
||||
cc.loader.loadRes(url, cc.Prefab, (err, res:cc.Prefab) => {
|
||||
if(err)
|
||||
{
|
||||
cc.warn("loadPrefabObj error", url);
|
||||
return;
|
||||
}
|
||||
if(cc.isValid(res)) {
|
||||
this.cacheAsset(res);
|
||||
let node:cc.Node = cc.instantiate(res);
|
||||
cb.exec(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**从resources目录加载prefab列表(省略资源后缀),加载成功后生成prefab实例*/
|
||||
loadPrefabObjArray(urls:string[], cb:handler)
|
||||
{
|
||||
let loaded_obj:any = {};
|
||||
let unloaded_urls:string[] = [];
|
||||
urls.forEach((url:string) => {
|
||||
const res:cc.Prefab = cc.loader.getRes(url, cc.Prefab);
|
||||
if(cc.isValid(res))
|
||||
{
|
||||
loaded_obj[url] = cc.instantiate(res);
|
||||
}
|
||||
else
|
||||
{
|
||||
unloaded_urls.push(url);
|
||||
}
|
||||
});
|
||||
if(unloaded_urls.length == 0)
|
||||
{
|
||||
cb.exec(loaded_obj);
|
||||
return;
|
||||
}
|
||||
cc.loader.loadResArray(unloaded_urls, cc.Prefab, err => {
|
||||
if(err)
|
||||
{
|
||||
cc.warn("loadPrefabObjArray error", unloaded_urls);
|
||||
return;
|
||||
}
|
||||
let isValid = true;
|
||||
unloaded_urls.forEach(url => {
|
||||
const res:cc.Prefab = cc.loader.getRes(url, cc.Prefab);
|
||||
if(!cc.isValid(res)) {
|
||||
isValid = false;
|
||||
return;
|
||||
}
|
||||
loaded_obj[url] = cc.instantiate(res);
|
||||
this.cacheAsset(res);
|
||||
});
|
||||
if(isValid) {
|
||||
cb.exec(loaded_obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
loadPrefabDir(dir_path:string, cb:handler)
|
||||
{
|
||||
let map:any = {};
|
||||
cc.loader.loadResDir(dir_path, cc.Prefab, (err:any, res_arr:any[], urls:string[]) => {
|
||||
if(err)
|
||||
{
|
||||
cc.warn("loadPrefabObjDir error", dir_path);
|
||||
return;
|
||||
}
|
||||
let isValid = true;
|
||||
urls.forEach(url => {
|
||||
const res:cc.Prefab = cc.loader.getRes(url, cc.Prefab);
|
||||
if(!cc.isValid(res)) {
|
||||
isValid = false;
|
||||
return;
|
||||
}
|
||||
map[url] = res;
|
||||
this.cacheAsset(res);
|
||||
});
|
||||
if(isValid) {
|
||||
cb.exec(map);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
loadPrefabObjDir(dir_path:string, cb:handler):void
|
||||
{
|
||||
let map:any = {};
|
||||
cc.loader.loadResDir(dir_path, cc.Prefab, (err:any, res_arr:any[], urls:string[]) => {
|
||||
if(err)
|
||||
{
|
||||
cc.warn("loadPrefabObjDir error", dir_path);
|
||||
return;
|
||||
}
|
||||
let isValid = true;
|
||||
urls.forEach(url => {
|
||||
const res:cc.Prefab = cc.loader.getRes(url, cc.Prefab);
|
||||
if(!cc.isValid(res)) {
|
||||
isValid = false;
|
||||
return;
|
||||
}
|
||||
map[url] = cc.instantiate(res);
|
||||
this.cacheAsset(res);
|
||||
});
|
||||
if(isValid) {
|
||||
cb.exec(map);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private cacheAsset(asset:cc.Asset)
|
||||
{
|
||||
if(cc.isValid(asset)) {
|
||||
const key:string = cc.loader._getReferenceKey(asset);
|
||||
if(key) {
|
||||
this._loadedRes.set(key, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private cacheExternalAsset(url:string)
|
||||
{
|
||||
this._loadedExternalUrls.set(url, true);
|
||||
}
|
||||
|
||||
releaseAll(excludeMap = null)
|
||||
{
|
||||
const leftRes:Map<string, boolean> = new Map();
|
||||
this._loadedRes.forEach((_, res) => {
|
||||
const deps = cc.loader.getDependsRecursively(res);
|
||||
deps.forEach(d => {
|
||||
if(!d) {
|
||||
return;
|
||||
}
|
||||
// cc.log(`loaderMgr release loadedRes, dep=${d}, exclude=${excludeMap ? excludeMap[d] : false}`);
|
||||
if(!excludeMap || !excludeMap[d]) {
|
||||
cc.loader.release(d);
|
||||
}
|
||||
else {
|
||||
leftRes.set(d, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
this._loadedRes.clear();
|
||||
leftRes.forEach((_, d) => {
|
||||
this._loadedRes.set(d, true);
|
||||
});
|
||||
|
||||
//释放外部资源
|
||||
this._loadedExternalUrls.forEach((_, url) => {
|
||||
// cc.log(`loaderMgr release loadedExternalRes, url=${url}`);
|
||||
cc.loader.release(url);
|
||||
});
|
||||
this._loadedExternalUrls.clear();
|
||||
|
||||
cc.sys.garbageCollect();
|
||||
// this.dump();
|
||||
}
|
||||
|
||||
dump()
|
||||
{
|
||||
cc.log(`---------------------loader_mgr dump begin--------------------------`);
|
||||
const cache = cc.loader._cache;
|
||||
let count = 0;
|
||||
for(let id in cache)
|
||||
{
|
||||
count++;
|
||||
cc.log(`id=${id}, value=${cache[id]}`);
|
||||
}
|
||||
cc.log(`---------------------loader_mgr dump end, totalCount=${count}--------------------------`);
|
||||
}
|
||||
}
|
||||
46
loader/loading_queue.ts
Normal file
46
loader/loading_queue.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { handler, gen_handler } from "../util";
|
||||
import { loader_mgr } from "./loader_mgr";
|
||||
|
||||
export class LoadingQueue
|
||||
{
|
||||
private static _inst:LoadingQueue;
|
||||
private _loadingMap:Map<string, handler[]>;
|
||||
private constructor()
|
||||
{
|
||||
this._loadingMap = new Map();
|
||||
}
|
||||
|
||||
static getInst()
|
||||
{
|
||||
if(!this._inst) {
|
||||
this._inst = new LoadingQueue();
|
||||
}
|
||||
return this._inst;
|
||||
}
|
||||
|
||||
loadPrefabObj(path:string, cb:handler)
|
||||
{
|
||||
let cbs = this._loadingMap.get(path);
|
||||
if(!cbs) {
|
||||
cbs = [];
|
||||
this._loadingMap.set(path, cbs);
|
||||
}
|
||||
cbs.push(cb);
|
||||
if(cbs.length > 1) {
|
||||
return;
|
||||
}
|
||||
loader_mgr.get_inst().loadPrefabObj(path, gen_handler((node:cc.Node) => {
|
||||
const cbs = this._loadingMap.get(path);
|
||||
if(cbs) {
|
||||
cbs.forEach(cb => cb.exec(node));
|
||||
cbs.length = 0;
|
||||
this._loadingMap.delete(path);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
clear()
|
||||
{
|
||||
this._loadingMap.clear();
|
||||
}
|
||||
}
|
||||
105
misc/storage.ts
Normal file
105
misc/storage.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
export class LocalStorage
|
||||
{
|
||||
private static _inst:LocalStorage;
|
||||
|
||||
private constructor()
|
||||
{
|
||||
}
|
||||
|
||||
static getInst():LocalStorage
|
||||
{
|
||||
if(!this._inst)
|
||||
{
|
||||
this._inst = new LocalStorage();
|
||||
}
|
||||
return this._inst;
|
||||
}
|
||||
|
||||
remove(key:string)
|
||||
{
|
||||
cc.sys.localStorage.removeItem(key);
|
||||
}
|
||||
|
||||
clear()
|
||||
{
|
||||
cc.sys.localStorage.clear();
|
||||
}
|
||||
|
||||
getString(key:string, defaultValue = "")
|
||||
{
|
||||
const value = cc.sys.localStorage.getItem(key);
|
||||
if(value != null) {
|
||||
return value;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
setString(key:string, value:string)
|
||||
{
|
||||
cc.sys.localStorage.setItem(key, value);
|
||||
}
|
||||
|
||||
getInt(key:string, defaultValue = 0)
|
||||
{
|
||||
const value = cc.sys.localStorage.getItem(key);
|
||||
if(value != null)
|
||||
{
|
||||
return parseInt(value);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
setInt(key:string, value:number)
|
||||
{
|
||||
cc.sys.localStorage.setItem(key, value.toString());
|
||||
}
|
||||
|
||||
getBool(key:string, defaultValue = false)
|
||||
{
|
||||
const value = cc.sys.localStorage.getItem(key);
|
||||
if(value != null)
|
||||
{
|
||||
return parseInt(value) == 1;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
setBool(key:string, value:boolean)
|
||||
{
|
||||
cc.sys.localStorage.setItem(key, value ? "1" : "0");
|
||||
}
|
||||
|
||||
getFloat(key:string, defaultValue = 0)
|
||||
{
|
||||
const value = cc.sys.localStorage.getItem(key);
|
||||
if(value != null)
|
||||
{
|
||||
return parseFloat(value);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
setFloat(key:string, value:number)
|
||||
{
|
||||
cc.sys.localStorage.setItem(key, value.toString());
|
||||
}
|
||||
|
||||
getJson(key:string, defaultValue = null)
|
||||
{
|
||||
const value = cc.sys.localStorage.getItem(key);
|
||||
if(value != null)
|
||||
{
|
||||
return JSON.parse(value);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
setJson(key:string, value:object)
|
||||
{
|
||||
cc.sys.localStorage.setItem(key, JSON.stringify(value));
|
||||
}
|
||||
}
|
||||
|
||||
export const LocalStorageKey = {
|
||||
initialLangId: "__initialLangId__",
|
||||
};
|
||||
9
native/deviceutils.ts
Normal file
9
native/deviceutils.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export const DeviceUtils = {
|
||||
setKeepScreenOn(value:boolean) {
|
||||
jsb.Device.setKeepScreenOn(value);
|
||||
},
|
||||
|
||||
vibrate(duration:number = 0.01) {
|
||||
jsb.Device.vibrate(duration);
|
||||
}
|
||||
};
|
||||
102
native/fileutils.ts
Normal file
102
native/fileutils.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
export class FileUtils
|
||||
{
|
||||
private static _inst:FileUtils;
|
||||
|
||||
private constructor()
|
||||
{
|
||||
}
|
||||
|
||||
static getInst()
|
||||
{
|
||||
if(!this._inst)
|
||||
{
|
||||
this._inst = new FileUtils();
|
||||
}
|
||||
return this._inst;
|
||||
}
|
||||
|
||||
getWritablePath()
|
||||
{
|
||||
return jsb.fileUtils.getWritablePath();
|
||||
}
|
||||
|
||||
createDirectory(dirPath:string):boolean
|
||||
{
|
||||
return jsb.fileUtils.createDirectory(dirPath);
|
||||
}
|
||||
|
||||
isDirectoryExist(dirPath:string):boolean
|
||||
{
|
||||
return jsb.fileUtils.isDirectoryExist(dirPath);
|
||||
}
|
||||
|
||||
removeDirectory(dirPath:string):boolean
|
||||
{
|
||||
return jsb.fileUtils.removeDirectory(dirPath);
|
||||
}
|
||||
|
||||
removeFile(filePath:string):boolean
|
||||
{
|
||||
return jsb.fileUtils.removeFile(filePath);
|
||||
}
|
||||
|
||||
isFileExist(filename:string):boolean
|
||||
{
|
||||
return jsb.fileUtils.isFileExist(filename);
|
||||
}
|
||||
|
||||
getSearchPaths():string[]
|
||||
{
|
||||
return jsb.fileUtils.getSearchPaths();
|
||||
}
|
||||
|
||||
setSearchPaths(searchPaths:string[])
|
||||
{
|
||||
return jsb.fileUtils.setSearchPaths(searchPaths);
|
||||
}
|
||||
|
||||
addSearchPath(path:string, front = false)
|
||||
{
|
||||
const prevSearchPaths = this.getSearchPaths();
|
||||
if(prevSearchPaths.indexOf(path) != -1) {
|
||||
return;
|
||||
}
|
||||
return jsb.fileUtils.addSearchPath(path, front);
|
||||
}
|
||||
|
||||
getDataFromFile(filename:string)
|
||||
{
|
||||
return jsb.fileUtils.getDataFromFile(filename);
|
||||
}
|
||||
|
||||
getStringFromFile(filename:string):string
|
||||
{
|
||||
return jsb.fileUtils.getStringFromFile(filename);
|
||||
}
|
||||
|
||||
getJsonFromFile(filename:string)
|
||||
{
|
||||
const str = this.getStringFromFile(filename);
|
||||
if(str)
|
||||
{
|
||||
return JSON.parse(str);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
writeStringToFile(dataStr:string, fullPath:string):boolean
|
||||
{
|
||||
return jsb.fileUtils.writeStringToFile(dataStr, fullPath);
|
||||
}
|
||||
|
||||
writeJsonToFile(data:object, fullPath:string)
|
||||
{
|
||||
const str = JSON.stringify(data);
|
||||
return this.writeStringToFile(str, fullPath);
|
||||
}
|
||||
|
||||
unzipFileToDirAsync(zipFileName:string, destPath:string)
|
||||
{
|
||||
return jsb.fileUtils.unzipFileToDirAsync(zipFileName, destPath);
|
||||
}
|
||||
}
|
||||
370
native/md5.ts
Executable file
370
native/md5.ts
Executable file
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
Javascript MD5 library - version 0.6
|
||||
|
||||
Coded (2011-2016) by Luigi Galli - the running geek LG@THLG.NL - http://THLG.NL
|
||||
|
||||
Thanks for feedback/tips/suggestions/comments: Roberto Viola, Lucien Nel, Gahl Saraf, Saqib Dareshani, Philip Peterson.
|
||||
|
||||
The below code is PUBLIC DOMAIN - NO WARRANTY!
|
||||
|
||||
Changelog: Version 0.6 - 2016-06-26
|
||||
** FIXED: processing an array as input could result in the size of the array changing.
|
||||
- new email/website
|
||||
Version 0.4 - 2011-06-19
|
||||
+ added compact version (md5_compact_min.js), this is a slower but smaller version
|
||||
(more than 4KB lighter before stripping/minification)
|
||||
+ added preliminary support for Typed Arrays (see:
|
||||
https://developer.mozilla.org/en/JavaScript_typed_arrays and
|
||||
http://www.khronos.org/registry/typedarray/specs/latest/)
|
||||
MD5() now accepts input data as ArrayBuffer, Float32Array, Float64Array,
|
||||
Int16Array, Int32Array, Int8Array, Uint16Array, Uint32Array or Uint8Array
|
||||
- moved unit tests to md5_test.js
|
||||
- minor refactoring
|
||||
|
||||
Version 0.3.* - 2011-06-##
|
||||
- Internal dev versions
|
||||
|
||||
Version 0.2 - 2011-05-22
|
||||
** FIXED: serious integer overflow problems which could cause a wrong MD5 hash being returned
|
||||
|
||||
Version 0.1 - 2011
|
||||
-Initial version
|
||||
*/
|
||||
|
||||
// if (typeof faultylabs === 'undefined') {
|
||||
// faultylabs = {};
|
||||
// }
|
||||
|
||||
/*
|
||||
MD5()
|
||||
|
||||
Computes the MD5 hash for the given input data
|
||||
|
||||
input : data as String - (Assumes Unicode code points are encoded as UTF-8. If you
|
||||
attempt to digest Unicode strings using other encodings
|
||||
you will get incorrect results!)
|
||||
|
||||
data as array of characters - (Assumes Unicode code points are encoded as UTF-8. If you
|
||||
attempt to digest Unicode strings using other encodings
|
||||
you will get incorrect results!)
|
||||
|
||||
data as array of bytes (plain javascript array of integer numbers)
|
||||
|
||||
data as ArrayBuffer (see: https://developer.mozilla.org/en/JavaScript_typed_arrays)
|
||||
|
||||
data as Float32Array, Float64Array, Int16Array, Int32Array, Int8Array, Uint16Array, Uint32Array or Uint8Array (see: https://developer.mozilla.org/en/JavaScript_typed_arrays)
|
||||
|
||||
(DataView is not supported yet)
|
||||
|
||||
output: MD5 hash (as Hex Uppercase String)
|
||||
*/
|
||||
|
||||
let cache;
|
||||
export const MD5 = function(data) {
|
||||
//use cache first
|
||||
if(cache) {
|
||||
const cache_ret = cache.get(data);
|
||||
if(cache_ret) {
|
||||
return cache_ret;
|
||||
}
|
||||
}
|
||||
|
||||
// convert number to (unsigned) 32 bit hex, zero filled string
|
||||
function to_zerofilled_hex(n) {
|
||||
var t1 = (n >>> 0).toString(16);
|
||||
return "00000000".substr(0, 8 - t1.length) + t1;
|
||||
}
|
||||
|
||||
// convert array of chars to array of bytes
|
||||
function chars_to_bytes(ac) {
|
||||
var retval = [];
|
||||
for (var i = 0; i < ac.length; i++) {
|
||||
retval = retval.concat(str_to_bytes(ac[i]));
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
// convert a 64 bit unsigned number to array of bytes. Little endian
|
||||
function int64_to_bytes(num) {
|
||||
var retval = [];
|
||||
for (var i = 0; i < 8; i++) {
|
||||
retval.push(num & 0xFF);
|
||||
num = num >>> 8;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
// 32 bit left-rotation
|
||||
function rol(num, places) {
|
||||
return ((num << places) & 0xFFFFFFFF) | (num >>> (32 - places));
|
||||
}
|
||||
|
||||
// The 4 MD5 functions
|
||||
function fF(b, c, d) {
|
||||
return (b & c) | (~b & d);
|
||||
}
|
||||
|
||||
function fG(b, c, d) {
|
||||
return (d & b) | (~d & c);
|
||||
}
|
||||
|
||||
function fH(b, c, d) {
|
||||
return b ^ c ^ d;
|
||||
}
|
||||
|
||||
function fI(b, c, d) {
|
||||
return c ^ (b | ~d);
|
||||
}
|
||||
|
||||
// pick 4 bytes at specified offset. Little-endian is assumed
|
||||
function bytes_to_int32(arr, off) {
|
||||
return (arr[off + 3] << 24) | (arr[off + 2] << 16) | (arr[off + 1] << 8) | (arr[off]);
|
||||
}
|
||||
|
||||
/*
|
||||
Conver string to array of bytes in UTF-8 encoding
|
||||
See:
|
||||
http://www.dangrossman.info/2007/05/25/handling-utf-8-in-javascript-php-and-non-utf8-databases/
|
||||
http://stackoverflow.com/questions/1240408/reading-bytes-from-a-javascript-string
|
||||
How about a String.getBytes(<ENCODING>) for Javascript!? Isn't it time to add it?
|
||||
*/
|
||||
function str_to_bytes(str) {
|
||||
var retval = [ ];
|
||||
for (var i = 0; i < str.length; i++)
|
||||
if (str.charCodeAt(i) <= 0x7F) {
|
||||
retval.push(str.charCodeAt(i));
|
||||
} else {
|
||||
var tmp = encodeURIComponent(str.charAt(i)).substr(1).split('%')
|
||||
for (var j = 0; j < tmp.length; j++) {
|
||||
retval.push(parseInt(tmp[j], 0x10));
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
// convert the 4 32-bit buffers to a 128 bit hex string. (Little-endian is assumed)
|
||||
function int128le_to_hex(a, b, c, d) {
|
||||
var ra = "";
|
||||
var t = 0;
|
||||
var ta = 0;
|
||||
for (var i = 3; i >= 0; i--) {
|
||||
ta = arguments[i];
|
||||
t = (ta & 0xFF);
|
||||
ta = ta >>> 8;
|
||||
t = t << 8;
|
||||
t = t | (ta & 0xFF);
|
||||
ta = ta >>> 8;
|
||||
t = t << 8;
|
||||
t = t | (ta & 0xFF);
|
||||
ta = ta >>> 8;
|
||||
t = t << 8;
|
||||
t = t | ta;
|
||||
ra = ra + to_zerofilled_hex(t);
|
||||
}
|
||||
return ra;
|
||||
}
|
||||
|
||||
// conversion from typed byte array to plain javascript array
|
||||
function typed_to_plain(tarr) {
|
||||
var retval = new Array(tarr.length);
|
||||
for (var i = 0; i < tarr.length; i++) {
|
||||
retval[i] = tarr[i];
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
// check input data type and perform conversions if needed
|
||||
var databytes = null;
|
||||
|
||||
function cloneArray(inpArr) {
|
||||
return inpArr.slice();
|
||||
}
|
||||
|
||||
// String
|
||||
var type_mismatch = null;
|
||||
if (typeof data == 'string') {
|
||||
// convert string to array bytes
|
||||
databytes = str_to_bytes(data);
|
||||
} else if (data.constructor == Array) {
|
||||
if (data.length === 0) {
|
||||
// if it's empty, just assume array of bytes
|
||||
databytes = cloneArray(data);
|
||||
} else if (typeof data[0] == 'string') {
|
||||
databytes = chars_to_bytes(data);
|
||||
} else if (typeof data[0] == 'number') {
|
||||
databytes = cloneArray(data);
|
||||
} else {
|
||||
type_mismatch = typeof data[0];
|
||||
}
|
||||
} else if (typeof ArrayBuffer != 'undefined') {
|
||||
if (data instanceof ArrayBuffer) {
|
||||
databytes = typed_to_plain(new Uint8Array(data));
|
||||
} else if ((data instanceof Uint8Array) || (data instanceof Int8Array)) {
|
||||
databytes = typed_to_plain(data);
|
||||
} else if ((data instanceof Uint32Array) || (data instanceof Int32Array) ||
|
||||
(data instanceof Uint16Array) || (data instanceof Int16Array) ||
|
||||
(data instanceof Float32Array) || (data instanceof Float64Array)
|
||||
) {
|
||||
databytes = typed_to_plain(new Uint8Array(data.buffer));
|
||||
} else {
|
||||
type_mismatch = typeof data;
|
||||
}
|
||||
} else {
|
||||
type_mismatch = typeof data;
|
||||
}
|
||||
|
||||
if (type_mismatch) {
|
||||
alert('MD5 type mismatch, cannot process ' + type_mismatch);
|
||||
}
|
||||
|
||||
function _add(n1, n2) {
|
||||
return 0x0FFFFFFFF & (n1 + n2);
|
||||
}
|
||||
|
||||
const ret = do_digest();
|
||||
cache = cache || new Map();
|
||||
cache.set(data, ret);
|
||||
return ret;
|
||||
|
||||
function do_digest() {
|
||||
|
||||
// function update partial state for each run
|
||||
function updateRun(nf, sin32, dw32, b32) {
|
||||
var temp = d;
|
||||
d = c;
|
||||
c = b;
|
||||
//b = b + rol(a + (nf + (sin32 + dw32)), b32)
|
||||
b = _add(b,
|
||||
rol(
|
||||
_add(a,
|
||||
_add(nf, _add(sin32, dw32))
|
||||
), b32
|
||||
)
|
||||
);
|
||||
a = temp;
|
||||
}
|
||||
|
||||
// save original length
|
||||
var org_len = databytes.length;
|
||||
|
||||
// first append the "1" + 7x "0"
|
||||
databytes.push(0x80);
|
||||
|
||||
// determine required amount of padding
|
||||
var tail = databytes.length % 64
|
||||
// no room for msg length?
|
||||
if (tail > 56) {
|
||||
// pad to next 512 bit block
|
||||
for (var i = 0; i < (64 - tail); i++) {
|
||||
databytes.push(0x0);
|
||||
}
|
||||
tail = databytes.length % 64;
|
||||
}
|
||||
for (i = 0; i < (56 - tail); i++) {
|
||||
databytes.push(0x0);
|
||||
}
|
||||
// message length in bits mod 512 should now be 448
|
||||
// append 64 bit, little-endian original msg length (in *bits*!)
|
||||
databytes = databytes.concat(int64_to_bytes(org_len * 8));
|
||||
|
||||
// initialize 4x32 bit state
|
||||
var h0 = 0x67452301;
|
||||
var h1 = 0xEFCDAB89;
|
||||
var h2 = 0x98BADCFE;
|
||||
var h3 = 0x10325476;
|
||||
|
||||
// temp buffers
|
||||
var a = 0, b = 0, c = 0, d = 0;
|
||||
|
||||
// Digest message
|
||||
for (i = 0; i < databytes.length / 64; i++) {
|
||||
// initialize run
|
||||
a = h0;
|
||||
b = h1;
|
||||
c = h2;
|
||||
d = h3;
|
||||
|
||||
var ptr = i * 64;
|
||||
|
||||
// do 64 runs
|
||||
updateRun(fF(b, c, d), 0xd76aa478, bytes_to_int32(databytes, ptr), 7);
|
||||
updateRun(fF(b, c, d), 0xe8c7b756, bytes_to_int32(databytes, ptr + 4), 12);
|
||||
updateRun(fF(b, c, d), 0x242070db, bytes_to_int32(databytes, ptr + 8), 17);
|
||||
updateRun(fF(b, c, d), 0xc1bdceee, bytes_to_int32(databytes, ptr + 12), 22);
|
||||
updateRun(fF(b, c, d), 0xf57c0faf, bytes_to_int32(databytes, ptr + 16), 7);
|
||||
updateRun(fF(b, c, d), 0x4787c62a, bytes_to_int32(databytes, ptr + 20), 12);
|
||||
updateRun(fF(b, c, d), 0xa8304613, bytes_to_int32(databytes, ptr + 24), 17);
|
||||
updateRun(fF(b, c, d), 0xfd469501, bytes_to_int32(databytes, ptr + 28), 22);
|
||||
updateRun(fF(b, c, d), 0x698098d8, bytes_to_int32(databytes, ptr + 32), 7);
|
||||
updateRun(fF(b, c, d), 0x8b44f7af, bytes_to_int32(databytes, ptr + 36), 12);
|
||||
updateRun(fF(b, c, d), 0xffff5bb1, bytes_to_int32(databytes, ptr + 40), 17);
|
||||
updateRun(fF(b, c, d), 0x895cd7be, bytes_to_int32(databytes, ptr + 44), 22);
|
||||
updateRun(fF(b, c, d), 0x6b901122, bytes_to_int32(databytes, ptr + 48), 7);
|
||||
updateRun(fF(b, c, d), 0xfd987193, bytes_to_int32(databytes, ptr + 52), 12);
|
||||
updateRun(fF(b, c, d), 0xa679438e, bytes_to_int32(databytes, ptr + 56), 17);
|
||||
updateRun(fF(b, c, d), 0x49b40821, bytes_to_int32(databytes, ptr + 60), 22);
|
||||
updateRun(fG(b, c, d), 0xf61e2562, bytes_to_int32(databytes, ptr + 4), 5);
|
||||
updateRun(fG(b, c, d), 0xc040b340, bytes_to_int32(databytes, ptr + 24), 9);
|
||||
updateRun(fG(b, c, d), 0x265e5a51, bytes_to_int32(databytes, ptr + 44), 14);
|
||||
updateRun(fG(b, c, d), 0xe9b6c7aa, bytes_to_int32(databytes, ptr), 20);
|
||||
updateRun(fG(b, c, d), 0xd62f105d, bytes_to_int32(databytes, ptr + 20), 5);
|
||||
updateRun(fG(b, c, d), 0x2441453, bytes_to_int32(databytes, ptr + 40), 9);
|
||||
updateRun(fG(b, c, d), 0xd8a1e681, bytes_to_int32(databytes, ptr + 60), 14);
|
||||
updateRun(fG(b, c, d), 0xe7d3fbc8, bytes_to_int32(databytes, ptr + 16), 20);
|
||||
updateRun(fG(b, c, d), 0x21e1cde6, bytes_to_int32(databytes, ptr + 36), 5);
|
||||
updateRun(fG(b, c, d), 0xc33707d6, bytes_to_int32(databytes, ptr + 56), 9);
|
||||
updateRun(fG(b, c, d), 0xf4d50d87, bytes_to_int32(databytes, ptr + 12), 14);
|
||||
updateRun(fG(b, c, d), 0x455a14ed, bytes_to_int32(databytes, ptr + 32), 20);
|
||||
updateRun(fG(b, c, d), 0xa9e3e905, bytes_to_int32(databytes, ptr + 52), 5);
|
||||
updateRun(fG(b, c, d), 0xfcefa3f8, bytes_to_int32(databytes, ptr + 8), 9);
|
||||
updateRun(fG(b, c, d), 0x676f02d9, bytes_to_int32(databytes, ptr + 28), 14);
|
||||
updateRun(fG(b, c, d), 0x8d2a4c8a, bytes_to_int32(databytes, ptr + 48), 20);
|
||||
updateRun(fH(b, c, d), 0xfffa3942, bytes_to_int32(databytes, ptr + 20), 4);
|
||||
updateRun(fH(b, c, d), 0x8771f681, bytes_to_int32(databytes, ptr + 32), 11);
|
||||
updateRun(fH(b, c, d), 0x6d9d6122, bytes_to_int32(databytes, ptr + 44), 16);
|
||||
updateRun(fH(b, c, d), 0xfde5380c, bytes_to_int32(databytes, ptr + 56), 23);
|
||||
updateRun(fH(b, c, d), 0xa4beea44, bytes_to_int32(databytes, ptr + 4), 4);
|
||||
updateRun(fH(b, c, d), 0x4bdecfa9, bytes_to_int32(databytes, ptr + 16), 11);
|
||||
updateRun(fH(b, c, d), 0xf6bb4b60, bytes_to_int32(databytes, ptr + 28), 16);
|
||||
updateRun(fH(b, c, d), 0xbebfbc70, bytes_to_int32(databytes, ptr + 40), 23);
|
||||
updateRun(fH(b, c, d), 0x289b7ec6, bytes_to_int32(databytes, ptr + 52), 4);
|
||||
updateRun(fH(b, c, d), 0xeaa127fa, bytes_to_int32(databytes, ptr), 11);
|
||||
updateRun(fH(b, c, d), 0xd4ef3085, bytes_to_int32(databytes, ptr + 12), 16);
|
||||
updateRun(fH(b, c, d), 0x4881d05, bytes_to_int32(databytes, ptr + 24), 23);
|
||||
updateRun(fH(b, c, d), 0xd9d4d039, bytes_to_int32(databytes, ptr + 36), 4);
|
||||
updateRun(fH(b, c, d), 0xe6db99e5, bytes_to_int32(databytes, ptr + 48), 11);
|
||||
updateRun(fH(b, c, d), 0x1fa27cf8, bytes_to_int32(databytes, ptr + 60), 16);
|
||||
updateRun(fH(b, c, d), 0xc4ac5665, bytes_to_int32(databytes, ptr + 8), 23);
|
||||
updateRun(fI(b, c, d), 0xf4292244, bytes_to_int32(databytes, ptr), 6);
|
||||
updateRun(fI(b, c, d), 0x432aff97, bytes_to_int32(databytes, ptr + 28), 10);
|
||||
updateRun(fI(b, c, d), 0xab9423a7, bytes_to_int32(databytes, ptr + 56), 15);
|
||||
updateRun(fI(b, c, d), 0xfc93a039, bytes_to_int32(databytes, ptr + 20), 21);
|
||||
updateRun(fI(b, c, d), 0x655b59c3, bytes_to_int32(databytes, ptr + 48), 6);
|
||||
updateRun(fI(b, c, d), 0x8f0ccc92, bytes_to_int32(databytes, ptr + 12), 10);
|
||||
updateRun(fI(b, c, d), 0xffeff47d, bytes_to_int32(databytes, ptr + 40), 15);
|
||||
updateRun(fI(b, c, d), 0x85845dd1, bytes_to_int32(databytes, ptr + 4), 21);
|
||||
updateRun(fI(b, c, d), 0x6fa87e4f, bytes_to_int32(databytes, ptr + 32), 6);
|
||||
updateRun(fI(b, c, d), 0xfe2ce6e0, bytes_to_int32(databytes, ptr + 60), 10);
|
||||
updateRun(fI(b, c, d), 0xa3014314, bytes_to_int32(databytes, ptr + 24), 15);
|
||||
updateRun(fI(b, c, d), 0x4e0811a1, bytes_to_int32(databytes, ptr + 52), 21);
|
||||
updateRun(fI(b, c, d), 0xf7537e82, bytes_to_int32(databytes, ptr + 16), 6);
|
||||
updateRun(fI(b, c, d), 0xbd3af235, bytes_to_int32(databytes, ptr + 44), 10);
|
||||
updateRun(fI(b, c, d), 0x2ad7d2bb, bytes_to_int32(databytes, ptr + 8), 15);
|
||||
updateRun(fI(b, c, d), 0xeb86d391, bytes_to_int32(databytes, ptr + 36), 21);
|
||||
|
||||
// update buffers
|
||||
h0 = _add(h0, a);
|
||||
h1 = _add(h1, b);
|
||||
h2 = _add(h2, c);
|
||||
h3 = _add(h3, d);
|
||||
}
|
||||
// Done! Convert buffers to 128 bit (LE)
|
||||
return int128le_to_hex(h3, h2, h1, h0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// module.exports = MD5;
|
||||
192
network/downloader.ts
Normal file
192
network/downloader.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
//同个文件只会下载一次
|
||||
//并发下载数量限制
|
||||
//失败重试机制
|
||||
export class Downloader
|
||||
{
|
||||
private static _inst:Downloader;
|
||||
private _jsbDownloaderPool:any[];
|
||||
private _maxDownloadingCnt:number;
|
||||
private _downloadingCnt:number;
|
||||
private _downloadQueue:DownloadItem[]; //等待下载列表
|
||||
private _downloadedMap:Map<string, DownloadTask>; //已下载资源列表,url -> DownloadTask
|
||||
private _downloadingMap:Map<string, DownloadItem[]>; //下载同一个url的item列表
|
||||
|
||||
private constructor()
|
||||
{
|
||||
if(!CC_JSB)
|
||||
{
|
||||
cc.warn('Downloader is a NATIVE ONLY feature.');
|
||||
}
|
||||
this._maxDownloadingCnt = 2;
|
||||
this._downloadingCnt = 0;
|
||||
}
|
||||
|
||||
static getInst()
|
||||
{
|
||||
if(!this._inst)
|
||||
{
|
||||
this._inst = new Downloader();
|
||||
}
|
||||
return this._inst;
|
||||
}
|
||||
|
||||
downloadFile(item:DownloadItem)
|
||||
{
|
||||
const requestURL = item.requestURL;
|
||||
|
||||
//之前下载过
|
||||
const task = this._downloadedMap ? this._downloadedMap.get(requestURL) : null;
|
||||
if(task && item.onFileTaskSuccess)
|
||||
{
|
||||
item.onFileTaskSuccess(task);
|
||||
this.downloadQueueItem();
|
||||
return;
|
||||
}
|
||||
|
||||
//大于最大并发数量,需要等待当前item下载完成
|
||||
if(this._downloadingCnt >= this._maxDownloadingCnt)
|
||||
{
|
||||
this._downloadQueue = this._downloadQueue || [];
|
||||
this._downloadQueue.push(item);
|
||||
return;
|
||||
}
|
||||
|
||||
//同一url只下载一次
|
||||
this._downloadingMap = this._downloadingMap || new Map();
|
||||
let downloadingItems = this._downloadingMap.get(requestURL);
|
||||
if(!downloadingItems)
|
||||
{
|
||||
downloadingItems = [];
|
||||
this._downloadingMap.set(requestURL, downloadingItems);
|
||||
}
|
||||
downloadingItems.push(item);
|
||||
if(downloadingItems.length > 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//创建下载任务
|
||||
let jsbDownloader = this.popJsbDownloader();
|
||||
jsbDownloader.setOnTaskProgress(this.onTaskProgress.bind(this, requestURL));
|
||||
jsbDownloader.setOnFileTaskSuccess(this.onFileTaskSuccess.bind(this, requestURL, jsbDownloader));
|
||||
jsbDownloader.setOnTaskError(this.onTaskError.bind(this, requestURL, jsbDownloader));
|
||||
jsbDownloader.createDownloadFileTask(requestURL, item.storagePath);
|
||||
this._downloadingCnt++;
|
||||
cc.log(`Downloader, downloadFile, url=${requestURL}, downloadingCnt=${this._downloadingCnt}`);
|
||||
}
|
||||
|
||||
purge()
|
||||
{
|
||||
if(this._jsbDownloaderPool)
|
||||
{
|
||||
this._jsbDownloaderPool.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private downloadQueueItem()
|
||||
{
|
||||
if(!this._downloadQueue || this._downloadQueue.length < 1)
|
||||
{
|
||||
cc.log(`Downloader, no queue or all queue item had been downloaded`);
|
||||
return;
|
||||
}
|
||||
const item = this._downloadQueue.pop();
|
||||
this.downloadFile(item);
|
||||
}
|
||||
|
||||
private onTaskProgress(requestURL:string, task:DownloadTask, bytesReceived:number, totalBytesReceived:number, totalBytesExpected:number)
|
||||
{
|
||||
if(this._downloadingMap)
|
||||
{
|
||||
const downloadingItems = this._downloadingMap.get(requestURL);
|
||||
downloadingItems.forEach(item => {
|
||||
if(item.onTaskProgress) {
|
||||
item.onTaskProgress(task, bytesReceived, totalBytesReceived, totalBytesExpected);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private onFileTaskSuccess(requestURL:string, jsbDownloader, task:DownloadTask)
|
||||
{
|
||||
//保存下载后的地址
|
||||
this._downloadedMap = this._downloadedMap || new Map();
|
||||
this._downloadedMap.set(requestURL, task);
|
||||
|
||||
//通知downloadItem下载完成
|
||||
if(this._downloadingMap)
|
||||
{
|
||||
const downloadingItems = this._downloadingMap.get(requestURL);
|
||||
downloadingItems.forEach(item => {
|
||||
if(item.onFileTaskSuccess) {
|
||||
item.onFileTaskSuccess(task);
|
||||
}
|
||||
});
|
||||
downloadingItems.length = 0;
|
||||
this._downloadingMap.delete(requestURL);
|
||||
}
|
||||
|
||||
this.pushJsbDownloader(jsbDownloader);
|
||||
this._downloadingCnt--;
|
||||
cc.log(`Downloader, onFileTaskSuccess, url=${requestURL}, downloadingCnt=${this._downloadingCnt}`);
|
||||
this.downloadQueueItem();
|
||||
}
|
||||
|
||||
private onTaskError(requestURL:string, jsbDownloader, task:DownloadTask, errCode:number, errCodeInternal:number, errStr:string)
|
||||
{
|
||||
if(this._downloadingMap)
|
||||
{
|
||||
const downloadingItems = this._downloadingMap.get(requestURL);
|
||||
downloadingItems.forEach(item => {
|
||||
if(item.onTaskError) {
|
||||
item.onTaskError(task, errCode, errStr);
|
||||
}
|
||||
});
|
||||
downloadingItems.length = 0;
|
||||
this._downloadingMap.delete(requestURL);
|
||||
}
|
||||
|
||||
this.pushJsbDownloader(jsbDownloader);
|
||||
this._downloadingCnt--;
|
||||
cc.log(`Downloader, onTaskError, url=${requestURL}, downloadingCnt=${this._downloadingCnt}`);
|
||||
this.downloadQueueItem();
|
||||
}
|
||||
|
||||
private popJsbDownloader()
|
||||
{
|
||||
let jsbDownloader = null;
|
||||
if(this._jsbDownloaderPool)
|
||||
{
|
||||
jsbDownloader = this._jsbDownloaderPool.pop();
|
||||
}
|
||||
if(!jsbDownloader)
|
||||
{
|
||||
jsbDownloader = new jsb.Downloader();
|
||||
}
|
||||
return jsbDownloader;
|
||||
}
|
||||
|
||||
private pushJsbDownloader(jsbDownloader)
|
||||
{
|
||||
jsbDownloader.setOnTaskProgress(null);
|
||||
jsbDownloader.setOnFileTaskSuccess(null);
|
||||
jsbDownloader.setOnTaskError(null);
|
||||
this._jsbDownloaderPool = this._jsbDownloaderPool || [];
|
||||
this._jsbDownloaderPool.push(jsbDownloader);
|
||||
}
|
||||
}
|
||||
|
||||
interface DownloadItem
|
||||
{
|
||||
requestURL:string;
|
||||
storagePath:string;
|
||||
onTaskProgress?:(task:DownloadTask, bytesReceived:number, totalBytesReceived:number, totalBytesExpected:number) => void;
|
||||
onTaskError?:(task:DownloadTask, errCode:number, errStr:string) => void;
|
||||
onFileTaskSuccess?:(task:DownloadTask) => void;
|
||||
}
|
||||
|
||||
export interface DownloadTask
|
||||
{
|
||||
requestURL:string;
|
||||
storagePath:string;
|
||||
}
|
||||
153
network/httpservice.ts
Normal file
153
network/httpservice.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import { handler } from "../util";
|
||||
|
||||
export class HttpService
|
||||
{
|
||||
private static _inst:HttpService;
|
||||
|
||||
private constructor()
|
||||
{
|
||||
}
|
||||
|
||||
static getInst():HttpService
|
||||
{
|
||||
if(!this._inst)
|
||||
{
|
||||
this._inst = new HttpService();
|
||||
}
|
||||
return this._inst;
|
||||
}
|
||||
|
||||
doGet(url:string, headers, params, cb:handler)
|
||||
{
|
||||
if(params)
|
||||
{
|
||||
if(url.indexOf("?") == -1)
|
||||
{
|
||||
url += "?";
|
||||
}
|
||||
url += this.getQueryString(params);
|
||||
}
|
||||
this.doHttp(url, headers, null, "GET", cb);
|
||||
}
|
||||
|
||||
doPost(url:string, headers, params, cb:handler)
|
||||
{
|
||||
this.doHttp(url, headers, params, "POST", cb);
|
||||
}
|
||||
|
||||
doDownload()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private doHttp(url:string, headers, params, method:string, cb:handler)
|
||||
{
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.responseType = "text";
|
||||
xhr.onreadystatechange = this.onReadyStateChange.bind(this, xhr, cb);
|
||||
xhr.ontimeout = this.onTimeout.bind(this, xhr, url);
|
||||
xhr.onerror = this.onError.bind(this, xhr, url);
|
||||
xhr.onabort = this.onAbort.bind(this, xhr, url);
|
||||
|
||||
cc.log(`HttpService, doHttp url=${url}, method=${method}, parmas=${params}`)
|
||||
xhr.open(method, url, true);
|
||||
if(headers)
|
||||
{
|
||||
this.setHttpHeaders(xhr, headers);
|
||||
}
|
||||
if (cc.sys.isNative)
|
||||
{
|
||||
this.setHttpHeaders(xhr, {"Accept-Encoding": "gzip,deflate"});
|
||||
}
|
||||
if(params && typeof params === "object")
|
||||
{
|
||||
params = JSON.stringify(params);
|
||||
}
|
||||
xhr.send(params);
|
||||
}
|
||||
|
||||
private onReadyStateChange(xhr:XMLHttpRequest, cb:handler)
|
||||
{
|
||||
cc.log(`HttpService, onReadyStateChange, readyState=${xhr.readyState}, status=${xhr.status}`);
|
||||
if(xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 300)
|
||||
{
|
||||
cc.log(`HttpService, onReadyStateChange, responseText=${xhr.responseText}`);
|
||||
let data;
|
||||
let code = HttpCode.kUnknown;
|
||||
const response = JSON.parse(xhr.responseText);
|
||||
if(response && response.code)
|
||||
{
|
||||
code = response.code;
|
||||
data = response.content;
|
||||
}
|
||||
else
|
||||
{
|
||||
code = HttpCode.kSuccess;
|
||||
data = response;
|
||||
}
|
||||
this.notifyCallback(cb, code, data);
|
||||
this.removeXhrEvent(xhr);
|
||||
}
|
||||
}
|
||||
|
||||
private onTimeout(xhr:XMLHttpRequest, url:string)
|
||||
{
|
||||
cc.warn(`${url}, request ontimeout`);
|
||||
this.removeXhrEvent(xhr);
|
||||
}
|
||||
|
||||
private onError(xhr:XMLHttpRequest, url:string)
|
||||
{
|
||||
cc.warn(`${url}, request onerror`);
|
||||
this.removeXhrEvent(xhr);
|
||||
}
|
||||
|
||||
private onAbort(xhr:XMLHttpRequest, url:string)
|
||||
{
|
||||
cc.warn(`${url}, request onabort`);
|
||||
this.removeXhrEvent(xhr);
|
||||
}
|
||||
|
||||
private removeXhrEvent(xhr:XMLHttpRequest)
|
||||
{
|
||||
xhr.ontimeout = null;
|
||||
xhr.onerror = null;
|
||||
xhr.onabort = null;
|
||||
xhr.onreadystatechange = null;
|
||||
}
|
||||
|
||||
private notifyCallback(cb:handler, code:number, data?)
|
||||
{
|
||||
if(cb)
|
||||
{
|
||||
cb.exec(code, data);
|
||||
}
|
||||
}
|
||||
|
||||
private setHttpHeaders(xhr:XMLHttpRequest, headers)
|
||||
{
|
||||
for(let key in headers)
|
||||
{
|
||||
xhr.setRequestHeader(key, headers[key]);
|
||||
}
|
||||
}
|
||||
|
||||
private getQueryString(params)
|
||||
{
|
||||
const tmps:string[] = [];
|
||||
for(let key in params)
|
||||
{
|
||||
tmps.push(`${key}=${params[key]}`);
|
||||
}
|
||||
return tmps.join("&");
|
||||
}
|
||||
}
|
||||
|
||||
export enum HttpCode {
|
||||
kSuccess = 0,
|
||||
kTimeout = 10000,
|
||||
kUnknown = 10001,
|
||||
kSessionTimeout = -8,
|
||||
kIAmInBlocklist = -3013,
|
||||
kUserIsInMyBlocklist = -3014
|
||||
}
|
||||
120
pool/pool_mgr.ts
120
pool/pool_mgr.ts
@@ -1,61 +1,61 @@
|
||||
import {loader_mgr} from "../loader/loader_mgr"
|
||||
import {handler, gen_handler} from "../util"
|
||||
import {ui_pool} from "./ui_pool"
|
||||
|
||||
export class pool_mgr
|
||||
{
|
||||
private static _inst:pool_mgr;
|
||||
private ui_pool:ui_pool;
|
||||
|
||||
private constructor()
|
||||
{
|
||||
this.ui_pool = new ui_pool();
|
||||
}
|
||||
|
||||
static get_inst():pool_mgr
|
||||
{
|
||||
if(!this._inst)
|
||||
{
|
||||
this._inst = new pool_mgr();
|
||||
}
|
||||
return this._inst;
|
||||
}
|
||||
|
||||
get_ui(path:string, cb:handler):void
|
||||
{
|
||||
let ui:cc.Node = this.ui_pool.get(path);
|
||||
if(ui)
|
||||
{
|
||||
// cc.info("pool_mgr:get_ui from cache", path);
|
||||
cb.exec(ui);
|
||||
return;
|
||||
}
|
||||
// cc.info("pool_mgr:get_ui from loader", path);
|
||||
loader_mgr.get_inst().loadPrefabObj(path, cb);
|
||||
}
|
||||
|
||||
put_ui(path:string, ui:cc.Node):void
|
||||
{
|
||||
if(!ui)
|
||||
{
|
||||
cc.warn("pool_mgr:put_ui, invalid node");
|
||||
return;
|
||||
}
|
||||
this.ui_pool.put(path, ui);
|
||||
}
|
||||
|
||||
clear_atpath(path:string)
|
||||
{
|
||||
this.ui_pool.clear_atpath(path);
|
||||
}
|
||||
|
||||
clear()
|
||||
{
|
||||
this.ui_pool.clear();
|
||||
}
|
||||
|
||||
dump()
|
||||
{
|
||||
this.ui_pool.dump();
|
||||
}
|
||||
import {loader_mgr} from "../loader/loader_mgr"
|
||||
import {handler} from "../util"
|
||||
import {ui_pool} from "./ui_pool"
|
||||
|
||||
export class pool_mgr
|
||||
{
|
||||
private static inst:pool_mgr;
|
||||
private ui_pool:ui_pool;
|
||||
|
||||
private constructor()
|
||||
{
|
||||
this.ui_pool = new ui_pool();
|
||||
}
|
||||
|
||||
static get_inst()
|
||||
{
|
||||
if(!this.inst)
|
||||
{
|
||||
this.inst = new pool_mgr();
|
||||
}
|
||||
return this.inst;
|
||||
}
|
||||
|
||||
get_ui(path:string, cb:handler)
|
||||
{
|
||||
const ui:cc.Node = this.ui_pool.get(path);
|
||||
if(cc.isValid(ui))
|
||||
{
|
||||
// cc.log("pool_mgr:get_ui from cache", path);
|
||||
cb.exec(ui);
|
||||
return;
|
||||
}
|
||||
// cc.log("pool_mgr:get_ui from loader", path);
|
||||
loader_mgr.get_inst().loadPrefabObj(path, cb);
|
||||
}
|
||||
|
||||
put_ui(path:string, ui:cc.Node, destroyAtOnce = false)
|
||||
{
|
||||
if(!cc.isValid(ui))
|
||||
{
|
||||
cc.warn("pool_mgr:put_ui, invalid node");
|
||||
return;
|
||||
}
|
||||
this.ui_pool.put(path, ui, destroyAtOnce);
|
||||
}
|
||||
|
||||
clear_atpath(path:string)
|
||||
{
|
||||
this.ui_pool.clear_atpath(path);
|
||||
}
|
||||
|
||||
clear()
|
||||
{
|
||||
this.ui_pool.clear();
|
||||
}
|
||||
|
||||
dump()
|
||||
{
|
||||
this.ui_pool.dump();
|
||||
}
|
||||
}
|
||||
203
pool/ui_pool.ts
203
pool/ui_pool.ts
@@ -1,102 +1,103 @@
|
||||
//lru(last recently used) cache
|
||||
export class ui_pool
|
||||
{
|
||||
private cache:any; //path => cc.Node[]
|
||||
private path2time:any;
|
||||
private size:number;
|
||||
private max_size:number = 2;
|
||||
|
||||
constructor()
|
||||
{
|
||||
this.cache = {};
|
||||
this.path2time = {};
|
||||
this.size = 0;
|
||||
}
|
||||
|
||||
get(path:string):cc.Node
|
||||
{
|
||||
let uis:cc.Node[] = this.cache[path];
|
||||
if(uis && uis.length > 0)
|
||||
{
|
||||
this.size--;
|
||||
return uis.pop();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
put(path:string, ui:cc.Node):void
|
||||
{
|
||||
if(this.size >= this.max_size)
|
||||
{
|
||||
//删除最早的缓存
|
||||
let del_path:string;
|
||||
let ts:number = cc.sys.now();
|
||||
for(let p in this.cache)
|
||||
{
|
||||
if(this.cache[p].length > 0 && this.path2time[p] < ts)
|
||||
{
|
||||
ts = this.path2time[p];
|
||||
del_path = p;
|
||||
}
|
||||
}
|
||||
if(del_path && del_path != "")
|
||||
{
|
||||
let del_ui:cc.Node = this.cache[del_path].pop();
|
||||
del_ui.destroy();
|
||||
this.size--;
|
||||
// cc.info("ui_pool:pool capacity is max, destroy old ui,", del_path);
|
||||
}
|
||||
}
|
||||
let uis:cc.Node[] = this.cache[path];
|
||||
if(!uis)
|
||||
{
|
||||
this.cache[path] = uis = [];
|
||||
}
|
||||
ui.removeFromParent(false);
|
||||
uis.push(ui);
|
||||
this.size++;
|
||||
this.path2time[path] = cc.sys.now();
|
||||
}
|
||||
|
||||
clear_atpath(path:string):void
|
||||
{
|
||||
let uis:cc.Node[] = this.cache[path];
|
||||
if(!uis || uis.length <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
while(uis.length > 0)
|
||||
{
|
||||
let ui:cc.Node = uis.pop();
|
||||
ui.destroy();
|
||||
this.size--;
|
||||
}
|
||||
}
|
||||
|
||||
clear():void
|
||||
{
|
||||
for(let path in this.cache)
|
||||
{
|
||||
this.clear_atpath(path);
|
||||
}
|
||||
this.cache = {};
|
||||
this.path2time = {};
|
||||
if(this.size != 0)
|
||||
{
|
||||
cc.warn("size should be 0, but now is", this.size);
|
||||
}
|
||||
}
|
||||
|
||||
dump()
|
||||
{
|
||||
let str:string = "********ui_pool dump********";
|
||||
for(let path in this.cache)
|
||||
{
|
||||
str += "\n" + path + "\n";
|
||||
this.cache[path].forEach((u:cc.Node):void=>{
|
||||
str += u.name + ",";
|
||||
});
|
||||
}
|
||||
cc.info(str);
|
||||
}
|
||||
const DestroyAtOnce = false;
|
||||
|
||||
//lru(last recently used) cache
|
||||
export class ui_pool
|
||||
{
|
||||
private path2nodes:Map<string, cc.Node[]>; //path => cc.Node[]
|
||||
private path2time:Map<string, number>;
|
||||
private size:number;
|
||||
private max_size = 2;
|
||||
|
||||
constructor()
|
||||
{
|
||||
this.path2nodes = new Map();
|
||||
this.path2time = new Map();
|
||||
this.size = 0;
|
||||
}
|
||||
|
||||
get(path:string)
|
||||
{
|
||||
const nodes = this.path2nodes.get(path);
|
||||
if(nodes && nodes.length > 0)
|
||||
{
|
||||
this.size--;
|
||||
return nodes.pop();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
put(path:string, node:cc.Node, destroyAtOnce:boolean)
|
||||
{
|
||||
if(DestroyAtOnce || destroyAtOnce)
|
||||
{
|
||||
node.destroy();
|
||||
return;
|
||||
}
|
||||
if(this.size >= this.max_size)
|
||||
{
|
||||
//删除最早的缓存
|
||||
let del_path:string;
|
||||
let ts = cc.sys.now();
|
||||
this.path2nodes.forEach((nodes, p) => {
|
||||
if(nodes.length > 0 && this.path2time.get(p) < ts)
|
||||
{
|
||||
ts = this.path2time.get(p);
|
||||
del_path = p;
|
||||
}
|
||||
});
|
||||
if(del_path)
|
||||
{
|
||||
const del_node = this.path2nodes.get(del_path).pop();
|
||||
del_node.destroy();
|
||||
this.size--;
|
||||
cc.log("ui_pool:pool capacity is max, destroy old ui,", del_path);
|
||||
}
|
||||
}
|
||||
let nodes = this.path2nodes.get(path);
|
||||
if(!nodes)
|
||||
{
|
||||
nodes = [];
|
||||
this.path2nodes.set(path, nodes);
|
||||
}
|
||||
node.removeFromParent(false);
|
||||
nodes.push(node);
|
||||
this.size++;
|
||||
this.path2time.set(path, cc.sys.now());
|
||||
}
|
||||
|
||||
clear_atpath(path:string):void
|
||||
{
|
||||
const nodes = this.path2nodes.get(path);
|
||||
while(nodes && nodes.length > 0)
|
||||
{
|
||||
const node = nodes.pop();
|
||||
node.destroy();
|
||||
this.size--;
|
||||
}
|
||||
}
|
||||
|
||||
clear():void
|
||||
{
|
||||
this.path2nodes.forEach((_, path) => {
|
||||
this.clear_atpath(path);
|
||||
});
|
||||
this.path2nodes.clear();
|
||||
this.path2time.clear();
|
||||
if(this.size != 0)
|
||||
{
|
||||
cc.warn("size should be 0, but now is", this.size);
|
||||
}
|
||||
}
|
||||
|
||||
dump()
|
||||
{
|
||||
let str:string = "********ui_pool dump********";
|
||||
this.path2nodes.forEach((nodes, path) => {
|
||||
str += "\n" + path + "\n";
|
||||
nodes.forEach(node => {
|
||||
str += node.name + ",";
|
||||
});
|
||||
});
|
||||
cc.log(str);
|
||||
}
|
||||
}
|
||||
@@ -1,175 +1,196 @@
|
||||
import {handler, gen_handler} from "../util"
|
||||
import {LinkList, LinkListNode} from "../linklist"
|
||||
|
||||
export class TimerMgr
|
||||
{
|
||||
private static inst:TimerMgr;
|
||||
private iterList:LinkList<TimerHandler>;
|
||||
private pendingList:LinkList<TimerHandler>;
|
||||
private pool:TimerHandler[];
|
||||
private key:number;
|
||||
|
||||
private constructor()
|
||||
{
|
||||
this.key = 0;
|
||||
this.pool = [];
|
||||
this.iterList = new LinkList<TimerHandler>();
|
||||
this.pendingList = new LinkList<TimerHandler>();
|
||||
}
|
||||
|
||||
static getInst():TimerMgr
|
||||
{
|
||||
if(!this.inst)
|
||||
{
|
||||
this.inst = new TimerMgr();
|
||||
}
|
||||
return this.inst;
|
||||
}
|
||||
|
||||
add(interval:number, delay:number, repeat:number, cb:handler, is_updater:boolean = false):number
|
||||
{
|
||||
let th = this.pool.pop();
|
||||
if(th)
|
||||
{
|
||||
th.interval = interval;
|
||||
th.delay = delay;
|
||||
th.repeat = repeat;
|
||||
th.elapsed = 0;
|
||||
th.times = 0;
|
||||
th.is_updater = is_updater;
|
||||
th.cb = cb;
|
||||
}
|
||||
else
|
||||
{
|
||||
th = {interval, delay, repeat, elapsed:0, times:0, is_updater, cb};
|
||||
}
|
||||
const key = this.pendingList.append(++this.key, th);
|
||||
// cc.info(`[TimerMgr]addPending, key=${key}`);
|
||||
return key;
|
||||
}
|
||||
|
||||
remove(key:number)
|
||||
{
|
||||
if(!this.removeIter(key))
|
||||
{
|
||||
this.removePending(key);
|
||||
}
|
||||
}
|
||||
|
||||
private removeIter(key:number)
|
||||
{
|
||||
const node = this.iterList.remove(key);
|
||||
if(node)
|
||||
{
|
||||
this.pool.push(node.data);
|
||||
// cc.info(`[TimerMgr]removeIter, key=${key}`);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private removePending(key:number)
|
||||
{
|
||||
const node = this.pendingList.remove(key);
|
||||
if(node)
|
||||
{
|
||||
this.pool.push(node.data);
|
||||
// cc.info(`[TimerMgr]removePending, key=${key}`);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
loop(interval:number, cb:handler):number
|
||||
{
|
||||
return this.add(interval, 0, 0, cb);
|
||||
}
|
||||
|
||||
loopTimes(interval:number, repeat:number, cb:handler):number
|
||||
{
|
||||
return this.add(interval, 0, repeat, cb);
|
||||
}
|
||||
|
||||
frameLoop(cb:handler):number
|
||||
{
|
||||
return this.add(1/24, 0, 0, cb);
|
||||
}
|
||||
|
||||
delayLoop(interval:number, delay:number, cb:handler):number
|
||||
{
|
||||
return this.add(interval, delay, 0, cb);
|
||||
}
|
||||
|
||||
once(delay:number, cb:handler):number
|
||||
{
|
||||
return this.add(0, delay, 1, cb);
|
||||
}
|
||||
|
||||
add_updater(cb:handler):number
|
||||
{
|
||||
return this.add(0, 0, 0, cb, true);
|
||||
}
|
||||
|
||||
update(dt:number)
|
||||
{
|
||||
let node = this.iterList.head;
|
||||
|
||||
//执行当前帧的定时器
|
||||
while(node)
|
||||
{
|
||||
if(node.data.is_updater)
|
||||
{
|
||||
//先保存next引用,防止回调函数里回收node导致next被修改
|
||||
const next = node.next;
|
||||
node.data.cb.exec(dt);
|
||||
node = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(node.data.repeat != 0 && node.data.times >= node.data.repeat)
|
||||
{
|
||||
const next = node.next;
|
||||
this.removeIter(node.key);
|
||||
node = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(node.data.elapsed >= node.data.delay + node.data.interval)
|
||||
{
|
||||
//exec回调可能会调用remove函数回收timerHandler.避免操作已回收的对象。
|
||||
// cc.info(`[TimerMgr]execHandler, key=${node.key}`);
|
||||
const next = node.next;
|
||||
node.data.times++;
|
||||
node.data.elapsed = node.data.delay;
|
||||
node.data.cb.exec();
|
||||
node = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
node.data.elapsed += dt;
|
||||
node = node.next;
|
||||
}
|
||||
}
|
||||
|
||||
//添加下一帧的定时器
|
||||
node = this.pendingList.head;
|
||||
while(node)
|
||||
{
|
||||
const key = node.key;
|
||||
const th = node.data;
|
||||
node = node.next;
|
||||
this.pendingList.remove(key);
|
||||
this.iterList.append(key, th);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type TimerHandler = {
|
||||
interval:number; //执行间隔, 只执行一次的定时器值为0,单位秒
|
||||
delay:number; //延时多久执行,单位秒
|
||||
repeat:number; //要执行多少次,0表示无限次
|
||||
elapsed:number; //已过去的时间
|
||||
times:number; //已执行次数
|
||||
is_updater:boolean; //是否每帧调用
|
||||
cb:handler; //回调函数
|
||||
import {handler} from "../util"
|
||||
import {LinkList} from "../linklist"
|
||||
|
||||
export class TimerMgr
|
||||
{
|
||||
private static inst:TimerMgr;
|
||||
private iterList:LinkList<TimerHandler>;
|
||||
private pendingList:LinkList<TimerHandler>;
|
||||
private pool:TimerHandler[];
|
||||
private key:number;
|
||||
|
||||
private constructor()
|
||||
{
|
||||
this.key = 0;
|
||||
this.pool = [];
|
||||
this.iterList = new LinkList<TimerHandler>();
|
||||
this.pendingList = new LinkList<TimerHandler>();
|
||||
}
|
||||
|
||||
static getInst():TimerMgr
|
||||
{
|
||||
if(!this.inst) {
|
||||
this.inst = new TimerMgr();
|
||||
}
|
||||
return this.inst;
|
||||
}
|
||||
|
||||
add(interval:number, delay:number, repeat:number, cb:handler, is_updater = false, tag:string = null, target:cc.Node = null):number
|
||||
{
|
||||
let th = this.pool.pop();
|
||||
if(!th) {
|
||||
th = new TimerHandler();
|
||||
}
|
||||
th.init(interval, delay, repeat, 0, 0, is_updater, cb, tag, target);
|
||||
return this.pendingList.append(++this.key, th);
|
||||
}
|
||||
|
||||
remove(key:number)
|
||||
{
|
||||
if(!this.removeIter(key)) {
|
||||
this.removePending(key);
|
||||
}
|
||||
}
|
||||
|
||||
private removeIter(key:number)
|
||||
{
|
||||
const node = this.iterList.remove(key);
|
||||
if(node) {
|
||||
this.pool.push(node.data);
|
||||
node.data = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private removePending(key:number)
|
||||
{
|
||||
const node = this.pendingList.remove(key);
|
||||
if(node) {
|
||||
this.pool.push(node.data);
|
||||
node.data = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
loop(interval:number, cb:handler, tag:string = null, target:cc.Node = null):number
|
||||
{
|
||||
return this.add(interval, 0, 0, cb, false, tag, target);
|
||||
}
|
||||
|
||||
loopTimes(interval:number, repeat:number, cb:handler, tag:string = null, target:cc.Node = null):number
|
||||
{
|
||||
return this.add(interval, 0, repeat, cb, false, tag, target);
|
||||
}
|
||||
|
||||
frameLoop(cb:handler, tag:string = null, target:cc.Node = null):number
|
||||
{
|
||||
return this.add(1/60, 0, 0, cb, false, tag, target);
|
||||
}
|
||||
|
||||
delayLoop(interval:number, delay:number, cb:handler, tag:string = null, target:cc.Node = null):number
|
||||
{
|
||||
return this.add(interval, delay, 0, cb, false, tag, target);
|
||||
}
|
||||
|
||||
once(delay:number, cb:handler, tag:string = null, target:cc.Node = null):number
|
||||
{
|
||||
return this.add(0, delay, 1, cb, false, tag, target);
|
||||
}
|
||||
|
||||
add_updater(cb:handler, tag:string = null, target:cc.Node = null):number
|
||||
{
|
||||
return this.add(0, 0, 0, cb, true, tag, target);
|
||||
}
|
||||
|
||||
update(dt:number)
|
||||
{
|
||||
let node = this.iterList.head;
|
||||
|
||||
//执行当前帧的定时器
|
||||
while(node) {
|
||||
const timerHandler = node.data;
|
||||
// cc.log(`timer update, key=${timerHandler.tag}`);
|
||||
|
||||
//目标已失效
|
||||
if(!timerHandler || !timerHandler.isValid()) {
|
||||
const next = node.next;
|
||||
this.removeIter(node.key);
|
||||
node = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
const repeat = timerHandler.repeat;
|
||||
const delay = timerHandler.delay;
|
||||
const interval = timerHandler.interval;
|
||||
const times = timerHandler.times;
|
||||
const elapsed = timerHandler.elapsed;
|
||||
const cb = timerHandler.cb;
|
||||
|
||||
if(timerHandler.is_updater) {
|
||||
//先保存next引用,防止回调函数里回收node导致next被修改
|
||||
const next = node.next;
|
||||
cb.exec(dt);
|
||||
node = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(repeat != 0 && times >= repeat) {
|
||||
const next = node.next;
|
||||
this.removeIter(node.key);
|
||||
node = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(elapsed >= delay + interval) {
|
||||
//exec回调可能会调用remove函数回收timerHandler.避免操作已回收的对象。
|
||||
const next = node.next;
|
||||
timerHandler.times++;
|
||||
timerHandler.elapsed = delay;
|
||||
cb.exec();
|
||||
node = next;
|
||||
}
|
||||
else {
|
||||
timerHandler.elapsed += dt;
|
||||
node = node.next;
|
||||
}
|
||||
}
|
||||
|
||||
//添加下一帧的定时器
|
||||
node = this.pendingList.head;
|
||||
while(node) {
|
||||
const key = node.key;
|
||||
const th = node.data;
|
||||
node = node.next;
|
||||
this.pendingList.remove(key);
|
||||
this.iterList.append(key, th);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TimerHandler
|
||||
{
|
||||
public interval:number;
|
||||
public delay:number;
|
||||
public repeat:number;
|
||||
public elapsed:number;
|
||||
public times:number;
|
||||
public is_updater:boolean;
|
||||
public cb:handler;
|
||||
public tag:string;
|
||||
public target:cc.Node;
|
||||
private _hasTarget:boolean;
|
||||
|
||||
init(interval:number, delay:number, repeat:number, elapsed:number, times:number, is_updater:boolean, cb:handler, tag:string, target:cc.Node)
|
||||
{
|
||||
this.interval = interval;
|
||||
this.delay = delay;
|
||||
this.repeat = repeat;
|
||||
this.elapsed = elapsed;
|
||||
this.times = times;
|
||||
this.is_updater = is_updater;
|
||||
this.cb = cb;
|
||||
this.tag = tag;
|
||||
this.target = target;
|
||||
this._hasTarget = target != null;
|
||||
}
|
||||
|
||||
isValid()
|
||||
{
|
||||
if(!this._hasTarget) {
|
||||
return true;
|
||||
}
|
||||
return cc.isValid(this.target) && this.target.activeInHierarchy;
|
||||
}
|
||||
}
|
||||
63
tree/nodebase.ts
Normal file
63
tree/nodebase.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
type NodeFinishedCb = (...params) => void;
|
||||
|
||||
export class NodeBase
|
||||
{
|
||||
private _next:NodeBase;
|
||||
private _finishedCb:NodeFinishedCb;
|
||||
|
||||
constructor()
|
||||
{
|
||||
}
|
||||
|
||||
protected onEnter(...params)
|
||||
{
|
||||
}
|
||||
|
||||
protected execute(...params)
|
||||
{
|
||||
}
|
||||
|
||||
protected onExit(...params)
|
||||
{
|
||||
}
|
||||
|
||||
start(...params)
|
||||
{
|
||||
this.onEnter(...params);
|
||||
this.execute(...params);
|
||||
}
|
||||
|
||||
protected continue(...params)
|
||||
{
|
||||
this.onExit(...params);
|
||||
if(this._finishedCb)
|
||||
{
|
||||
this._finishedCb(...params);
|
||||
}
|
||||
if(this._next)
|
||||
{
|
||||
this._next.start(...params);
|
||||
}
|
||||
}
|
||||
|
||||
protected break()
|
||||
{
|
||||
this.onExit();
|
||||
this._next = null;
|
||||
}
|
||||
|
||||
setNext(next:NodeBase)
|
||||
{
|
||||
this._next = next;
|
||||
}
|
||||
|
||||
setFinishedCb(cb:NodeFinishedCb)
|
||||
{
|
||||
this._finishedCb = cb;
|
||||
}
|
||||
|
||||
protected getName()
|
||||
{
|
||||
return "NodeBase";
|
||||
}
|
||||
}
|
||||
46
tree/nodesequence.ts
Normal file
46
tree/nodesequence.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { NodeBase } from "./nodebase";
|
||||
|
||||
//子node依次执行,最后执行自身continue
|
||||
export class NodeSequence extends NodeBase
|
||||
{
|
||||
private _nodes:NodeBase[];
|
||||
|
||||
constructor(...nodes:NodeBase[])
|
||||
{
|
||||
super();
|
||||
this._nodes = nodes;
|
||||
this.linkNodes();
|
||||
}
|
||||
|
||||
protected execute()
|
||||
{
|
||||
if(this._nodes.length > 0)
|
||||
{
|
||||
this._nodes[0].start();
|
||||
}
|
||||
}
|
||||
|
||||
appendNode(node:NodeBase)
|
||||
{
|
||||
this._nodes.push(node);
|
||||
}
|
||||
|
||||
linkNodes()
|
||||
{
|
||||
const nodes = this._nodes;
|
||||
for(let i = 0, len = nodes.length; i < len - 1; i++)
|
||||
{
|
||||
nodes[i].setNext(nodes[i + 1]);
|
||||
nodes[i].setFinishedCb(null);
|
||||
}
|
||||
if(nodes.length > 0)
|
||||
{
|
||||
nodes[nodes.length - 1].setFinishedCb(this.continue.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
protected getName()
|
||||
{
|
||||
return "NodeSequence";
|
||||
}
|
||||
}
|
||||
57
tree/nodespawn.ts
Normal file
57
tree/nodespawn.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { NodeBase } from "./nodebase";
|
||||
|
||||
//子node并行执行,等待它们全部执行完毕才执行自身continue
|
||||
export class NodeSpawn extends NodeBase
|
||||
{
|
||||
private _nodes:NodeBase[];
|
||||
private _waitCount:number;
|
||||
|
||||
constructor(...nodes:NodeBase[])
|
||||
{
|
||||
super()
|
||||
this._nodes = nodes;
|
||||
this.arrageNodes();
|
||||
}
|
||||
|
||||
protected execute()
|
||||
{
|
||||
this._nodes.forEach(node => {
|
||||
node.start();
|
||||
});
|
||||
}
|
||||
|
||||
appendNode(node:NodeBase)
|
||||
{
|
||||
this._nodes.push(node);
|
||||
this.arrageNodes();
|
||||
}
|
||||
|
||||
insertNodes(index:number, ...node:NodeBase[])
|
||||
{
|
||||
this._nodes.splice(index, 0, ...node);
|
||||
this.arrageNodes();
|
||||
}
|
||||
|
||||
private arrageNodes()
|
||||
{
|
||||
const nodes = this._nodes;
|
||||
this._waitCount = nodes.length;
|
||||
nodes.forEach(node => {
|
||||
node.setFinishedCb(this.onChildNodeFinished.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
private onChildNodeFinished()
|
||||
{
|
||||
this._waitCount--;
|
||||
if(this._waitCount == 0)
|
||||
{
|
||||
this.continue();
|
||||
}
|
||||
}
|
||||
|
||||
protected getName()
|
||||
{
|
||||
return "NodeSpawn";
|
||||
}
|
||||
}
|
||||
@@ -1,151 +1,151 @@
|
||||
/*
|
||||
t:timestamp,动画执行到当前帧花费的时间
|
||||
b:begining,起始值
|
||||
c:change,需要变化的量
|
||||
d:duration,动画的总时间
|
||||
参见:http://www.cnblogs.com/bluedream2009/archive/2010/06/19/1760909.html
|
||||
*/
|
||||
export let TweenFunc = {
|
||||
Linear: function(t,b,c,d){ return c*t/d + b; },
|
||||
Quad: {
|
||||
easeIn: function(t,b,c,d){
|
||||
return c*(t/=d)*t + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d){
|
||||
return -c *(t/=d)*(t-2) + b;
|
||||
},
|
||||
easeInOut: function(t,b,c,d){
|
||||
if ((t/=d/2) < 1) return c/2*t*t + b;
|
||||
return -c/2 * ((--t)*(t-2) - 1) + b;
|
||||
}
|
||||
},
|
||||
Cubic: {
|
||||
easeIn: function(t,b,c,d){
|
||||
return c*(t/=d)*t*t + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d){
|
||||
return c*((t=t/d-1)*t*t + 1) + b;
|
||||
},
|
||||
easeInOut: function(t,b,c,d){
|
||||
if ((t/=d/2) < 1) return c/2*t*t*t + b;
|
||||
return c/2*((t-=2)*t*t + 2) + b;
|
||||
}
|
||||
},
|
||||
Quart: {
|
||||
easeIn: function(t,b,c,d){
|
||||
return c*(t/=d)*t*t*t + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d){
|
||||
return -c * ((t=t/d-1)*t*t*t - 1) + b;
|
||||
},
|
||||
easeInOut: function(t,b,c,d){
|
||||
if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
|
||||
return -c/2 * ((t-=2)*t*t*t - 2) + b;
|
||||
}
|
||||
},
|
||||
Quint: {
|
||||
easeIn: function(t,b,c,d){
|
||||
return c*(t/=d)*t*t*t*t + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d){
|
||||
return c*((t=t/d-1)*t*t*t*t + 1) + b;
|
||||
},
|
||||
easeInOut: function(t,b,c,d){
|
||||
if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
|
||||
return c/2*((t-=2)*t*t*t*t + 2) + b;
|
||||
}
|
||||
},
|
||||
Sine: {
|
||||
easeIn: function(t,b,c,d){
|
||||
return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d){
|
||||
return c * Math.sin(t/d * (Math.PI/2)) + b;
|
||||
},
|
||||
easeInOut: function(t,b,c,d){
|
||||
return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
|
||||
}
|
||||
},
|
||||
Expo: {
|
||||
easeIn: function(t,b,c,d){
|
||||
return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d){
|
||||
return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
|
||||
},
|
||||
easeInOut: function(t,b,c,d){
|
||||
if (t==0) return b;
|
||||
if (t==d) return b+c;
|
||||
if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
|
||||
return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
|
||||
}
|
||||
},
|
||||
Circ: {
|
||||
easeIn: function(t,b,c,d){
|
||||
return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d){
|
||||
return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
|
||||
},
|
||||
easeInOut: function(t,b,c,d){
|
||||
if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
|
||||
return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
|
||||
}
|
||||
},
|
||||
Elastic: {
|
||||
easeIn: function(t,b,c,d,a,p){
|
||||
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
|
||||
if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
|
||||
else var s = p/(2*Math.PI) * Math.asin (c/a);
|
||||
return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d,a,p){
|
||||
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
|
||||
if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
|
||||
else var s = p/(2*Math.PI) * Math.asin (c/a);
|
||||
return (a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b);
|
||||
},
|
||||
easeInOut: function(t,b,c,d,a,p){
|
||||
if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5);
|
||||
if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
|
||||
else var s = p/(2*Math.PI) * Math.asin (c/a);
|
||||
if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
|
||||
return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
|
||||
}
|
||||
},
|
||||
Back: {
|
||||
easeIn: function(t,b,c,d,s){
|
||||
if (s == undefined) s = 1.70158;
|
||||
return c*(t/=d)*t*((s+1)*t - s) + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d,s){
|
||||
if (s == undefined) s = 1.70158;
|
||||
return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
|
||||
},
|
||||
easeInOut: function(t,b,c,d,s){
|
||||
if (s == undefined) s = 1.70158;
|
||||
if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
|
||||
return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
|
||||
}
|
||||
},
|
||||
Bounce: {
|
||||
easeIn: function(t,b,c,d){
|
||||
return c - TweenFunc.Bounce.easeOut(d-t, 0, c, d) + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d){
|
||||
if ((t/=d) < (1/2.75)) {
|
||||
return c*(7.5625*t*t) + b;
|
||||
} else if (t < (2/2.75)) {
|
||||
return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
|
||||
} else if (t < (2.5/2.75)) {
|
||||
return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
|
||||
} else {
|
||||
return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
|
||||
}
|
||||
},
|
||||
easeInOut: function(t,b,c,d){
|
||||
if (t < d/2) return TweenFunc.Bounce.easeIn(t*2, 0, c, d) * .5 + b;
|
||||
else return TweenFunc.Bounce.easeOut(t*2-d, 0, c, d) * .5 + c*.5 + b;
|
||||
}
|
||||
}
|
||||
/*
|
||||
t:timestamp,动画执行到当前帧花费的时间
|
||||
b:begining,起始值
|
||||
c:change,需要变化的量
|
||||
d:duration,动画的总时间
|
||||
参见:http://www.cnblogs.com/bluedream2009/archive/2010/06/19/1760909.html
|
||||
*/
|
||||
export let TweenFunc = {
|
||||
Linear: function(t,b,c,d){ return c*t/d + b; },
|
||||
Quad: {
|
||||
easeIn: function(t,b,c,d){
|
||||
return c*(t/=d)*t + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d){
|
||||
return -c *(t/=d)*(t-2) + b;
|
||||
},
|
||||
easeInOut: function(t,b,c,d){
|
||||
if ((t/=d/2) < 1) return c/2*t*t + b;
|
||||
return -c/2 * ((--t)*(t-2) - 1) + b;
|
||||
}
|
||||
},
|
||||
Cubic: {
|
||||
easeIn: function(t,b,c,d){
|
||||
return c*(t/=d)*t*t + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d){
|
||||
return c*((t=t/d-1)*t*t + 1) + b;
|
||||
},
|
||||
easeInOut: function(t,b,c,d){
|
||||
if ((t/=d/2) < 1) return c/2*t*t*t + b;
|
||||
return c/2*((t-=2)*t*t + 2) + b;
|
||||
}
|
||||
},
|
||||
Quart: {
|
||||
easeIn: function(t,b,c,d){
|
||||
return c*(t/=d)*t*t*t + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d){
|
||||
return -c * ((t=t/d-1)*t*t*t - 1) + b;
|
||||
},
|
||||
easeInOut: function(t,b,c,d){
|
||||
if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
|
||||
return -c/2 * ((t-=2)*t*t*t - 2) + b;
|
||||
}
|
||||
},
|
||||
Quint: {
|
||||
easeIn: function(t,b,c,d){
|
||||
return c*(t/=d)*t*t*t*t + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d){
|
||||
return c*((t=t/d-1)*t*t*t*t + 1) + b;
|
||||
},
|
||||
easeInOut: function(t,b,c,d){
|
||||
if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
|
||||
return c/2*((t-=2)*t*t*t*t + 2) + b;
|
||||
}
|
||||
},
|
||||
Sine: {
|
||||
easeIn: function(t,b,c,d){
|
||||
return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d){
|
||||
return c * Math.sin(t/d * (Math.PI/2)) + b;
|
||||
},
|
||||
easeInOut: function(t,b,c,d){
|
||||
return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
|
||||
}
|
||||
},
|
||||
Expo: {
|
||||
easeIn: function(t,b,c,d){
|
||||
return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d){
|
||||
return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
|
||||
},
|
||||
easeInOut: function(t,b,c,d){
|
||||
if (t==0) return b;
|
||||
if (t==d) return b+c;
|
||||
if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
|
||||
return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
|
||||
}
|
||||
},
|
||||
Circ: {
|
||||
easeIn: function(t,b,c,d){
|
||||
return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d){
|
||||
return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
|
||||
},
|
||||
easeInOut: function(t,b,c,d){
|
||||
if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
|
||||
return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
|
||||
}
|
||||
},
|
||||
Elastic: {
|
||||
easeIn: function(t,b,c,d,a,p){
|
||||
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
|
||||
if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
|
||||
else var s = p/(2*Math.PI) * Math.asin (c/a);
|
||||
return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d,a,p){
|
||||
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
|
||||
if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
|
||||
else var s = p/(2*Math.PI) * Math.asin (c/a);
|
||||
return (a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b);
|
||||
},
|
||||
easeInOut: function(t,b,c,d,a,p){
|
||||
if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5);
|
||||
if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
|
||||
else var s = p/(2*Math.PI) * Math.asin (c/a);
|
||||
if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
|
||||
return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
|
||||
}
|
||||
},
|
||||
Back: {
|
||||
easeIn: function(t,b,c,d,s){
|
||||
if (s == undefined) s = 1.70158;
|
||||
return c*(t/=d)*t*((s+1)*t - s) + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d,s){
|
||||
if (s == undefined) s = 1.70158;
|
||||
return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
|
||||
},
|
||||
easeInOut: function(t,b,c,d,s){
|
||||
if (s == undefined) s = 1.70158;
|
||||
if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
|
||||
return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
|
||||
}
|
||||
},
|
||||
Bounce: {
|
||||
easeIn: function(t,b,c,d){
|
||||
return c - TweenFunc.Bounce.easeOut(d-t, 0, c, d) + b;
|
||||
},
|
||||
easeOut: function(t,b,c,d){
|
||||
if ((t/=d) < (1/2.75)) {
|
||||
return c*(7.5625*t*t) + b;
|
||||
} else if (t < (2/2.75)) {
|
||||
return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
|
||||
} else if (t < (2.5/2.75)) {
|
||||
return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
|
||||
} else {
|
||||
return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
|
||||
}
|
||||
},
|
||||
easeInOut: function(t,b,c,d){
|
||||
if (t < d/2) return TweenFunc.Bounce.easeIn(t*2, 0, c, d) * .5 + b;
|
||||
else return TweenFunc.Bounce.easeOut(t*2-d, 0, c, d) * .5 + c*.5 + b;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,318 +1,509 @@
|
||||
import {TimerMgr} from "../timer/timer_mgr"
|
||||
import {handler, gen_handler} from "../util"
|
||||
import {LinkList} from "../linklist"
|
||||
import {TweenFunc} from "./tweenfunc"
|
||||
|
||||
export class TweenUtil
|
||||
{
|
||||
private static inst:TweenUtil;
|
||||
private iterList:LinkList<TweenHandler>;
|
||||
private pendingList:LinkList<TweenHandler>;
|
||||
private pool:TweenHandler[];
|
||||
private key:number;
|
||||
private timer:number;
|
||||
|
||||
private constructor()
|
||||
{
|
||||
this.key = 0;
|
||||
this.pool = [];
|
||||
this.iterList = new LinkList<TweenHandler>();
|
||||
this.pendingList = new LinkList<TweenHandler>();
|
||||
}
|
||||
|
||||
public static getInst()
|
||||
{
|
||||
if(!this.inst)
|
||||
{
|
||||
this.inst = new TweenUtil();
|
||||
}
|
||||
return this.inst;
|
||||
}
|
||||
|
||||
to(params:TweenParams):number
|
||||
{
|
||||
const node = params.node;
|
||||
if(!node || !cc.isValid(node))
|
||||
{
|
||||
cc.warn("invalid node");
|
||||
return 0;
|
||||
}
|
||||
|
||||
let th = this.pool.pop();
|
||||
if(!th)
|
||||
{
|
||||
th = {
|
||||
node:null, elapsed:null, duration:null, delay:null,
|
||||
exectors:null, tweenFunc:null, onUpdate:null, onComplete:null
|
||||
};
|
||||
}
|
||||
th.node = node;
|
||||
th.elapsed = 0;
|
||||
th.duration = params.duration || 1;
|
||||
th.delay = params.delay || 0;
|
||||
th.exectors = [];
|
||||
th.tweenFunc = params.tweenFunc || TweenFunc.Linear;
|
||||
th.onUpdate = params.onUpdate;
|
||||
th.onComplete = params.onComplete;
|
||||
|
||||
if(params.x != null)
|
||||
{
|
||||
const from = node.x;
|
||||
const delta = params.x - from;
|
||||
th.exectors.push((elapsed) => {
|
||||
const curr_x = th.tweenFunc(elapsed, from, delta, th.duration);
|
||||
th.node.x = curr_x; //测试发现用node.position.x,不能移动位置
|
||||
});
|
||||
}
|
||||
if(params.y != null)
|
||||
{
|
||||
const from = node.y;
|
||||
const delta = params.y - from;
|
||||
th.exectors.push((elapsed) => {
|
||||
const curr_y = th.tweenFunc(elapsed, from, delta, th.duration);
|
||||
th.node.y = curr_y;
|
||||
});
|
||||
}
|
||||
if(params.rotation != null)
|
||||
{
|
||||
const from = node.rotation;
|
||||
const delta = params.rotation - from;
|
||||
th.exectors.push((elapsed) => {
|
||||
const curr_rot = th.tweenFunc(elapsed, from, delta, th.duration);
|
||||
th.node.rotation = curr_rot;
|
||||
});
|
||||
}
|
||||
if(params.width != null)
|
||||
{
|
||||
const from = node.width;
|
||||
const delta = params.width - from;
|
||||
th.exectors.push((elapsed) => {
|
||||
const curr_width = th.tweenFunc(elapsed, from, delta, th.duration);
|
||||
th.node.width = curr_width;
|
||||
});
|
||||
}
|
||||
if(params.height != null)
|
||||
{
|
||||
const from = node.height;
|
||||
const delta = params.height - from;
|
||||
th.exectors.push((elapsed) => {
|
||||
const curr_height = th.tweenFunc(elapsed, from, delta, th.duration);
|
||||
th.node.height = curr_height;
|
||||
});
|
||||
}
|
||||
if(params.opacity != null)
|
||||
{
|
||||
const from = node.opacity;
|
||||
const delta = params.opacity - from;
|
||||
th.exectors.push((elapsed) => {
|
||||
const curr_opacity = th.tweenFunc(elapsed, from, delta, th.duration);
|
||||
th.node.opacity = curr_opacity;
|
||||
});
|
||||
}
|
||||
|
||||
if(!this.timer)
|
||||
{
|
||||
this.timer = TimerMgr.getInst().add_updater(gen_handler(this.update, this));
|
||||
}
|
||||
return this.pendingList.append(++this.key, th);
|
||||
}
|
||||
|
||||
from(params:TweenParams):number
|
||||
{
|
||||
const node = params.node;
|
||||
if(!node || !cc.isValid(node))
|
||||
{
|
||||
cc.warn("invalid node");
|
||||
return 0;
|
||||
}
|
||||
if(params.x != null)
|
||||
{
|
||||
[node.x, params.x] = [params.x, node.x];
|
||||
}
|
||||
if(params.y != null)
|
||||
{
|
||||
[node.y, params.y] = [params.y, node.y];
|
||||
}
|
||||
if(params.rotation != null)
|
||||
{
|
||||
[node.rotation, params.rotation] = [params.rotation, node.rotation];
|
||||
}
|
||||
if(params.width != null)
|
||||
{
|
||||
[node.width, params.width] = [params.width, node.width];
|
||||
}
|
||||
if(params.height != null)
|
||||
{
|
||||
[node.height, params.height] = [params.height, node.height];
|
||||
}
|
||||
if(params.opacity != null)
|
||||
{
|
||||
[node.opacity, params.opacity] = [params.opacity, node.opacity];
|
||||
}
|
||||
return this.to(params);
|
||||
}
|
||||
|
||||
kill(key:number)
|
||||
{
|
||||
if(!this.killIter(key))
|
||||
{
|
||||
this.killPending(key);
|
||||
}
|
||||
}
|
||||
|
||||
private killIter(key:number)
|
||||
{
|
||||
const node = this.iterList.remove(key);
|
||||
if(node)
|
||||
{
|
||||
this.pool.push(node.data);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private killPending(key:number)
|
||||
{
|
||||
const node = this.pendingList.remove(key);
|
||||
if(node)
|
||||
{
|
||||
this.pool.push(node.data);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static from(params:TweenParams):number
|
||||
{
|
||||
return this.getInst().from(params);
|
||||
}
|
||||
|
||||
static to(params:TweenParams):number
|
||||
{
|
||||
return this.getInst().to(params);
|
||||
}
|
||||
|
||||
static kill(key:number)
|
||||
{
|
||||
this.getInst().kill(key);
|
||||
}
|
||||
|
||||
private update(dt:number)
|
||||
{
|
||||
//什么都没有,停止定时器
|
||||
if(!this.iterList.head && !this.pendingList.head)
|
||||
{
|
||||
TimerMgr.getInst().remove(this.timer);
|
||||
this.timer = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
//执行当前帧的th
|
||||
let node = this.iterList.head;
|
||||
while(node)
|
||||
{
|
||||
//节点已失效
|
||||
const displayNode = node.data.node;
|
||||
if(!displayNode || !cc.isValid(displayNode))
|
||||
{
|
||||
//先保存next引用,防止回调函数里回收node导致next被修改
|
||||
const next = node.next;
|
||||
this.killIter(node.key);
|
||||
node = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
const elapsed = node.data.elapsed;
|
||||
const delay = node.data.delay;
|
||||
const duration = node.data.duration;
|
||||
|
||||
//执行完毕
|
||||
if(elapsed >= duration + delay)
|
||||
{
|
||||
const next = node.next;
|
||||
const key = node.key;
|
||||
node.data.exectors.forEach(func => {
|
||||
func(duration);
|
||||
});
|
||||
if(node.data.onComplete)
|
||||
{
|
||||
node.data.onComplete.exec(displayNode, 1);
|
||||
}
|
||||
this.killIter(key);
|
||||
node = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
//延时时间到了
|
||||
if(elapsed >= delay)
|
||||
{
|
||||
//onUpdate回调可能会调用kill回收tweenHandler.避免操作已回收的对象。
|
||||
const next = node.next;
|
||||
node.data.elapsed += dt;
|
||||
node.data.exectors.forEach(func => {
|
||||
func(elapsed - delay);
|
||||
});
|
||||
if(node.data.onUpdate)
|
||||
{
|
||||
node.data.onUpdate.exec(displayNode, this.clamp01((elapsed - delay) / duration));
|
||||
}
|
||||
node = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
node.data.elapsed += dt;
|
||||
node = node.next;
|
||||
}
|
||||
}
|
||||
|
||||
//添加下一帧的th
|
||||
node = this.pendingList.head;
|
||||
while(node)
|
||||
{
|
||||
const key = node.key;
|
||||
const th = node.data;
|
||||
node = node.next;
|
||||
this.pendingList.remove(key);
|
||||
this.iterList.append(key, th);
|
||||
}
|
||||
}
|
||||
|
||||
private clamp01(value:number)
|
||||
{
|
||||
if(value < 0)
|
||||
{
|
||||
value = 0;
|
||||
}
|
||||
if(value > 1)
|
||||
{
|
||||
value = 1;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
type TweenHandler = {
|
||||
node:cc.Node;
|
||||
elapsed:number;
|
||||
delay:number;
|
||||
duration:number;
|
||||
tweenFunc:Function;
|
||||
exectors:((elapsed:number) => void)[];
|
||||
onUpdate?:handler;
|
||||
onComplete?:handler;
|
||||
}
|
||||
|
||||
type TweenParams = {
|
||||
node:cc.Node;
|
||||
duration:number; //动画持续时间,单位秒
|
||||
delay?:number; //延时多久执行
|
||||
x?:number;
|
||||
y?:number;
|
||||
rotation?:number;
|
||||
width?:number;
|
||||
height?:number;
|
||||
opacity?:number;
|
||||
tweenFunc?:Function;
|
||||
onUpdate?:handler;
|
||||
onComplete?:handler;
|
||||
}
|
||||
import {TimerMgr} from "../timer/timer_mgr"
|
||||
import {gen_handler} from "../util"
|
||||
import {LinkList} from "../linklist"
|
||||
import {TweenFunc} from "./tweenfunc"
|
||||
|
||||
const clamp01 = (value:number) => {
|
||||
if(value < 0) {
|
||||
value = 0;
|
||||
}
|
||||
if(value > 1) {
|
||||
value = 1;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
export class TweenUtil
|
||||
{
|
||||
private static inst:TweenUtil;
|
||||
private iterList:LinkList<TweenTarget>;
|
||||
private pendingList:LinkList<TweenTarget>;
|
||||
private pool:TweenTarget[];
|
||||
private key:number;
|
||||
private timer:number;
|
||||
|
||||
private constructor()
|
||||
{
|
||||
this.key = 0;
|
||||
this.pool = [];
|
||||
this.iterList = new LinkList<TweenTarget>();
|
||||
this.pendingList = new LinkList<TweenTarget>();
|
||||
}
|
||||
|
||||
public static getInst()
|
||||
{
|
||||
if(!this.inst) {
|
||||
this.inst = new TweenUtil();
|
||||
}
|
||||
return this.inst;
|
||||
}
|
||||
|
||||
from(params:TweenParams):number
|
||||
{
|
||||
const target = params.target;
|
||||
if(!target || !cc.isValid(target)) {
|
||||
cc.warn("TweenUtil, invalid tween target");
|
||||
return 0;
|
||||
}
|
||||
|
||||
let tweenTarget = this.pool.pop();
|
||||
if(!tweenTarget) {
|
||||
tweenTarget = new TweenTarget();
|
||||
}
|
||||
tweenTarget.init(target, 0, params.delay || 0, params.duration || 1, params.tweenFunc || TweenFunc.Linear, params.onUpdate, params.onComplete);
|
||||
|
||||
if(params.x != null) {
|
||||
const prop = TweenProperty.x;
|
||||
const value = tweenTarget.getValue(prop);
|
||||
tweenTarget.setValue(prop, params.x);
|
||||
params.x = value;
|
||||
}
|
||||
if(params.y != null) {
|
||||
const prop = TweenProperty.y;
|
||||
const value = tweenTarget.getValue(prop);
|
||||
tweenTarget.setValue(prop, params.y);
|
||||
params.y = value;
|
||||
}
|
||||
if(params.scaleX != null) {
|
||||
const prop = TweenProperty.scaleX;
|
||||
const value = tweenTarget.getValue(prop);
|
||||
tweenTarget.setValue(prop, params.scaleX);
|
||||
params.scaleX = value;
|
||||
}
|
||||
if(params.scaleY != null) {
|
||||
const prop = TweenProperty.scaleY;
|
||||
const value = tweenTarget.getValue(prop);
|
||||
tweenTarget.setValue(prop, params.scaleY);
|
||||
params.scaleY = value;
|
||||
}
|
||||
if(params.scale != null) {
|
||||
const prop = TweenProperty.scale;
|
||||
const value = tweenTarget.getValue(prop);
|
||||
tweenTarget.setValue(prop, params.scale);
|
||||
params.scale = value;
|
||||
}
|
||||
if(params.rotation != null) {
|
||||
const prop = TweenProperty.rotation;
|
||||
const value = tweenTarget.getValue(prop);
|
||||
tweenTarget.setValue(prop, params.rotation);
|
||||
params.rotation = value;
|
||||
}
|
||||
if(params.width != null) {
|
||||
const prop = TweenProperty.width;
|
||||
const value = tweenTarget.getValue(prop);
|
||||
tweenTarget.setValue(prop, params.width);
|
||||
params.width = value;
|
||||
}
|
||||
if(params.height != null) {
|
||||
const prop = TweenProperty.height;
|
||||
const value = tweenTarget.getValue(prop);
|
||||
tweenTarget.setValue(prop, params.height);
|
||||
params.height = value;
|
||||
}
|
||||
if(params.opacity != null) {
|
||||
const prop = TweenProperty.opacity;
|
||||
const value = tweenTarget.getValue(prop);
|
||||
tweenTarget.setValue(prop, params.opacity);
|
||||
params.opacity = value;
|
||||
}
|
||||
if(params.startValue != null) {
|
||||
const prop = TweenProperty.string;
|
||||
params.stopValue = params.stopValue || tweenTarget.getValue(prop);
|
||||
tweenTarget.setValue(prop, params.startValue);
|
||||
}
|
||||
return this.to(params, tweenTarget);
|
||||
}
|
||||
|
||||
to(params:TweenParams, tweenTarget:TweenTarget = null):number
|
||||
{
|
||||
const target = params.target;
|
||||
if(!target || !cc.isValid(target)) {
|
||||
cc.warn("TweenUtil, invalid tween target");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!tweenTarget) {
|
||||
tweenTarget = this.pool.pop();
|
||||
if(!tweenTarget) {
|
||||
tweenTarget = new TweenTarget();
|
||||
}
|
||||
tweenTarget.init(target, 0, params.delay || 0, params.duration || 1, params.tweenFunc || TweenFunc.Linear, params.onUpdate, params.onComplete);
|
||||
}
|
||||
|
||||
const funcs:((elapsed:number) => void)[] = [];
|
||||
if(params.x != null) {
|
||||
const prop = TweenProperty.x;
|
||||
const from = tweenTarget.getValue(prop);
|
||||
const delta = params.x - from;
|
||||
funcs.push(elapsed => {
|
||||
const value = tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration);
|
||||
tweenTarget.setValue(prop, value);
|
||||
});
|
||||
}
|
||||
if(params.y != null) {
|
||||
const prop = TweenProperty.y;
|
||||
const from = tweenTarget.getValue(prop);
|
||||
const delta = params.y - from;
|
||||
funcs.push(elapsed => {
|
||||
const value = tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration);
|
||||
tweenTarget.setValue(prop, value);
|
||||
});
|
||||
}
|
||||
if(params.scaleX != null) {
|
||||
const prop = TweenProperty.scaleX;
|
||||
const from = tweenTarget.getValue(prop);
|
||||
const delta = params.scaleX - from;
|
||||
funcs.push(elapsed => {
|
||||
const value = tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration);
|
||||
tweenTarget.setValue(prop, value);
|
||||
});
|
||||
}
|
||||
if(params.scaleY != null) {
|
||||
const prop = TweenProperty.scaleY;
|
||||
const from = tweenTarget.getValue(prop);
|
||||
const delta = params.scaleY - from;
|
||||
funcs.push(elapsed => {
|
||||
const value = tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration);
|
||||
tweenTarget.setValue(prop, value);
|
||||
});
|
||||
}
|
||||
if(params.scale != null) {
|
||||
const prop = TweenProperty.scale;
|
||||
const from = tweenTarget.getValue(prop);
|
||||
const delta = params.scale - from;
|
||||
funcs.push(elapsed => {
|
||||
const value = tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration);
|
||||
tweenTarget.setValue(prop, value);
|
||||
});
|
||||
}
|
||||
if(params.rotation != null) {
|
||||
const prop = TweenProperty.rotation;
|
||||
const from = tweenTarget.getValue(prop);
|
||||
const delta = params.rotation - from;
|
||||
funcs.push(elapsed => {
|
||||
const value = tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration);
|
||||
tweenTarget.setValue(prop, value);
|
||||
});
|
||||
}
|
||||
if(params.width != null) {
|
||||
const prop = TweenProperty.width;
|
||||
const from = tweenTarget.getValue(prop);
|
||||
const delta = params.width - from;
|
||||
funcs.push(elapsed => {
|
||||
const value = tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration);
|
||||
tweenTarget.setValue(prop, value);
|
||||
});
|
||||
}
|
||||
if(params.height != null) {
|
||||
const prop = TweenProperty.height;
|
||||
const from = tweenTarget.getValue(prop);
|
||||
const delta = params.height - from;
|
||||
funcs.push(elapsed => {
|
||||
const value = tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration);
|
||||
tweenTarget.setValue(prop, value);
|
||||
});
|
||||
}
|
||||
if(params.opacity != null) {
|
||||
const prop = TweenProperty.opacity;
|
||||
const from = tweenTarget.getValue(prop);
|
||||
const delta = params.opacity - from;
|
||||
funcs.push(elapsed => {
|
||||
const value = tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration);
|
||||
tweenTarget.setValue(prop, value);
|
||||
});
|
||||
}
|
||||
if(params.stopValue != null) {
|
||||
const prop = TweenProperty.string;
|
||||
const from = params.startValue || tweenTarget.getValue(prop);
|
||||
const delta = params.stopValue - from;
|
||||
funcs.push(elapsed => {
|
||||
const value = parseInt(tweenTarget.tweenFunc(elapsed, from, delta, tweenTarget.duration));
|
||||
if(params.onSetValue && tweenTarget.isValid()) {
|
||||
params.onSetValue(value);
|
||||
}
|
||||
else {
|
||||
tweenTarget.setValue(prop, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const onUpdate = tweenTarget.onUpdate;
|
||||
const onComplete = tweenTarget.onComplete;
|
||||
tweenTarget.onUpdate = (ratio:number, elapsed:number) => {
|
||||
if(!tweenTarget.isValid()) {
|
||||
return;
|
||||
}
|
||||
funcs.forEach(func => func(elapsed));
|
||||
if(onUpdate) {
|
||||
onUpdate(ratio, elapsed);
|
||||
}
|
||||
};
|
||||
tweenTarget.onComplete = (ratio:number, elapsed:number) => {
|
||||
if(!tweenTarget.isValid()) {
|
||||
return;
|
||||
}
|
||||
funcs.forEach(func => func(elapsed));
|
||||
if(onComplete) {
|
||||
onComplete(ratio, elapsed);
|
||||
}
|
||||
};
|
||||
this.addTween(tweenTarget);
|
||||
}
|
||||
|
||||
private ratio(duration:number, delay?:number, onUpdate?:TweenOnUpdate, onComplete?:TweenOnComplete, tweenFunc?:Function)
|
||||
{
|
||||
let tweenTarget = this.pool.pop();
|
||||
if(!tweenTarget) {
|
||||
tweenTarget = new TweenTarget();
|
||||
}
|
||||
tweenTarget.init(null, 0, delay || 0, duration || 1, tweenFunc || TweenFunc.Linear, onUpdate, onComplete);
|
||||
this.addTween(tweenTarget);
|
||||
}
|
||||
|
||||
private addTween(tweenTarget:TweenTarget)
|
||||
{
|
||||
if(!this.timer) {
|
||||
this.timer = TimerMgr.getInst().add_updater(gen_handler(this.update, this), "TweenUtil addTween");
|
||||
}
|
||||
return this.pendingList.append(++this.key, tweenTarget);
|
||||
}
|
||||
|
||||
private kill(key:number)
|
||||
{
|
||||
if(!this.killIter(key)) {
|
||||
this.killPending(key);
|
||||
}
|
||||
}
|
||||
|
||||
private killIter(key:number)
|
||||
{
|
||||
const node = this.iterList.remove(key);
|
||||
if(node) {
|
||||
this.pool.push(node.data);
|
||||
node.data = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private killPending(key:number)
|
||||
{
|
||||
const node = this.pendingList.remove(key);
|
||||
if(node) {
|
||||
this.pool.push(node.data);
|
||||
node.data = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static from(params:TweenParams):number
|
||||
{
|
||||
return this.getInst().from(params);
|
||||
}
|
||||
|
||||
static to(params:TweenParams):number
|
||||
{
|
||||
return this.getInst().to(params);
|
||||
}
|
||||
|
||||
static ratio(duration:number, delay?:number, onUpdate?:TweenOnUpdate, onComplete?:TweenOnComplete, tweenFunc?:Function)
|
||||
{
|
||||
return this.getInst().ratio(duration, delay, onUpdate, onComplete, tweenFunc);
|
||||
}
|
||||
|
||||
static kill(key:number)
|
||||
{
|
||||
this.getInst().kill(key);
|
||||
}
|
||||
|
||||
private update(dt:number)
|
||||
{
|
||||
//什么都没有,停止定时器
|
||||
if(!this.iterList.head && !this.pendingList.head) {
|
||||
TimerMgr.getInst().remove(this.timer);
|
||||
this.timer = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
//执行当前帧的th
|
||||
let node = this.iterList.head;
|
||||
while(node) {
|
||||
const tweenTarget = node.data;
|
||||
|
||||
//目标已失效
|
||||
if(!tweenTarget || !tweenTarget.isValid()) {
|
||||
//先保存next引用,防止回调函数里回收node导致next被修改
|
||||
const next = node.next;
|
||||
this.killIter(node.key);
|
||||
node = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
const elapsed = tweenTarget.elapsed;
|
||||
const delay = tweenTarget.delay;
|
||||
const duration = tweenTarget.duration;
|
||||
|
||||
//执行完毕
|
||||
if(elapsed >= duration + delay) {
|
||||
const next = node.next;
|
||||
const key = node.key;
|
||||
if(tweenTarget.onUpdate) {
|
||||
tweenTarget.onUpdate(1, duration);
|
||||
}
|
||||
if(tweenTarget.onComplete) {
|
||||
tweenTarget.onComplete(1, duration);
|
||||
}
|
||||
this.killIter(key);
|
||||
node = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
//延时时间到了
|
||||
if(elapsed >= delay) {
|
||||
//onUpdate回调可能会调用kill回收tweenHandler.避免操作已回收的对象。
|
||||
const next = node.next;
|
||||
tweenTarget.elapsed += dt;
|
||||
if(tweenTarget.onUpdate) {
|
||||
tweenTarget.onUpdate(clamp01((elapsed - delay) / duration), elapsed - delay);
|
||||
}
|
||||
node = next;
|
||||
}
|
||||
else {
|
||||
tweenTarget.elapsed += dt;
|
||||
node = node.next;
|
||||
}
|
||||
}
|
||||
|
||||
//添加下一帧的th
|
||||
node = this.pendingList.head;
|
||||
while(node) {
|
||||
const key = node.key;
|
||||
const tt = node.data;
|
||||
node = node.next;
|
||||
this.pendingList.remove(key);
|
||||
this.iterList.append(key, tt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface TweenParams
|
||||
{
|
||||
target:cc.Node|cc.Label;
|
||||
duration:number; //动画持续时间,单位秒
|
||||
delay?:number; //延时多久执行
|
||||
x?:number;
|
||||
y?:number;
|
||||
scaleX?:number;
|
||||
scaleY?:number;
|
||||
scale?:number;
|
||||
rotation?:number;
|
||||
width?:number;
|
||||
height?:number;
|
||||
opacity?:number;
|
||||
startValue?:number;
|
||||
stopValue?:number;
|
||||
tweenFunc?:Function;
|
||||
onUpdate?:TweenOnUpdate;
|
||||
onComplete?:TweenOnComplete;
|
||||
onSetValue?:TweenOnSetValue;
|
||||
}
|
||||
|
||||
enum TweenProperty
|
||||
{
|
||||
x = "x",
|
||||
y = "y",
|
||||
scaleX = "scaleX",
|
||||
scaleY = "scaleY",
|
||||
scale = "scale",
|
||||
rotation = "rotation",
|
||||
width = "width",
|
||||
height = "height",
|
||||
opacity = "opacity",
|
||||
string = "string",
|
||||
}
|
||||
|
||||
type TweenOnUpdate = (ratio:number, elapsed:number) => void;
|
||||
type TweenOnComplete = (ratio:number, elapsed:number) => void;
|
||||
type TweenOnSetValue = (value:number) => void;
|
||||
|
||||
class TweenTarget
|
||||
{
|
||||
public elapsed:number;
|
||||
public delay:number;
|
||||
public duration:number;
|
||||
public tweenFunc:Function;
|
||||
public onUpdate:TweenOnUpdate;
|
||||
public onComplete:TweenOnComplete;
|
||||
private _hasTarget:boolean;
|
||||
private _node:cc.Node;
|
||||
private _label:cc.Label;
|
||||
|
||||
init(target:cc.Node|cc.Label, elapsed:number, delay:number, duration:number, tweenFunc:Function, onUpdate:TweenOnUpdate, onComplete:TweenOnComplete)
|
||||
{
|
||||
this.elapsed = elapsed;
|
||||
this.delay = delay;
|
||||
this.duration = duration;
|
||||
this.tweenFunc = tweenFunc;
|
||||
this.onUpdate = onUpdate;
|
||||
this.onComplete = onComplete;
|
||||
this._hasTarget = target != null;
|
||||
if(target) {
|
||||
if((<cc.Node>target).getPosition) {
|
||||
this._node = target as cc.Node;
|
||||
this._label = null;
|
||||
}
|
||||
else if((<cc.Label>target).string) {
|
||||
this._label = target as cc.Label;
|
||||
this._node = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isValid()
|
||||
{
|
||||
if(!this._hasTarget) {
|
||||
return true;
|
||||
}
|
||||
if(this._node) {
|
||||
return cc.isValid(this._node);
|
||||
}
|
||||
else if(this._label && this._label.node) {
|
||||
return cc.isValid(this._label.node);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
setValue(key:TweenProperty, value:number)
|
||||
{
|
||||
if(!this._hasTarget) {
|
||||
return;
|
||||
}
|
||||
if(!this.isValid()) {
|
||||
return;
|
||||
}
|
||||
if(this._node) {
|
||||
this._node[key] = value;
|
||||
}
|
||||
else if(this._label && this._label.node) {
|
||||
this._label[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
getValue(key:TweenProperty):number
|
||||
{
|
||||
if(!this._hasTarget) {
|
||||
return 0;
|
||||
}
|
||||
if(!this.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
if(this._node) {
|
||||
return parseFloat(this._node[key]);
|
||||
}
|
||||
else if(this._label && this._label.node) {
|
||||
return parseFloat(this._label[key]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
50
ui/base_scene.ts
Normal file
50
ui/base_scene.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { BaseUIComponent } from "./base_ui_component";
|
||||
|
||||
const {ccclass, property} = cc._decorator;
|
||||
|
||||
@ccclass
|
||||
export class BaseScene extends BaseUIComponent {
|
||||
private _name:string;
|
||||
|
||||
setName(value:string)
|
||||
{
|
||||
this._name = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 只能由scene_mgr调用
|
||||
*/
|
||||
__onStarted__(...params)
|
||||
{
|
||||
cc.log(`scene<${this._name}> __onStarted__`, ...params);
|
||||
this.onStarted(...params);
|
||||
this.enabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 只能由scene_mgr调用
|
||||
*/
|
||||
__beforeDestroy__()
|
||||
{
|
||||
cc.log(`scene<${this._name}> __beforeDestroy__`);
|
||||
this.enabled = false;
|
||||
this.clearEventListeners();
|
||||
this.beforeDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景start方法后调用
|
||||
*/
|
||||
onStarted(...params)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景onDestroy方法前调用
|
||||
*/
|
||||
beforeDestroy()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
43
ui/base_ui_component.ts
Normal file
43
ui/base_ui_component.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { Event_Name, MyEvnetHandler, event_mgr } from "../event/event_mgr";
|
||||
|
||||
export class BaseUIComponent extends cc.Component
|
||||
{
|
||||
private _eventListeners:{event:Event_Name, handler:MyEvnetHandler}[];
|
||||
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
protected addEventListener(event:Event_Name, handler:MyEvnetHandler)
|
||||
{
|
||||
event_mgr.get_inst().add(event, handler, this);
|
||||
if(!this._eventListeners) {
|
||||
this._eventListeners = [];
|
||||
}
|
||||
this._eventListeners.push({event, handler});
|
||||
}
|
||||
|
||||
protected removeEventListener(event:Event_Name, handler:MyEvnetHandler)
|
||||
{
|
||||
event_mgr.get_inst().remove(event, handler, this);
|
||||
}
|
||||
|
||||
protected fireEvent(event:Event_Name, ...params)
|
||||
{
|
||||
event_mgr.get_inst().fire(event, ...params);
|
||||
}
|
||||
|
||||
protected clearEventListeners()
|
||||
{
|
||||
const eventListeners = this._eventListeners;
|
||||
if(eventListeners && eventListeners.length) {
|
||||
const eventMgr = event_mgr.get_inst();
|
||||
eventListeners.forEach(listener => {
|
||||
eventMgr.remove(listener.event, listener.handler, this);
|
||||
});
|
||||
}
|
||||
this._eventListeners = null;
|
||||
cc.game.targetOff(this);
|
||||
}
|
||||
}
|
||||
662
ui/pop_mgr.ts
662
ui/pop_mgr.ts
@@ -1,191 +1,473 @@
|
||||
import {pool_mgr} from "../pool/pool_mgr"
|
||||
import {handler, gen_handler} from "../util"
|
||||
import {POP_UI_BASE} from "./pop_ui_base"
|
||||
import { TimerMgr } from "../timer/timer_mgr"
|
||||
import * as utils from '../util'
|
||||
import { TweenUtil } from "../tween/tweenutil"
|
||||
import { TweenFunc } from "../tween/tweenfunc"
|
||||
|
||||
export class pop_mgr
|
||||
{
|
||||
private static inst:pop_mgr;
|
||||
private ui_cache:any; //path => pop_ui
|
||||
private ui_stack:Array<string>; //ui stacks
|
||||
private ui_show_handler:handler;
|
||||
private ui_hide_handler:handler;
|
||||
|
||||
private constructor()
|
||||
{
|
||||
this.ui_cache = {};
|
||||
this.ui_stack = new Array<string>();
|
||||
}
|
||||
|
||||
static get_inst():pop_mgr
|
||||
{
|
||||
if(!this.inst)
|
||||
{
|
||||
this.inst = new pop_mgr();
|
||||
}
|
||||
return this.inst;
|
||||
}
|
||||
|
||||
private get_ui(path:string):pop_ui
|
||||
{
|
||||
let ui:pop_ui = this.ui_cache[path];
|
||||
if(!ui)
|
||||
{
|
||||
this.ui_cache[path] = ui = {node:null, is_show:false};
|
||||
}
|
||||
return ui;
|
||||
}
|
||||
|
||||
clear()
|
||||
{
|
||||
for(let path in this.ui_cache)
|
||||
{
|
||||
this.hide(path);
|
||||
}
|
||||
this.ui_cache = {};
|
||||
this.ui_stack.length = 0;
|
||||
}
|
||||
|
||||
peek()
|
||||
{
|
||||
return this.ui_stack[this.ui_stack.length - 1];
|
||||
}
|
||||
|
||||
set_handlers(on_ui_show:handler, on_ui_hide:handler)
|
||||
{
|
||||
this.ui_show_handler = on_ui_show;
|
||||
this.ui_hide_handler = on_ui_hide;
|
||||
}
|
||||
|
||||
is_show(path:string):boolean
|
||||
{
|
||||
let ui:pop_ui = this.ui_cache[path];
|
||||
return ui != null;
|
||||
}
|
||||
|
||||
show(path:string, transition?:UI_TRANSITION, ...params:any[]):void
|
||||
{
|
||||
let ui:pop_ui = this.get_ui(path);
|
||||
if(ui.is_show)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ui.is_show = true;
|
||||
pool_mgr.get_inst().get_ui(path, gen_handler((node:cc.Node):void=>{
|
||||
if(!ui.is_show)
|
||||
{
|
||||
pool_mgr.get_inst().put_ui(path, node);
|
||||
return;
|
||||
}
|
||||
ui.node = node;
|
||||
//应用过渡效果
|
||||
this.applyTransitionEffect(node, transition);
|
||||
cc.director.getScene().addChild(node);
|
||||
TimerMgr.getInst().once(0, utils.gen_handler(() => {
|
||||
//在加到场景同一帧调用界面show方法,计算位置会不准确,故统一在下一帧调用show
|
||||
if(!ui.is_show)
|
||||
{
|
||||
return;
|
||||
}
|
||||
let ui_base:POP_UI_BASE = node.getComponent(POP_UI_BASE);
|
||||
ui_base.ui_name = path;
|
||||
ui_base.__show__(...params);
|
||||
//进栈
|
||||
this.ui_stack.push(path);
|
||||
//钩子函数调用
|
||||
if(this.ui_show_handler)
|
||||
{
|
||||
this.ui_show_handler.exec();
|
||||
}
|
||||
}));
|
||||
}, this));
|
||||
}
|
||||
|
||||
//关闭界面时不destroy,只是从父节点移除并缓存
|
||||
hide(path:string):void
|
||||
{
|
||||
let ui:pop_ui = this.ui_cache[path];
|
||||
if(!ui)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.ui_cache[path] = null;
|
||||
ui.is_show = false;
|
||||
if(ui.node)
|
||||
{
|
||||
pool_mgr.get_inst().put_ui(path, ui.node);
|
||||
//调用hide
|
||||
let ui_base:POP_UI_BASE = ui.node.getComponent(POP_UI_BASE);
|
||||
ui_base.__hide__();
|
||||
//出栈
|
||||
const lastIdx = this.ui_stack.lastIndexOf(path);
|
||||
if(lastIdx != -1)
|
||||
{
|
||||
this.ui_stack.splice(lastIdx, 1);
|
||||
}
|
||||
//钩子函数调用
|
||||
if(this.ui_hide_handler)
|
||||
{
|
||||
this.ui_hide_handler.exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
applyTransitionEffect(node:cc.Node, transition:UI_TRANSITION)
|
||||
{
|
||||
if(transition && transition.transType == UI_TRANSITION_TYPE.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
transition = transition || {
|
||||
transType:UI_TRANSITION_TYPE.FadeIn,
|
||||
duration:0.5,
|
||||
tweenFunc:TweenFunc.Linear
|
||||
};
|
||||
switch(transition.transType)
|
||||
{
|
||||
case UI_TRANSITION_TYPE.FadeIn:
|
||||
TweenUtil.from({node, duration:transition.duration || 1, opacity:0, tweenFunc:transition.tweenFunc || TweenFunc.Linear});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type pop_ui = {
|
||||
node:cc.Node;
|
||||
is_show:boolean;
|
||||
}
|
||||
|
||||
//界面prefab路径配置, 相对于assets/resources目录
|
||||
export const UI_CONFIG = {
|
||||
overlay_bg:"panels/panel_overlay_bg",
|
||||
level:"panels/panel_level",
|
||||
level_detail:"panels/panel_leveldetail",
|
||||
game:"panels/panel_game",
|
||||
level_result:"panels/panel_levelresult",
|
||||
level_reward:"panels/panel_levelreward",
|
||||
rank:"panels/panel_rank",
|
||||
newbee_gift:"panels/panel_newbeegift",
|
||||
sos_gift:"panels/panel_sosgift",
|
||||
login_gift:"panels/panel_logingift",
|
||||
}
|
||||
|
||||
interface UI_TRANSITION
|
||||
{
|
||||
transType:UI_TRANSITION_TYPE;
|
||||
tweenFunc?:Function;
|
||||
duration?:number;
|
||||
}
|
||||
|
||||
export const enum UI_TRANSITION_TYPE
|
||||
{
|
||||
None = 1,
|
||||
FadeIn,
|
||||
DropDown,
|
||||
PopUp,
|
||||
LeftIn,
|
||||
RightIn,
|
||||
import { pool_mgr } from "../pool/pool_mgr"
|
||||
import { gen_handler } from "../util"
|
||||
import { POP_UI_BASE } from "./pop_ui_base"
|
||||
import { TweenUtil } from "../tween/tweenutil"
|
||||
import { TweenFunc } from "../tween/tweenfunc"
|
||||
import { event_mgr, Event_Name } from "../event/event_mgr";
|
||||
import { LoadingQueue } from "../loader/loading_queue";
|
||||
|
||||
const panel_overlay_bg = "panel_overlay_bg";
|
||||
const isDualInstanceView = (path:string) => {
|
||||
const config:UI_CONFIG = UI_CONFIG_MAP[path];
|
||||
return config && config.dualInstance;
|
||||
}
|
||||
const isModalView = (path:string) => {
|
||||
const config:UI_CONFIG = UI_CONFIG_MAP[path];
|
||||
return !config || config.needOverlayBg != false;
|
||||
}
|
||||
const isFullScreenView = (path:string) => {
|
||||
const config:UI_CONFIG = UI_CONFIG_MAP[path];
|
||||
return config && config.fullScreen;
|
||||
}
|
||||
const isDestroyAtOnce = (path:string) => {
|
||||
const config:UI_CONFIG = UI_CONFIG_MAP[path];
|
||||
return config && config.destroyAtOnce;
|
||||
}
|
||||
const getOverlayBgOpacity = (path:string) => {
|
||||
const config:UI_CONFIG = UI_CONFIG_MAP[path];
|
||||
if(!config || config.overlayBgOpacity == null) {
|
||||
return 175;
|
||||
}
|
||||
return config.overlayBgOpacity;
|
||||
}
|
||||
|
||||
export class pop_mgr
|
||||
{
|
||||
private static inst:pop_mgr;
|
||||
private view_map:Map<string, PopView[]>;
|
||||
private view_stack:PopView[];
|
||||
private overlayNode:cc.Node;
|
||||
|
||||
private constructor()
|
||||
{
|
||||
this.view_map = new Map();
|
||||
this.view_stack = [];
|
||||
event_mgr.get_inst().add(Event_Name.UI_SHOW, this.onViewShow, this);
|
||||
event_mgr.get_inst().add(Event_Name.UI_HIDE, this.onViewHide, this);
|
||||
}
|
||||
|
||||
static get_inst():pop_mgr
|
||||
{
|
||||
if(!this.inst) {
|
||||
this.inst = new pop_mgr();
|
||||
}
|
||||
return this.inst;
|
||||
}
|
||||
|
||||
show(path:string, transition?:UI_TRANSITION, ...params)
|
||||
{
|
||||
let views = this.view_map.get(path);
|
||||
if(!isDualInstanceView(path) && views && views.length > 0) {
|
||||
return views[0];
|
||||
}
|
||||
if(!views) {
|
||||
views = [];
|
||||
this.view_map.set(path, views);
|
||||
}
|
||||
const view = new PopView(path);
|
||||
const scene = cc.director.getScene();
|
||||
if(cc.isValid(scene)) {
|
||||
const viewRoot = scene.getChildByName("Canvas");
|
||||
if(cc.isValid(viewRoot)) {
|
||||
view.setParent(viewRoot);
|
||||
view.setTransition(transition);
|
||||
view.setModal(isModalView(path));
|
||||
view.show(UI_CONFIG_MAP[path], ...params);
|
||||
views.push(view);
|
||||
}
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
/** deprecated */
|
||||
hide_bypath(path:string)
|
||||
{
|
||||
const views = this.view_map.get(path);
|
||||
if(!views || views.length <= 0 || isDualInstanceView(path)) {
|
||||
return;
|
||||
}
|
||||
const view = views.pop();
|
||||
view.hide();
|
||||
}
|
||||
|
||||
has_views()
|
||||
{
|
||||
let ret = false;
|
||||
this.view_map.forEach(views => {
|
||||
if(views.length > 0) {
|
||||
ret = true;
|
||||
}
|
||||
});
|
||||
if(!ret) {
|
||||
ret = this.view_stack.length > 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
top_view()
|
||||
{
|
||||
if(this.view_stack.length > 0) {
|
||||
return this.view_stack[this.view_stack.length - 1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
clear()
|
||||
{
|
||||
this.view_map.forEach(views => {
|
||||
views.forEach(view => view.hide());
|
||||
});
|
||||
this.view_map.clear();
|
||||
this.view_stack.length = 0;
|
||||
if(cc.isValid(this.overlayNode)) {
|
||||
this.overlayNode.destroy();
|
||||
this.overlayNode = null;
|
||||
}
|
||||
cc.log(`PopMgr clear`);
|
||||
}
|
||||
|
||||
private onViewShow(view:PopView)
|
||||
{
|
||||
cc.log(`PopMgr, onViewShow, view path=${view.getPath()}`);
|
||||
this._pushViewToStack(view);
|
||||
this._updateOverlay();
|
||||
}
|
||||
|
||||
private onViewHide(view:PopView)
|
||||
{
|
||||
cc.log(`PopMgr, onViewHide, view path=${view.getPath()}`);
|
||||
this._removeViewFromMap(view);
|
||||
this._popViewFromStack(view);
|
||||
this._updateOverlay();
|
||||
}
|
||||
|
||||
private _removeViewFromMap(view:PopView)
|
||||
{
|
||||
const path = view.getPath();
|
||||
const views = this.view_map.get(path);
|
||||
if(views) {
|
||||
const idx = views.indexOf(view);
|
||||
if(idx != -1) {
|
||||
views.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _pushViewToStack(view:PopView)
|
||||
{
|
||||
if(this.view_stack.indexOf(view) == -1) {
|
||||
this.view_stack.push(view);
|
||||
}
|
||||
}
|
||||
|
||||
private _popViewFromStack(view:PopView)
|
||||
{
|
||||
const idx = this.view_stack.indexOf(view);
|
||||
if(idx != -1) {
|
||||
this.view_stack.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private _updateOverlay()
|
||||
{
|
||||
//找到最上层模态view
|
||||
const topModalView = this.getToppestModalView();
|
||||
if(!topModalView) {
|
||||
this._removeOverlay();
|
||||
return;
|
||||
}
|
||||
this._addOverlay(topModalView);
|
||||
}
|
||||
|
||||
private _removeOverlay()
|
||||
{
|
||||
const overlayNode = this.overlayNode;
|
||||
if(cc.isValid(overlayNode) && overlayNode.parent) {
|
||||
overlayNode.removeFromParent(true);
|
||||
overlayNode.destroy();
|
||||
this.overlayNode = null;
|
||||
cc.log(`PopMgr, removeOverlay`);
|
||||
}
|
||||
}
|
||||
|
||||
private _addOverlay(view:PopView)
|
||||
{
|
||||
cc.log(`PopMgr, _addOverlay, view=${view.getPath()}`);
|
||||
const viewRoot = view.getParent();
|
||||
if(!cc.isValid(viewRoot)) {
|
||||
cc.log(`PopMgr, _addOverlay, invalid viewRoot 1`);
|
||||
return;
|
||||
}
|
||||
const overlayNode = viewRoot.getChildByName(panel_overlay_bg);
|
||||
if(overlayNode) {
|
||||
this.overlayNode = overlayNode;
|
||||
this._addOverlayUnderView(overlayNode, view);
|
||||
// this._debugOverlay(viewRoot, overlayNode);
|
||||
return;
|
||||
}
|
||||
LoadingQueue.getInst().loadPrefabObj(UI_NAME.overlay_bg, gen_handler((overlayNode:cc.Node) => {
|
||||
if(!cc.isValid(viewRoot)) {
|
||||
cc.log(`PopMgr, _addOverlay, invalid viewRoot 2`);
|
||||
return;
|
||||
}
|
||||
if(view != this.getToppestModalView()) {
|
||||
// cc.log(`PopMgr, _addOverlay, ${view.getPath()} isn't toppest modal view`);
|
||||
return;
|
||||
}
|
||||
this.overlayNode = overlayNode;
|
||||
overlayNode.parent = viewRoot;
|
||||
overlayNode.name = panel_overlay_bg;
|
||||
this._addOverlayUnderView(overlayNode, view);
|
||||
// this._debugOverlay(viewRoot, overlayNode);
|
||||
}));
|
||||
}
|
||||
|
||||
private _addOverlayUnderView(overlayNode:cc.Node, view:PopView)
|
||||
{
|
||||
cc.log(`PopMgr, _addOverlayUnderView, view=${view.getPath()}`);
|
||||
overlayNode.opacity = getOverlayBgOpacity(view.getPath());
|
||||
const viewZOrder = view.getNode().getSiblingIndex();
|
||||
if(overlayNode.getSiblingIndex() < viewZOrder) {
|
||||
overlayNode.setSiblingIndex(viewZOrder - 1);
|
||||
}
|
||||
else {
|
||||
overlayNode.setSiblingIndex(viewZOrder);
|
||||
}
|
||||
}
|
||||
|
||||
private getToppestModalView()
|
||||
{
|
||||
const len = this.view_stack.length;
|
||||
if(len <= 0) {
|
||||
return null;
|
||||
}
|
||||
let modalView:PopView;
|
||||
for(let i = len - 1; i >= 0; i--) {
|
||||
const view = this.view_stack[i];
|
||||
const path = view.getPath();
|
||||
if(isModalView(path) && !isFullScreenView(path)) {
|
||||
modalView = view;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return modalView;
|
||||
}
|
||||
|
||||
private _debugOverlay(viewRoot:cc.Node, overlayNode:cc.Node)
|
||||
{
|
||||
cc.log("---------debug viewtree start-----------");
|
||||
viewRoot.children.forEach(c => cc.log(c.name + "\n"));
|
||||
cc.log(`overlay opcaity=${overlayNode.opacity}`);
|
||||
cc.log("---------debug viewtree end-----------");
|
||||
}
|
||||
}
|
||||
|
||||
export class PopView
|
||||
{
|
||||
private _path:string;
|
||||
private _node:cc.Node;
|
||||
private _parent:cc.Node;
|
||||
private _posX:number;
|
||||
private _posY:number;
|
||||
private _transition:UI_TRANSITION;
|
||||
private _config:UI_CONFIG;
|
||||
private _params:any[];
|
||||
private _isActive:boolean;
|
||||
private _isModal:boolean;
|
||||
private _isHide:boolean;
|
||||
|
||||
constructor(path:string)
|
||||
{
|
||||
this._path = path;
|
||||
pool_mgr.get_inst().get_ui(path, gen_handler((node:cc.Node)=>{
|
||||
if(this._isHide) {
|
||||
pool_mgr.get_inst().put_ui(path, node);
|
||||
return;
|
||||
}
|
||||
this._node = node;
|
||||
if(cc.isValid(this._parent)) {
|
||||
this._setParent(this._parent, this._posX, this._posY);
|
||||
}
|
||||
if(this._transition != null) {
|
||||
this._setTransition(this._transition);
|
||||
}
|
||||
if(this._params != null) {
|
||||
this._show(this._config, ...this._params);
|
||||
}
|
||||
if(this._isActive != null) {
|
||||
this._setActive(this._isActive);
|
||||
}
|
||||
if(this._isModal != null) {
|
||||
this._setModal(this._isModal);
|
||||
}
|
||||
}, this));
|
||||
}
|
||||
|
||||
show(config:UI_CONFIG, ...params)
|
||||
{
|
||||
this._config = config;
|
||||
this._params = params;
|
||||
if(this.isValid()) {
|
||||
this._show(config, ...params);
|
||||
}
|
||||
}
|
||||
|
||||
private _show(config:UI_CONFIG, ...params)
|
||||
{
|
||||
const path = this._path;
|
||||
cc.log(`PopView show, path=${path}`);
|
||||
event_mgr.get_inst().fire(Event_Name.UI_SHOW, this);
|
||||
|
||||
const uiBase = this._node.getComponent(POP_UI_BASE);
|
||||
uiBase.setView(this);
|
||||
uiBase.__show__(...params);
|
||||
}
|
||||
|
||||
hide()
|
||||
{
|
||||
const path = this._path;
|
||||
if(this._isHide) {
|
||||
cc.log(`PopView hide, path=${path}, view is already hided`);
|
||||
return;
|
||||
}
|
||||
this._isHide = true;
|
||||
if(cc.isValid(this._node)) {
|
||||
const uiBase = this._node.getComponent(POP_UI_BASE);
|
||||
uiBase.__hide__();
|
||||
uiBase.setView(null);
|
||||
pool_mgr.get_inst().put_ui(path, this._node, isDestroyAtOnce(path));
|
||||
|
||||
cc.log(`PopView hide, path=${path}`);
|
||||
event_mgr.get_inst().fire(Event_Name.UI_HIDE, this);
|
||||
}
|
||||
}
|
||||
|
||||
setParent(parent:cc.Node, x = 0, y = 0)
|
||||
{
|
||||
this._parent = parent;
|
||||
this._posX = x;
|
||||
this._posY = y;
|
||||
if(this.isValid()) {
|
||||
this._setParent(parent, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
private _setParent(parent:cc.Node, x = 0, y = 0)
|
||||
{
|
||||
this._node.setParent(parent);
|
||||
this._node.setPosition(x, y);
|
||||
}
|
||||
|
||||
setActive(active:boolean)
|
||||
{
|
||||
this._isActive = active;
|
||||
if(this.isValid()) {
|
||||
this._setActive(active);
|
||||
}
|
||||
}
|
||||
|
||||
private _setActive(active:boolean)
|
||||
{
|
||||
this._node.active = active;
|
||||
}
|
||||
|
||||
setModal(isModal:boolean)
|
||||
{
|
||||
if(this._isModal == isModal) {
|
||||
return;
|
||||
}
|
||||
this._isModal = isModal;
|
||||
if(this.isValid()) {
|
||||
this._setModal(isModal);
|
||||
}
|
||||
}
|
||||
|
||||
private _setModal(isModal:boolean)
|
||||
{
|
||||
const hasComp = this._node.getComponent(cc.BlockInputEvents);
|
||||
if(isModal && !hasComp) {
|
||||
this._node.addComponent(cc.BlockInputEvents);
|
||||
}
|
||||
else if(!isModal && hasComp) {
|
||||
this._node.removeComponent(cc.BlockInputEvents);
|
||||
}
|
||||
}
|
||||
|
||||
setTransition(transition:UI_TRANSITION)
|
||||
{
|
||||
this._transition = transition;
|
||||
if(this.isValid()) {
|
||||
this._setTransition(transition);
|
||||
}
|
||||
}
|
||||
|
||||
private _setTransition(transition:UI_TRANSITION)
|
||||
{
|
||||
transition = transition || {transType:UI_TRANSITION_TYPE.None};
|
||||
switch(transition.transType)
|
||||
{
|
||||
case UI_TRANSITION_TYPE.FadeIn:
|
||||
TweenUtil.from({target:this._node, duration:transition.duration || 1, opacity:0, tweenFunc:transition.tweenFunc || TweenFunc.Linear});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private isValid()
|
||||
{
|
||||
return cc.isValid(this._node) && !this._isHide;
|
||||
}
|
||||
|
||||
getPath()
|
||||
{
|
||||
return this._path;
|
||||
}
|
||||
|
||||
getParent()
|
||||
{
|
||||
return this._parent;
|
||||
}
|
||||
|
||||
getNode()
|
||||
{
|
||||
return this._node;
|
||||
}
|
||||
}
|
||||
|
||||
//界面prefab路径配置, 相对于assets/resources目录
|
||||
export const UI_NAME = {
|
||||
overlay_bg:"prefabs/panels/panel_overlay_bg",
|
||||
settlement:"prefabs/panels/panel_settlement",
|
||||
setting:"prefabs/panels/panel_setting",
|
||||
msgBox:"prefabs/panels/panel_msgBox",
|
||||
}
|
||||
|
||||
const UI_CONFIG_MAP = {
|
||||
[UI_NAME.msgBox]: {dualInstance:true},
|
||||
[UI_NAME.settlement]: {fullScreen:true, destroyAtOnce:true},
|
||||
}
|
||||
|
||||
export interface UI_CONFIG
|
||||
{
|
||||
needOverlayBg?:boolean;
|
||||
overlayBgOpacity?:number;
|
||||
dualInstance?:boolean;
|
||||
fullScreen?:boolean;
|
||||
destroyAtOnce?:boolean;
|
||||
}
|
||||
|
||||
interface UI_TRANSITION
|
||||
{
|
||||
transType:UI_TRANSITION_TYPE;
|
||||
tweenFunc?:Function;
|
||||
duration?:number;
|
||||
}
|
||||
|
||||
export const enum UI_TRANSITION_TYPE
|
||||
{
|
||||
None = 1,
|
||||
FadeIn,
|
||||
DropDown,
|
||||
PopUp,
|
||||
LeftIn,
|
||||
RightIn,
|
||||
}
|
||||
@@ -1,101 +1,80 @@
|
||||
import {pop_mgr, UI_CONFIG} from "./pop_mgr"
|
||||
import {pool_mgr} from "../pool/pool_mgr"
|
||||
import {handler, gen_handler} from "../util"
|
||||
import * as Audio from "../../common/audio/audioplayer"
|
||||
import {wxHttpClient} from "../../common/wxapi/index"
|
||||
|
||||
const {ccclass, property} = cc._decorator;
|
||||
const pop_overlay_bg:string = "panel_overlay_bg";
|
||||
|
||||
@ccclass
|
||||
export class POP_UI_BASE extends cc.Component {
|
||||
|
||||
@property(cc.Button)
|
||||
btn_close: cc.Button = null;
|
||||
|
||||
//界面名字,UI_CONFIG.*
|
||||
private _ui_name:string;
|
||||
protected is_show:boolean;
|
||||
|
||||
/*由于pop_mgr缓存策略,此方法只会在首次打开界面时调用1次, 缓存以后再打开不会执行onLoad.
|
||||
因此不能用来做每次打开界面时的初始化工作
|
||||
*/
|
||||
// onLoad()
|
||||
// {
|
||||
// cc.info("on_load 00000000000000000");
|
||||
// }
|
||||
|
||||
set ui_name(value:string)
|
||||
{
|
||||
this._ui_name = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 只能由pop_mgr调用
|
||||
*/
|
||||
__show__(...params:any[]):void
|
||||
{
|
||||
cc.info("show", this._ui_name, ...params);
|
||||
if(this.btn_close)
|
||||
{
|
||||
this.btn_close.node.on(cc.Node.EventType.TOUCH_END, this.onCloseBtnTouch, this);
|
||||
}
|
||||
this.is_show = true;
|
||||
this.on_show(...params);
|
||||
|
||||
//添加遮罩背景
|
||||
let overlay:cc.Node = this.node.getChildByName(pop_overlay_bg);
|
||||
if(!overlay)
|
||||
{
|
||||
pool_mgr.get_inst().get_ui(UI_CONFIG.overlay_bg, gen_handler((bg_node:cc.Node):void=>{
|
||||
if(!this.is_show || this.node.getChildByName(pop_overlay_bg))
|
||||
{
|
||||
pool_mgr.get_inst().put_ui(UI_CONFIG.overlay_bg, bg_node);
|
||||
return;
|
||||
}
|
||||
bg_node.name = pop_overlay_bg;
|
||||
this.node.addChild(bg_node);
|
||||
bg_node.setSiblingIndex(0);
|
||||
}, this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 只能由pop_mgr调用
|
||||
*/
|
||||
__hide__():void
|
||||
{
|
||||
cc.info("hide", this._ui_name);
|
||||
if(this.btn_close)
|
||||
{
|
||||
this.btn_close.node.off(cc.Node.EventType.TOUCH_END, this.onCloseBtnTouch, this);
|
||||
}
|
||||
this.is_show = false;
|
||||
this.on_hide();
|
||||
wxHttpClient.unregisterCtxHandler(this);
|
||||
}
|
||||
|
||||
/**弹出界面时调用,且在onLoad之后调用,可以用来做一些初始化工作*/
|
||||
on_show(...params:any[]):void
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**关闭界面时调用,用来做清理工作*/
|
||||
on_hide():void
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**关闭自身*/
|
||||
hide():void
|
||||
{
|
||||
pop_mgr.get_inst().hide(this._ui_name);
|
||||
}
|
||||
|
||||
onCloseBtnTouch():void
|
||||
{
|
||||
this.hide();
|
||||
Audio.AudioPlayer.getInst().play_sound(Audio.AUDIO_CONFIG.Audio_Btn);
|
||||
}
|
||||
import { PopView } from "./pop_mgr"
|
||||
import { BaseUIComponent } from "./base_ui_component";
|
||||
|
||||
const {ccclass, property} = cc._decorator;
|
||||
|
||||
@ccclass
|
||||
export class POP_UI_BASE extends BaseUIComponent {
|
||||
|
||||
@property(cc.Button)
|
||||
btn_close: cc.Button = null;
|
||||
|
||||
protected is_show:boolean;
|
||||
protected view:PopView;
|
||||
|
||||
setView(value:PopView)
|
||||
{
|
||||
this.view = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 只能由pop_mgr调用
|
||||
*/
|
||||
__show__(...params)
|
||||
{
|
||||
if(this.btn_close)
|
||||
{
|
||||
this.btn_close.node.on(cc.Node.EventType.TOUCH_END, this.onCloseBtnTouch, this);
|
||||
}
|
||||
this.node.on(cc.Node.EventType.TOUCH_END, this.onTouchSelf, this);
|
||||
this.is_show = true;
|
||||
this.on_show(...params);
|
||||
this.enabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 只能由pop_mgr调用
|
||||
*/
|
||||
__hide__()
|
||||
{
|
||||
if(this.btn_close)
|
||||
{
|
||||
this.btn_close.node.off(cc.Node.EventType.TOUCH_END, this.onCloseBtnTouch, this);
|
||||
}
|
||||
this.node.off(cc.Node.EventType.TOUCH_END, this.onTouchSelf, this);
|
||||
this.is_show = false;
|
||||
this.enabled = false;
|
||||
this.clearEventListeners();
|
||||
this.on_hide();
|
||||
}
|
||||
|
||||
/**弹出界面时调用,可以用来做初始化工作*/
|
||||
protected on_show(...params)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**关闭界面时调用,用来做清理工作*/
|
||||
protected on_hide()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**关闭自身*/
|
||||
hide()
|
||||
{
|
||||
if(this.view) {
|
||||
this.view.hide();
|
||||
}
|
||||
}
|
||||
|
||||
protected onCloseBtnTouch()
|
||||
{
|
||||
this.hide();
|
||||
}
|
||||
|
||||
protected onTouchSelf(event:cc.Event.EventTouch)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
138
ui/scene_mgr.ts
Normal file
138
ui/scene_mgr.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import { BaseScene } from "./base_scene";
|
||||
import { event_mgr, Event_Name } from "../event/event_mgr";
|
||||
import { AudioPlayer } from "../audio/audioplayer";
|
||||
import { DragonBoneFactory } from "../effect/dragonbone_factory";
|
||||
import { ParticleFactory } from "../effect/particle_factory";
|
||||
import { pop_mgr } from "./pop_mgr";
|
||||
import { loader_mgr } from "../loader/loader_mgr";
|
||||
import { Toast } from "./toast";
|
||||
import { pool_mgr } from "../pool/pool_mgr";
|
||||
import { LoadingQueue } from "../loader/loading_queue";
|
||||
|
||||
export class SceneMgr
|
||||
{
|
||||
private static _inst:SceneMgr
|
||||
private _loadingSceneName:string;
|
||||
private _currSceneName:string;
|
||||
private _currScene:BaseScene;
|
||||
|
||||
private constructor()
|
||||
{
|
||||
cc.director.on(cc.Director.EVENT_BEFORE_SCENE_LAUNCH, this.onSceneWillLaunch, this);
|
||||
}
|
||||
|
||||
static getInst():SceneMgr
|
||||
{
|
||||
if(!this._inst) {
|
||||
this._inst = new SceneMgr();
|
||||
}
|
||||
return this._inst;
|
||||
}
|
||||
|
||||
get currScene()
|
||||
{
|
||||
return this._currSceneName;
|
||||
}
|
||||
|
||||
preloadScene(sceneName:string)
|
||||
{
|
||||
cc.director.preloadScene(sceneName);
|
||||
}
|
||||
|
||||
loadScene(sceneName:string, ...params)
|
||||
{
|
||||
//reload current scene
|
||||
if(this._currSceneName == sceneName) {
|
||||
if(this._currScene && cc.isValid(this._currScene.node)) {
|
||||
this.closeAllView();
|
||||
this._currScene.__beforeDestroy__();
|
||||
this._currScene.__onStarted__(...params);
|
||||
event_mgr.get_inst().fire(Event_Name.SCENE_CHANGED, sceneName);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(this._loadingSceneName) {
|
||||
return;
|
||||
}
|
||||
this._loadingSceneName = sceneName;
|
||||
cc.director.loadScene(sceneName, (_, scene:cc.Scene) => {
|
||||
this._loadingSceneName = null;
|
||||
if(!cc.isValid(scene)) {
|
||||
cc.log(`SceneMgr, loadScene scene = null`);
|
||||
return;
|
||||
}
|
||||
//destroy old scene
|
||||
const oldDestroy = scene.destroy;
|
||||
scene.destroy = () => {
|
||||
this.onSceneWillDestroy();
|
||||
return oldDestroy.call(scene);
|
||||
};
|
||||
|
||||
//make sure each scene has a child named "Canvas"
|
||||
const baseScene = scene.getChildByName("Canvas").getComponent(BaseScene);
|
||||
if(baseScene && cc.isValid(baseScene.node)) {
|
||||
this._currScene = baseScene;
|
||||
this._currSceneName = sceneName;
|
||||
baseScene.setName(sceneName);
|
||||
baseScene.__onStarted__(...params);
|
||||
event_mgr.get_inst().fire(Event_Name.SCENE_CHANGED, sceneName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private closeAllView()
|
||||
{
|
||||
//旧场景destroy前关闭所有已打开的界面
|
||||
pop_mgr.get_inst().clear();
|
||||
pool_mgr.get_inst().clear();
|
||||
}
|
||||
|
||||
private onSceneWillDestroy()
|
||||
{
|
||||
if(this._currScene && cc.isValid(this._currScene.node)) {
|
||||
this.closeAllView();
|
||||
this._currScene.__beforeDestroy__();
|
||||
this._currScene = null;
|
||||
this._currSceneName = null;
|
||||
}
|
||||
}
|
||||
|
||||
private onSceneWillLaunch(newScene:cc.Scene)
|
||||
{
|
||||
//收集新场景依赖的资源
|
||||
const excludeMap = {};
|
||||
const newSceneAssets = newScene.dependAssets;
|
||||
if(newSceneAssets) {
|
||||
newSceneAssets.forEach(a => {
|
||||
excludeMap[a] = true;
|
||||
// cc.log(`newSceneAssets, asset=${a}`);
|
||||
});
|
||||
}
|
||||
|
||||
//收集持久节点依赖的资源
|
||||
const toast = newScene.getChildByName("toast");
|
||||
if(toast) {
|
||||
cc.log("toast in current scene");
|
||||
const deps = cc.loader.getDependsRecursively(Toast.resPath);
|
||||
deps.forEach(d => {
|
||||
excludeMap[d] = true;
|
||||
// cc.log("prefabs/misc/toast deps=" + d);
|
||||
});
|
||||
}
|
||||
|
||||
//激活新场景前释放旧场景资源
|
||||
Toast.clear();
|
||||
AudioPlayer.getInst().clear_cache();
|
||||
DragonBoneFactory.getInst().releaseAll();
|
||||
ParticleFactory.getInst().releaseAll();
|
||||
LoadingQueue.getInst().clear();
|
||||
loader_mgr.get_inst().releaseAll(excludeMap);
|
||||
}
|
||||
}
|
||||
|
||||
export const SCENE_NAME = {
|
||||
launch:"hotupdate",
|
||||
main:"newmain",
|
||||
battle:"battle",
|
||||
}
|
||||
112
ui/toast.ts
Normal file
112
ui/toast.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { loader_mgr } from "../loader/loader_mgr";
|
||||
import { gen_handler } from "../util";
|
||||
|
||||
export class Toast
|
||||
{
|
||||
public static resPath = "prefabs/misc/toast";
|
||||
private static inst:Toast;
|
||||
private nodePool:cc.Node[];
|
||||
|
||||
private constructor()
|
||||
{
|
||||
this.nodePool = [];
|
||||
}
|
||||
|
||||
private static getInst()
|
||||
{
|
||||
if(!this.inst)
|
||||
{
|
||||
this.inst = new Toast();
|
||||
}
|
||||
return this.inst;
|
||||
}
|
||||
|
||||
private run(node:cc.Node)
|
||||
{
|
||||
node.parent = cc.director.getScene();
|
||||
cc.game.addPersistRootNode(node);
|
||||
|
||||
node.opacity = 0;
|
||||
node.setPosition(375, 0);
|
||||
const moveUp = cc.moveBy(0.2, cc.v2(0, 117));
|
||||
const fadeIn = cc.fadeIn(0.2);
|
||||
const moveFadeAct = cc.spawn(moveUp, fadeIn);
|
||||
const delayAction = cc.delayTime(1);
|
||||
const fadeOut = cc.fadeOut(0.5);
|
||||
const onFinished = cc.callFunc(() => {
|
||||
cc.game.removePersistRootNode(node);
|
||||
node.removeFromParent();
|
||||
this.nodePool.push(node);
|
||||
}, this);
|
||||
node.runAction(cc.sequence(moveFadeAct, delayAction, fadeOut, onFinished));
|
||||
}
|
||||
|
||||
private configure(node:cc.Node, params:ToastParmas)
|
||||
{
|
||||
const label = node.getChildByName("txt").getComponent(cc.Label);
|
||||
label.string = params.txt;
|
||||
label.node.color = params.txtColor || cc.color(255, 255, 255);
|
||||
|
||||
//label size
|
||||
label.overflow = cc.Label.Overflow.NONE;
|
||||
label._updateRenderData(true);
|
||||
let labelWidth = label.node.width;
|
||||
let labelHeight = label.node.height;
|
||||
if(labelWidth > 500) {
|
||||
labelWidth = 500;
|
||||
label.node.width = labelWidth;
|
||||
label.overflow = cc.Label.Overflow.RESIZE_HEIGHT;
|
||||
label._updateRenderData(true);
|
||||
labelHeight = label.node.height;
|
||||
if(labelHeight > 90) {
|
||||
labelHeight = 90
|
||||
label.node.height = labelHeight;
|
||||
label.overflow = cc.Label.Overflow.SHRINK;
|
||||
}
|
||||
}
|
||||
|
||||
//bg size
|
||||
const bg = label.node.parent;
|
||||
let bgWidth = Math.max(397, labelWidth + 50);
|
||||
let bgHeight = Math.max(76, labelHeight + 40);
|
||||
bg.setContentSize(bgWidth, bgHeight);
|
||||
|
||||
this.run(node);
|
||||
}
|
||||
|
||||
private make(params:ToastParmas)
|
||||
{
|
||||
if(this.nodePool.length > 0)
|
||||
{
|
||||
this.configure(this.nodePool.pop(), params);
|
||||
}
|
||||
else
|
||||
{
|
||||
loader_mgr.get_inst().loadPrefabObj(Toast.resPath, gen_handler((node:cc.Node) => {
|
||||
this.configure(node, params);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private clear()
|
||||
{
|
||||
this.nodePool.forEach(node => node.destroy());
|
||||
this.nodePool.length = 0;
|
||||
}
|
||||
|
||||
static show(params:ToastParmas)
|
||||
{
|
||||
Toast.getInst().make(params);
|
||||
}
|
||||
|
||||
static clear()
|
||||
{
|
||||
Toast.getInst().clear();
|
||||
}
|
||||
}
|
||||
|
||||
interface ToastParmas
|
||||
{
|
||||
txt:string;
|
||||
txtColor?:cc.Color;
|
||||
}
|
||||
141
util.ts
141
util.ts
@@ -1,81 +1,62 @@
|
||||
import {loader_mgr} from "../common/loader/loader_mgr"
|
||||
import * as consts from "../consts"
|
||||
|
||||
let handler_pool:handler[] = [];
|
||||
let handler_pool_size = 10;
|
||||
|
||||
//用于绑定回调函数this指针
|
||||
export class handler
|
||||
{
|
||||
private cb:Function;
|
||||
private host:any;
|
||||
private args:any[];
|
||||
|
||||
constructor(){}
|
||||
|
||||
init(cb:Function, host = null, ...args)
|
||||
{
|
||||
this.cb = cb;
|
||||
this.host = host;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
exec(...extras)
|
||||
{
|
||||
this.cb.apply(this.host, this.args.concat(extras));
|
||||
}
|
||||
}
|
||||
|
||||
export function gen_handler(cb:Function, host:any = null, ...args:any[]):handler
|
||||
{
|
||||
let single_handler:handler = handler_pool.length < 0 ? handler_pool.pop(): new handler()
|
||||
//这里要展开args, 否则会将args当数组传给wrapper, 导致其args参数变成2维数组[[]]
|
||||
single_handler.init(cb, host, ...args);
|
||||
return single_handler;
|
||||
}
|
||||
|
||||
export function load_img(sprite, img_path)
|
||||
{
|
||||
loader_mgr.get_inst().loadAsset(img_path, gen_handler((res) => {
|
||||
sprite.spriteFrame = res;
|
||||
}), cc.SpriteFrame);
|
||||
}
|
||||
|
||||
export function load_external_img(sprite:cc.Sprite, img_url:string, type?:string)
|
||||
{
|
||||
// console.log(`load_external_img ${img_url}`);
|
||||
loader_mgr.get_inst().loadExternalAsset(img_url, gen_handler((res) => {
|
||||
// console.log(sprite.spriteFrame, res, (res instanceof cc.Texture2D));
|
||||
sprite.spriteFrame = new cc.SpriteFrame(res);
|
||||
}), type);
|
||||
}
|
||||
|
||||
export function strfmt(fmt:string, ...args:any[])
|
||||
{
|
||||
return fmt.replace(/\{(\d+)\}/g, (match:string, argIndex:number) => {
|
||||
return args[argIndex] || match;
|
||||
});
|
||||
}
|
||||
|
||||
export function extend(target, ...sources) {
|
||||
for (var i = 0; i < sources.length; i += 1) {
|
||||
var source = sources[i];
|
||||
for (var key in source) {
|
||||
if (source.hasOwnProperty(key)) {
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
export function createBreathAction(node:cc.Node)
|
||||
{
|
||||
const action = cc.repeatForever(cc.sequence(cc.scaleTo(0.6, 1.1), cc.scaleTo(0.6, 0.9)));
|
||||
node.runAction(action);
|
||||
}
|
||||
|
||||
export function destroyBreathAction(node:cc.Node)
|
||||
{
|
||||
node.stopAllActions();
|
||||
let handler_pool:handler[] = [];
|
||||
let handler_pool_size = 10;
|
||||
|
||||
//用于绑定回调函数this指针
|
||||
export class handler
|
||||
{
|
||||
private cb:Function;
|
||||
private host:any;
|
||||
private args:any[];
|
||||
|
||||
constructor(){}
|
||||
|
||||
init(cb:Function, host = null, ...args)
|
||||
{
|
||||
this.cb = cb;
|
||||
this.host = host;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
exec(...extras)
|
||||
{
|
||||
this.cb.apply(this.host, this.args.concat(extras));
|
||||
}
|
||||
}
|
||||
|
||||
export function gen_handler(cb:Function, host:any = null, ...args:any[]):handler
|
||||
{
|
||||
let single_handler:handler = handler_pool.length < 0 ? handler_pool.pop(): new handler()
|
||||
//这里要展开args, 否则会将args当数组传给wrapper, 导致其args参数变成2维数组[[]]
|
||||
single_handler.init(cb, host, ...args);
|
||||
return single_handler;
|
||||
}
|
||||
|
||||
export function strfmt(fmt:string, ...args:any[])
|
||||
{
|
||||
return fmt.replace(/\{(\d+)\}/g, (match:string, argIndex:number) => {
|
||||
return args[argIndex] || match;
|
||||
});
|
||||
}
|
||||
|
||||
export function extend(target, ...sources) {
|
||||
for (var i = 0; i < sources.length; i += 1) {
|
||||
var source = sources[i];
|
||||
for (var key in source) {
|
||||
if (source.hasOwnProperty(key)) {
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
export function createBreathAction(node:cc.Node, min = 0.9, max = 1.1)
|
||||
{
|
||||
const action = cc.repeatForever(cc.sequence(cc.scaleTo(0.6, max), cc.scaleTo(0.6, min)));
|
||||
node.runAction(action);
|
||||
}
|
||||
|
||||
export function destroyBreathAction(node:cc.Node)
|
||||
{
|
||||
node.stopAllActions();
|
||||
}
|
||||
Reference in New Issue
Block a user