1.UE4动画系统播放Montage源码浅析(二)
2.Vue2剥丝抽茧-响应式系统之nextTick
3.解读useEffectåuseLayouEffectåç
UE4动画系统播放Montage源码浅析(二)
在先前的忙循码循文章中,我们对UE4动画蒙太奇播放过程进行了探讨,环源环源本篇将深入解析蒙太奇的代码其他相关知识,包括蒙太奇插槽、忙循码循蒙太奇片段和动画片段等。环源环源所分析的代码9.27选股源码源码版本为4.。
关于蒙太奇结构,忙循码循UAnimMontage蒙太奇动画可视为一种动态表现手段,环源环源无需将混合空间或动画序列拖入动画蓝图,代码只需在动画蓝图里放置一个FAnimNode_Slot动画节点,忙循码循即可通过montage_play接口播放该插槽下的环源环源所有蒙太奇资源。
这意味着我们无需修改动画蓝图,代码deepnode源码就可以播放全新的忙循码循动作。
蒙太奇动画除了动态播放动作外,环源环源还有更多应用。代码例如,现实中的蒙太奇概念。蒙太奇(montage)在法语中意为“剪接”,但在俄国,它被发展成一种**中镜头组合的理论。例如,将母亲煮菜、洗衣、minidso源码带小孩、父亲看报等镜头放在一起,会给人一种母亲“忙碌”的感觉,从而产生对比手法,突出人物或事物的具体特征,两个不同的片段之间相互联系,产生意想不到的效果。
如上所述,这类动画被称为蒙太奇,因为它还包括剪接、片段、upedit源码组合等特点,可用于循环播放动画、跳转到下一个动画等。
创建一个动画序列的蒙太奇,会看到如下面板:区域1为蒙太奇插槽,在动画蓝图中也要有对应插槽节点才会播放此蒙太奇;蒙太奇资源中可以添加多个插槽。区域2为蒙太奇片段,蒙太奇资源中可以创建多个片段并设置它们之间的关系,用于动画的跳转、循环等。区域3为动画片段,patient源码每个插槽下可以添加多个动画片段。
蒙太奇片段对应上图示例有三个片段:Default、Loop、End,我们可以设置它们之间的关系。图中Default片段后面的箭头图标表示播放完毕后会接着播放Loop,Loop片段后的循环图标表示循环播放Loop。如果我们显式跳转到End片段,End片段后面没有其他片段,那么播放结束后就结束了。
蒙太奇片段是独立的,与插槽、动画片段没有任何关系,它只是根据蒙太奇片段之间的关系确定当前播放时间。了解了蒙太奇片段的作用,我们来看具体实现。其数据结构如下:蒙太奇片段由FCompositeSection结构描述,CompositeSections就是蒙太奇资源上序列化的蒙太奇片段数组。
了解了基本数据结构,再看如何根据动画片段获取蒙太奇姿势。结合上一篇文章,姿势获取最后是调用FAnimInstanceProxy::SlotEvaluatePose函数,并遍历MontageEvaluationData数据(其中包含蒙太奇实例的时间、权重、蒙太奇引用等数据)。
以上便是关于UE4动画系统播放Montage源码的解析,希望对大家有所帮助。
Vue2剥丝抽茧-响应式系统之nextTick
在 Vue2 的源码解析系列文章中,探讨响应式系统的核心概念——nextTick。了解 dom 更新机制,认识到浏览器中的 js 引擎线程与 GUI 渲染线程交替执行,且 dom 更新发生在 js 线程,但需要渲染线程绘制后,用户才能观察到。因此,若 js 线程执行过长,可能无法及时将更新的 dom 显示给用户。
以极端情况为例,即使在js 线程中修改了 dom,如果 js 线程忙于执行其他任务,导致渲染线程未能及时绘制更新的 dom,用户将无法看到这些修改。引入 bundle.js 的情况下,即便修改了 dom,如果 js 线程陷入死循环,用户界面仍会保持空白状态,尽管 dom 已经更新。
值得注意的是,在 js 线程中连续修改同一 dom 元素,最终渲染时,显示的 dom 结果是最后一次修改后的状态。即使在 js 线程中进行了多次修改,最终呈现给用户的 dom 结果只会是最后一次修改的结果。
讨论到 js 线程中的宏任务与微任务队列,简单阐述宏任务与微任务的概念,以及它们如何影响事件循环的执行顺序。然而,这里不深入探讨具体实现细节,读者可进一步研究相关资料以获取更多深入理解。
解读useEffectåuseLayouEffectåç
èæ¯
åè¿ç¯æç« æ¯å 为工ä½ä¸ä¸æ¯é常ç¹å¿ï¼å¯ä»¥æ½ç©ºå¦ä¹ èªå·±å¸¸ç¨æ¡æ¶åç±»åºï¼æ·±å ¥ç解å®ä»¬ï¼å¨ææ¯ä¸å¸æææ´å¤§çè¿æ¥ï¼å¹å »å¦ä¹ å ´è¶£ï¼
useEffectåå ¶å®hooksä¸æ ·ï¼å è½½åæ´æ°æ§è¡ä¸ä¸æ ·çæ¹æ³(mountEffectåupdateEffect)ï¼
1.mountEffect页é¢å è½½æ¶ï¼æ§è¡mountEffectï¼
å建hook对象ï¼å å ¥ç»ä»¶çhookååé¾è¡¨ï¼
å¨ç»ä»¶çfiberçflagä¸å å ¥å¯ä½ç¨ç¸å ³çeffectTagï¼(å è½½æé´é»è®¤ælayoutEffectåeffectçå¯ä½ç¨)
å建effect对象ï¼ç»hook对象çmemoizedStateåå å ¥ç»ä»¶fiberçupdateQueueä¸å½¢æeffectç¯ç¶é¾è¡¨ï¼å¨æ¸²æå·¥ä½å®æåï¼ä¼å¾ªç¯è¿ä¸ªç¯ç¶é¾è¡¨ï¼æ§è¡æ¯ä¸ªeffect对象çdestoryåcreateï¼
consteffect={ tag,create,destroy,deps,next:null};tagæ¯effectçç±»åtag为9æ¯useEffect,5æ¯useLayoutEffectcreateæ¯useEffectæuseLayoutEffectçåè°å½æ°destroyæ¯createè¿åçåè°å½æ°depsæ¯useEffectæuseLayoutEffectçä¾èµæ°ç»nextæåä¸ä¸ªeffect对象ï¼1.1.effectç¯ç¶é¾è¡¨å¾functionpushEffect(tag,create,destroy,deps){ consteffect={ tag,create,destroy,deps,next:null};//æ°å建çeffect对象为æå为effecté¾è¡¨çä¸ä¸ªeffect对象ï¼componentUpdateQueue.lastEffectä¼æåæ°å建çeffect对象//æ°å建çeffect对象çnextä¼æå第ä¸ä¸ªeffct对象ï¼letcomponentUpdateQueue=(currentlyRenderingFiber.updateQueue);if(componentUpdateQueue===null){ //å½å没æupdateQueuecomponentUpdateQueue=createFunctionComponentUpdateQueue();//å建updateQueuecurrentlyRenderingFiber.updateQueue=componentUpdateQueue;//å½¢æä¸ä¸ªç¯ç¶é¾è¡¨componentUpdateQueue.lastEffect=effect.next=effect}else{ constlastEffect=componentUpdateQueue.lastEffect;if(lastEffect===null){ componentUpdateQueue.lastEffect=effect.next=effect;}else{ //第ä¸ä¸ªeffect对象为æå å建ççeffect对象constfirstEffect=lastEffect.next;//è·å第ä¸ä¸ªeffect对象lastEffect.next=effect;//æ§çæåä¸ä¸ªeffect对象çnextï¼æåæ°å建çeffecteffect.next=firstEffect;//æ°å建çeffect对象çnextæå第ä¸ä¸ªeffectcomponentUpdateQueue.lastEffect=effect;//updateQueueçlastEffectæåeffectï¼æ°å建çeffectå为æåä¸ä¸ªeffect对象}}returneffect;}2.updateEffect页é¢æ´æ°æ¶ï¼æ§è¡updateEffect;
æ ¹æ®hookååé¾è¡¨è·å对åºçæ´æ°æ¶çhook对象ï¼å建æ°çhook对象ï¼å å ¥hookååé¾è¡¨ï¼
å¦æeffectçdepsä¸ä¸ºnullï¼æè undefinedï¼ä¼ä»å½åhook对象æ¿å°ä¸ä¸æ¬¡effect对象ï¼åä»effect对象æ¿å°depsådestroyï¼ç¨æ°çdepsä¸ä¹æ¯è¾ï¼
å¦ææ°èdepsç¸çï¼pushä¸ä¸ªä¸å¸¦HookHasEffectçtagç»effect对象ï¼å å ¥updateQueueç¯ç¶é¾è¡¨ï¼è¿ä¸ªeffectä¸ä¼è¢«æ 记为æå¯ä½ç¨ï¼æ以ï¼effectçcreateådestroyä¸ä¼è¢«æ§è¡ï¼ï¼ä¸æ´æ°hook.memoizedStateï¼
å¦ææ°èdepsä¸ç¸çï¼æ´æ°effect对象ï¼å¨effectçtagä¸å å ¥HookHasEffectåä¸ä¸æ¬¡createæ§è¡çdestroyï¼æ´æ°hook.memoizedState;
3.useEffctçåè°å½æ°åéæ¯å½æ°çæ§è¡æ¶æºå¨renderæ¶ææ建effecté¾è¡¨ï¼å¨commitæ¶æ§è¡å æ§è¡ä¹å没ææ§è¡å®çuseEffectï¼ç¶åï¼å¨beforeMutationé¶æ®µæä½domåï¼ä»¥NormalPriority常è§ä¼å 级添å ä¸ä¸ªå¼æ¥ä»»å¡å°ä»»å¡éåï¼è¿ä¸ªå¼æ¥ä»»å¡æ¯ç¨æ¥æ§è¡useEffectçdestroyåcreateçï¼ï¼å¨layouté¶æ®µå®æï¼é¡µé¢å®æ渲æåï¼æ§è¡å¨beforeMutationé¶æ®µæ·»å çå¼æ¥ä»»å¡ï¼
3.1.commitå¼å§æ¶ä¸»è¦æ¯ä¸ºäºæ§è¡ä¹å没ææ§è¡çuseEffect
è¿å ¥commité¶æ®µï¼è¿åuseEffectå¼æ¥è°åº¦çç¹ç¹æå ³ï¼å®ä»¥ä¸è¬çä¼å 级被è°åº¦ï¼æå³çä¸æ¦ææ´é«ä¼å 级çä»»å¡è¿å ¥å°commité¶æ®µï¼ä¸ä¸æ¬¡ä»»å¡çuseEffectè¿æ²¡å¾å°æ§è¡ãæ以å¨æ¬æ¬¡æ´æ°å¼å§åï¼éè¦å å°ä¹åçuseEffecté½æ§è¡æï¼ä»¥ä¿è¯æ¬æ¬¡è°åº¦çuseEffecté½æ¯æ¬æ¬¡æ´æ°äº§ççã
functioncommitRootImpl(root,recoverableErrors,renderPriorityLevel){ do{ //`flushPassiveEffects`willcall`flushSyncUpdateQueue`attheend,which//means`flushPassiveEffects`willsometimesresultinadditional//passiveeffects.Soweneedtokeepflushinginaloopuntilthereare//nomorependingeffects.//TODO:Mightbebetterif`flushPassiveEffects`didnotautomatically//flushsynchronousworkattheend,toavoidfactoringhazardslikethis.flushPassiveEffects();}while(rootWithPendingPassiveEffects!==null);...çç¥ä»£ç }3.2.beforeMutationåªä¼åèµ·ä¸æ¬¡useEffectè°åº¦ï¼æ¯å¼æ¥è°åº¦ï¼ä»¥NormalPriority常è§ä¼å 级添å ä¸ä¸ªå¼æ¥ä»»å¡å¨ä»»å¡éåä¸ï¼push(timerQueue,newTask)ï¼ï¼å¨é¡µé¢æ¸²æå®ææ¶ï¼ä¼æ§è¡è¿ä¸ªå¼æ¥ä»»å¡
functioncommitRootImpl(root,recoverableErrors,renderPriorityLevel){ ...çç¥ä»£ç if((finishedWork.subtreeFlags&PassiveMask)!==NoFlags||(finishedWork.flags&PassiveMask)!==NoFlags){ if(!rootDoesHavePassiveEffects){ rootDoesHavePassiveEffects=true;scheduleCallback$1(NormalPriority,function(){ //æ·»å ä¸ä¸ªå¼æ¥ä»»å¡å°ä»»å¡éåflushPassiveEffects();//Thisrendertriggeredpassiveeffects:releasetherootcachepool//*after*passiveeffectsfiretoavoidfreeingacachepoolthatmay//bereferencedbyanodeinthetree(HostRoot,Cacheboundaryetc)returnnull;});}}...çç¥ä»£ç }3.3.layoutå è½½æ¶ï¼åªæ§è¡useEffectçcreateå½æ°å³å¯ï¼
å¦æpendingPassiveEffectsLanesæ¯åæ¥èµéï¼å°±å¨é¡µé¢æ¸²æå®ç´æ¥æ§è¡useEffectçcreateådestroyï¼å¨beforeMutationæ¶æ·»å çå¼æ¥ä»»å¡ï¼ä¸ä¼æ§è¡useEffectçcreateådestory
if(includesSomeLane(pendingPassiveEffectsLanes,SyncLane)&&root.tag!==LegacyRoot){ //å è½½æé´é»è®¤æ¯ä¸èµ°è¿éç//è¿éä¹æ¯æ§è¡useEffectçcreateï¼å¦æpendingPassiveEffectsLanesæ¯åæ¥èµéï¼//å°±å¨æ¸²æå®æåç´æ¥æ§è¡useEffectçcreateådestory//å¨beforeMutationæ¶æ·»å çå¼æ¥ä»»å¡æ§è¡æ¶ï¼ä¸ä¼æ§è¡useEffectçcreateådestoryflushPassiveEffects();}æ§è¡ä¸ä¸æ¬¡useEffectçcreateè¿åçdestroyï¼æ¿å°å½æ°ç»ä»¶fiberçupdateQueueï¼å¾ªç¯è¿ä¸ªeffectç¯ç¶é¾è¡¨ï¼æ¿å°effect对象çdestroyæ§è¡ï¼
functioncommitHookEffectListUnmount(flags,finishedWork,nearestMountedAncestor){ varupdateQueue=finishedWork.updateQueue;varlastEffect=updateQueue!==null?updateQueue.lastEffect:null;if(lastEffect!==null){ varfirstEffect=lastEffect.next;vareffect=firstEffect;do{ if((effect.tag&flags)===flags){ //Unmountvardestroy=effect.destroy;effect.destroy=undefined;if(destroy!==undefined){ { if((flags&Passive$1)!==NoFlags$1){ markComponentPassiveEffectUnmountStarted(finishedWork);}elseif((flags&Layout)!==NoFlags$1){ markComponentLayoutEffectUnmountStarted(finishedWork);}}safelyCallDestroy(finishedWork,nearestMountedAncestor,destroy);//æ§è¡destroy{ if((flags&Passive$1)!==NoFlags$1){ markComponentPassiveEffectUnmountStopped();}elseif((flags&Layout)!==NoFlags$1){ markComponentLayoutEffectUnmountStopped();}}}}effect=effect.next;}while(effect!==firstEffect);}}æ§è¡å®ææç»ä»¶çdestroyï¼åæ§è¡createï¼åçï¼ä¹æ¯æ¿å°å½æ°ç»ä»¶fiberçupdateQueueï¼å¾ªç¯è¿ä¸ªeffectç¯ç¶é¾è¡¨ï¼æ¿å°effect对象çcreateæ§è¡ï¼ç¶åæcreateè¿åçdestroyç»effect对象ï¼ççä¸çæ´æ°æ§è¡useEffectæ¶ç¨ï¼ï¼
functioncommitHookEffectListMount(flags,finishedWork){ varupdateQueue=finishedWork.updateQueue;varlastEffect=updateQueue!==null?updateQueue.lastEffect:null;if(lastEffect!==null){ varfirstEffect=lastEffect.next;vareffect=firstEffect;do{ if((effect.tag&flags)===flags){ { if((flags&Passive$1)!==NoFlags$1){ markComponentPassiveEffectMountStarted(finishedWork);}elseif((flags&Layout)!==NoFlags$1){ markComponentLayoutEffectMountStarted(finishedWork);}}//Mountvarcreate=effect.create;effect.destroy=create();{ if((flags&Passive$1)!==NoFlags$1){ markComponentPassiveEffectMountStopped();}elseif((flags&Layout)!==NoFlags$1){ markComponentLayoutEffectMountStopped();}}{ vardestroy=effect.destroy;if(destroy!==undefined&&typeofdestroy!=='function'){ varhookName=void0;if((effect.tag&Layout)!==NoFlags){ hookName='useLayoutEffect';}elseif((effect.tag&Insertion)!==NoFlags){ hookName='useInsertionEffect';}else{ hookName='useEffect';}varaddendum=void0;if(destroy===null){ addendum='Youreturnednull.Ifyoureffectdoesnotrequireclean'+'up,returnundefined(ornothing).';}elseif(typeofdestroy.then==='function'){ addendum='\n\nItlookslikeyouwrote'+hookName+'(async()=>...)orreturnedaPromise.'+'Instead,writetheasyncfunctioninsideyoureffect'+'andcallitimmediately:\n\n'+hookName+'(()=>{ \n'+'asyncfunctionfetchData(){ \n'+'//Youcanawaithere\n'+'constresponse=awaitMyAPI.getData(someId);\n'+'//...\n'+'}\n'+'fetchData();\n'+"},[someId]);//Or[]ifeffectdoesn'tneedpropsorstate\n\n"+'LearnmoreaboutdatafetchingwithHooks:/post/