Commit 037edd74 authored by Master Q's avatar Master Q

用tween 做动画, 如何做到 根据visible 去 清除

parent e8f81782
......@@ -77,13 +77,15 @@ export const DotAni: OriginalElementWidthEvents<{
}
export class DreamDotAni extends Dream.RenderContainer {
cont: FYGE.Container
render() {
return (
<FYGE.Container>
<FYGE.Container ref={el => this.cont = el}>
<FPShapeOfRectV2 type="rect" alpha={0.6} fillColor="#00000"></FPShapeOfRectV2>
{
Array.from({
length: 3
length: 1
}, (_, index) => {
return (
<DotAni inlineProps={{
......
......@@ -103,14 +103,23 @@ export function createElement<K extends new(p?: any) => ChildType, T extends Chi
eleins.addEventListener(FYGE.Event.ADDED_TO_STAGE, props.onAddedToStage!)
}
// 被移除的时候直接 去 清除 Tween
eleins.addEventListener(FYGE.Event.REMOVED_FROM_STAGE, function(e: any) {
e && e.target && FYGE.Tween.removeTweens(e.target)
})
// _noap 不会加到后面
!props._noap && addChildFromParent(eleins, children)
} else if (eleins instanceof RenderContainer) {
// 因为是 倒序的 nextTick 这里就先 didRendered 注册一下, 先让 render 里面的 ref 跑完
nextTick(function() {
(eleins as RenderContainer).didRendered()
(eleins as RenderContainer).didRendered();
(eleins as RenderContainer).isDidRendered = true
}, eleins)
const node = eleins.render()
const node: FYGE.Container = eleins.render()
node.addEventListener(FYGE.Event.REMOVED_FROM_STAGE, () => {
(eleins as RenderContainer).unMount()
})
// Promise.resolve().then(() => {
// // @ts-ignore
// eleins.didRendered()
......
type PropsWithRef<P extends Record<string, any>> = P & { ref?: (e: any) => any };
export class RenderContainer<P extends Record<string, any> = any> {
class T {}
export class RenderContainer<P extends Record<string, any> = any> extends T {
readonly props: Readonly<P> & Readonly<{ children?: FYGE.Container[] }>;
isDidRendered: boolean
constructor(props: Readonly<P> | P | Readonly<{ref?: (e: RenderContainer) => any} & P>) {
super()
this.props = props
}
......@@ -10,6 +16,10 @@ export class RenderContainer<P extends Record<string, any> = any> {
}
unMount() {
}
render(): any {
}
......
......@@ -2,6 +2,20 @@ export function isFunction(p: any) {
return typeof p === 'function'
}
/**
* 递归清除显示对象里面所有的Tween
* @param obj
* @param isRecursive 默认true,递归移除子级
*/
export function removeTweens(obj: any, isRecursive: boolean = true) {
if (!obj) return
FYGE.Tween.removeTweens(obj);
if (!isRecursive || !obj.children || !obj.children.length) return
obj.children.forEach((child: any) => {
removeTweens(child)
});
}
const inBrowser = !!document // 简单 的 判断 是否在浏览器中
// nextTick 简单原理
......
import Dream from "../Dream"
import { FPShapeOfRectV2 } from "../Dream/UI"
import { layers } from "./layers"
import { UseAni } from "./UseDecorator/useAni"
type UseAniConfig = {
showCall?: (c: FYGE.Container, ...args: any[]) => Promise<any>
hideCall?: (c: FYGE.Container) => Promise<any>
}
type ShowModalType = {
center: boolean,
maskAlpha: number
destroy: boolean
} & UseAniConfig
const defaultModalConfig:ShowModalType = {
center: true,
maskAlpha: 0.7,
destroy: true,
showCall: fadeIn,
hideCall: fadeOut
}
function fadeIn(cont: FYGE.Container) {
return new Promise(r => {
FYGE.Tween.removeTweens(cont)
FYGE.Tween.get(cont)
.set({alpha: 0})
.to({
alpha: 1
}, 100, FYGE.Ease.quadIn)
.call(r)
})
}
function fadeOut(cont: FYGE.Container) {
return new Promise(r => {
FYGE.Tween.removeTweens(cont)
FYGE.Tween.get(cont)
.to({
alpha: 0
}, 100, FYGE.Ease.quadIn)
.call(r)
})
}
function ModalWraper(ModalNode: any) {
return class extends Dream.RenderContainer {
ModalContainer: FYGE.Container
ModalBody: FYGE.Container
maskAniCont: { show: () => Promise<any>; hide: () => Promise<any> }
ModalBodyAniCont: any
didRendered() {
const ModalConfig = this.props._config
if (ModalConfig?.center) {
const body = this.ModalBody
this.ModalBody.position.set(layers.stageWidth / 2 - body.width / 2, layers.stageHeight / 2 - body.height / 2)
}
}
showModal = async () => {
const _ModalContainer = this.ModalContainer
this.ModalContainer?.parent?.addChild(_ModalContainer)
_ModalContainer.visible = true
await Promise.all([
this.maskAniCont.show(),
this.ModalBodyAniCont.show()
])
}
hideModal = async() => {
const _ModalContainer = this.ModalContainer
await Promise.all([
this.maskAniCont.hide(),
this.ModalBodyAniCont.hide()
])
_ModalContainer.visible = false
}
unMount(): void {
}
render() {
const {
maskAlpha = 0.7,
showCall,
hideCall
} = this.props._config
const {_config, ...otherProps} = this.props
const MaskCont: any = UseAni({
showCall: fadeIn,
hideCall: fadeOut
})(FPShapeOfRectV2)
const ModalBodyAniCont = UseAni({
showCall,
hideCall
})(function() {
return (
<ModalNode {...otherProps} ref={(el: any) => {
this.AniCont = el
}}></ModalNode>
)
})
return (
<FYGE.Container inlineProps={{
y: layers.stageOffsetY
}} ref={(el: FYGE.Container) => {
this.ModalContainer = el
}}>
<MaskCont getAniIns={(ins: { show: () => Promise<any>; hide: () => Promise<any> }) => {
this.maskAniCont = ins
}} alpha={maskAlpha}></MaskCont>
<FYGE.Container ref={(el: any) => {
this.ModalBody = el
}}>
<ModalBodyAniCont getAniIns={ins => {
this.ModalBodyAniCont = ins
}}></ModalBodyAniCont>
</FYGE.Container>
</FYGE.Container>
)
}
}
}
export const ModalCtroller = (function() {
const ModalMap = new Map()
return {
showModal(ModalNode: any, props?: Record<string, any>, config?: Partial<ShowModalType>) {
const _ = ModalMap.get(ModalNode)
const _config = Object.assign({}, defaultModalConfig, config)
if (_) {
_.showModal(_config)
return
}
const M = ModalWraper(ModalNode)
const cont = new FYGE.Container()
layers.popupLayer.addChild(cont)
Dream.VirtualRender(cont, <M
{...props}
_config={_config}
ref={(el: { showModal: () => void }) => {
ModalMap.set(ModalNode, el)
el.showModal()
}}
closeModal={() => {
ModalCtroller.closeModal(ModalNode)
}}
></M>)
},
async closeModal(ModalNode: any) {
const _ = ModalMap.get(ModalNode)
if (_) {
await _.hideModal()
_.ModalContainer.parent.removeChild(_.ModalContainer);
console.log('deeeee');
(_.ModalContainer as FYGE.Container).destroy()
return
}
}
}
})()
\ No newline at end of file
......@@ -6,27 +6,57 @@ type UseAniConfig = {
hideCall?: (c: FYGE.Container) => Promise<any>
}
/**
* 递归清除显示对象里面所有的Tween
* @param obj
* @param isRecursive 默认true,递归移除子级
*/
export function removeTweens(obj: any, isRecursive: boolean = true) {
if (!obj) return
FYGE.Tween.removeTweens(obj);
if (!isRecursive || !obj.children || !obj.children.length) return
obj.children.forEach((child: any) => {
removeTweens(child)
});
}
export function UseAni(cfg: UseAniConfig) {
return function(Node: any) {
return class extends Dream.RenderContainer<{
getAniIns: (ins: any) => any
getAniIns?: (ins: {
show: () => Promise<any>
hide: () => Promise<any>,
aniCont: FYGE.Container
}) => any
}> {
aniCont: FYGE.Container
didRendered(): void {
this.props.getAniIns && this.props.getAniIns({
show: this.showAni,
hide: this.hideAni,
aniCont: this.aniCont
})
this.showAni()
}
async showAni() {
showAni = async () => {
if (this.aniCont.visible) return
this.aniCont.visible = true
if (cfg.showCall) {
await cfg.showCall(this.aniCont)
}
}
async hideAni() {
hideAni = async () => {
if (!this.aniCont.visible) return
if (cfg.hideCall) {
await cfg.hideCall(this.aniCont)
}
// 清楚 子级的 tween
removeTweens(this.aniCont)
console.log('removeTween')
this.aniCont.visible = false
}
......@@ -38,6 +68,8 @@ export function UseAni(cfg: UseAniConfig) {
return (
<Container ref={el => {
this.aniCont = el
}} inlineProps={{
visible: false
}}>
<Node {...othersProps}></Node>
</Container>
......
import Dream from "../../Dream"
import { Container, DreamDotAni } from "../../Dream/UI"
import { createImperativePromise, ImperativePromise } from "../awesome-promise"
import { ModalCtroller } from "../ModalControl"
import { RES } from "../RES"
type UsePreloadType = {
resList: string[]
resPreloadFunc: Record<string, (res: string) => Promise<any>>,
loadingComponent: any,
}
const defaultConfig: UsePreloadType = {
resList: [],
resPreloadFunc: {
others: RES.getResAsync
},
loadingComponent: DreamDotAni
}
const loadResourceOnce = (function() {
interface QueueItem {
queue: {
onLoad: Function,
onProgress?: Function,
}[],
data: any // 这里可以 通过 data 字段来判断是否 加载过, 如果是请求的话, 可能就不需要了, 直接 delete loading[url] 就好了
wrapPrevPromise?: ImperativePromise<void>
}
const loading: Record<string, QueueItem> = {}
return function(url: string, onLoad: Function, onProgress: Function, loadFunc: (s: string, rc:(d: any) => void)=>Promise<any>, force?: boolean) {
if (!force) {
if (loading[url] && loading[url].data) {
onLoad(true, loading[url].data)
// TODO onProgress
return
}
// 有的话, 说明正在加载中
if (loading[url]) {
loading[url].queue.push({
onLoad,
onProgress
})
return
}
} else {
// cancel之前的 load Promise
loading[url] && loading[url].wrapPrevPromise && loading[url].wrapPrevPromise.cancel()
}
// 否则的话, 就是没有加载
const currItem: QueueItem = loading[url] || (loading[url] = {
queue: [],
data: null,
})
currItem.queue.push({
onLoad,
onProgress
})
const type = url.split('.').pop()
loading[url].wrapPrevPromise = createImperativePromise(
new Promise<{
url: string,
suc: boolean,
data?: any
}>((resolve) => {
loadFunc(url, (d) => {
if (d) {
resolve({
url,
suc: true,
data: d
})
} else {
resolve({
suc: false,
url
})
}
})
}).then((cfg) => {
// 防止 force 的时候,又 重新跑多余的逻辑
while (currItem.queue.length) {
currItem.queue.shift().onLoad(cfg.suc, cfg.data)
}
if (!cfg.suc) {
delete loading[cfg.url]
} else {
currItem.data = cfg.data
return cfg.data
}
})
)
return loading[url].wrapPrevPromise.promise
}
})()
/**
* 预先加载资源
* @param resouces 资源列表
* @param parallelMode 是否并行加载
* @param func 加载资源方法
* @returns
*/
export function onPreloadResource(
resouces: string[],
parallelMode: boolean = false,
func?: { [x: string]: (res: string) => Promise<any> },
onProcess?: (loaded: number, total: number) => void,
onLoaded?: (total: number) => void,
) {
if (!resouces || resouces.length === 0) {
return onLoaded(0);
}
return new Promise(async (resolve) => {
let totalCount = resouces.length,
loadedCount = 0;
// 不管加载有没有成功 都 loaded +1
const loadedSucOrError = () => {
loadedCount++;
try {
onProcess && onProcess(loadedCount, totalCount);
if (loadedCount === totalCount) {
onLoaded && onLoaded(totalCount);
}
} catch(e) {
console.error('onProcess or on Loaded exec Error:' + e);
}
return
}
const loadFunc = async function(item: string) {
const type = item.split('.').pop()
const re: any = await loadResourceOnce(item, () => { }, () => { }, func[type] || func['others'])
loadedSucOrError()
if (!re) {
console.error('onPreloadOnce Error: ' + item)
}
return re
}
if (parallelMode) {
await Promise.all(
resouces.map((item: string) => {
return loadFunc(item)
}),
);
} else {
for (let i = 0; i < resouces.length; i++) {
const item = resouces[i]
await loadFunc(item)
}
}
resolve(loadedCount);
});
}
export function UsePreload(cfg: Partial<UsePreloadType>) {
const finalCfg = Object.assign({}, defaultConfig, cfg, {
resPreloadFunc: Object.assign({}, defaultConfig.resPreloadFunc, cfg.resPreloadFunc)
})
return function(Node: any) {
return class extends Dream.RenderContainer<{
onLoaded?: () => any
}> {
RoleCont: FYGE.Container
type: string = 'preloadContainer'
render() {
const LoadingCompent = finalCfg.loadingComponent
onPreloadResource(
finalCfg.resList,
true,
finalCfg.resPreloadFunc,
(loaded, total) => {
},
() => {
this.props.onLoaded && this.props.onLoaded()
ModalCtroller.closeModal(LoadingCompent)
if (this.RoleCont) {
this.RoleCont.addChild(<Node></Node>)
}
}
)
ModalCtroller.showModal(LoadingCompent, {}, {
maskAlpha: 0,
center: false
})
return <Container ref={el => {
this.RoleCont = el
}}>
</Container>
}
}
}
}
\ No newline at end of file
......@@ -47,6 +47,7 @@ function SceneWrapper(SceneNode: any) {
export const SceneController = (function() {
let controller: FYGE.Container
const SceneMap = new Map()
let preScene
return {
init(cont: FYGE.Container) {
if (!controller) {
......
import Dream from "../Dream";
import { DotAni, DreamDotAni, DreamSpriteV2 } from "../Dream/UI";
import { ModalCtroller } from "../modules/ModalControl";
import { UseAni } from "../modules/UseDecorator/useAni";
import { PreloadeTest } from "./test";
function fadeIn(cont: FYGE.Container) {
return new Promise(r => {
......@@ -44,27 +46,54 @@ export class HomeScene extends Dream.RenderContainer {
cont: FYGE.Container
addFadeinCont() {
this.cont.addChild(<Test tt={3} ref={el => {
this.cont.addChild(<Test tt={3} ref={(el: any) => {
setTimeout(() => {
el.hideAni()
}, 3000)
}}></Test>)
}
testModal() {
ModalCtroller.showModal(function(props: any) {
return <FYGE.TextField onClick={props.closeModal} inlineProps={{
text: '???',
size: 100,
fillColor: '#000000'
}}></FYGE.TextField>
}, {}, {
showCall: function(t) {
return new Promise(r => {
FYGE.Tween.removeTweens(t)
const oy = t.y
FYGE.Tween.get(t)
.set({y: oy + 400})
.to({y: oy}, 300)
.call(r)
})
}
})
}
render() {
return (
// <DreamSpriteV2 src="http://qnpic.top/yoona2.jpg"></DreamSpriteV2>
<FYGE.Container ref={(el: any) => {
<FYGE.Container inlineProps={{
y: 200
}} ref={(el: any) => {
this.cont = el
}}>
<FYGE.TextField onClick={() => {
this.addFadeinCont()
// this.addFadeinCont()
this.testModal()
}} inlineProps={{
text: '爱的爱我',
text: '杭州要下雨',
size: 90,
fillColor: '#000000'
}}></FYGE.TextField>
<DreamDotAni></DreamDotAni>
{/* <DreamDotAni></DreamDotAni> */}
<PreloadeTest onLoaded={() => {
console.log('123123')
}}></PreloadeTest>
</FYGE.Container>
)
}
......
import Dream from "../Dream";
import { RES } from "../modules/RES";
import { UsePreload } from "../modules/UseDecorator/usePreload";
@UsePreload({
resList: Array.from({
length: 10
}, (_, i) => {
return `http://qnpic.top/yoona${i + 2}.jpg`
})
})
export class PreloadeTest extends Dream.RenderContainer {
render() {
console.log(RES.getRes('http://qnpic.top/yoona3.jpg'))
return (
<FYGE.Container>12312312</FYGE.Container>
)
}
}
\ No newline at end of file
export namespace Dream {
}
declare global {
namespace JSX {
interface IntrinsicElements{
div: any
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment