关于loading:AssetBundleUnloadtrue无法卸载图集

1)AssetBundle.Unload(true)无奈卸载图集2)对于Unity 2D游戏地图预加载的问题3)Addressables是否反对某些资源不打Bundle间接加载源文件 这是第342篇UWA技术常识分享的推送,精选了UWA社区的热门话题,涵盖了UWA问答、社区帖子等技术知识点,助力大家更全面地把握和学习。 UWA社区主页:community.uwa4d.comUWA QQ群:465082844 LoadingQ:如图,AssetBundle.Unload(true)中依然有一份图集残留在内存中,应用Resources.UnloadUnusedAssets()能够卸载,但用GC.Collect无奈卸载,若不调用Resources.UnloadUnusedAssets(),有其余的方法能够卸载这个图集吗? A1:目前Unity 3D中的Atlas不须要额定的卸载操作,只须要确保Atlas的AssetBundle和援用Atlas的AssetBundle齐全卸载后,Atlas天然会卸载。感激沐风@UWA问答社区提供了答复 A2:能够试试Resources.UnloadAsset,印象中切换场景会主动调用UnloadUnusedAssets。感激Knight-132872@UWA问答社区提供了答复 LoadingQ:请问,2D游戏地图如何实现地图的无缝预加载性能?须要具体学习什么能力写进去相干的代码呢? A1:假如上一个关卡为x-1,本关卡为x,下一关卡为x+1,依据须要提前预加载x+-y的关卡数量就行了,以自身关卡ID去计算需加载的范畴,每次切换关卡依据关卡从新计算范畴。感激曹潘赣@UWA问答社区提供了答复 A2:用一个工具阐明一下思路LDTK。 导出的数据是以关卡为单位的。进入指标关卡时,要队列加载相邻的关卡,如果加载过程中卡顿体验不好则要预测下一个指标关卡,并在加载时用其它视觉效果覆盖加载卡顿。 还有一种是把所有关卡都加载完,关卡转换基本上是无感的,这种要思考解决内存和效率问题。 具体代码实现就是应用工具提供的API,有编程根底就没有问题。 如果是Unity,则波及到资源加载、场景等概念。 感激李伟@UWA问答社区提供了答复 AssetBundleQ:请问Addressables是否反对某些资源不打Bundle间接加载源文件,比方mp4、bytes文件等? A:Addressables也是基于Bundle的,像播放mp4这样的文件如果在Bundle中则可能播放出错,能够在资源更新时把这样的文件读出来,放到设施的用户目录里,应用时就能够间接用文件或门路了。 bytes这样的文件能够放到Bundle里,某个版本的TextAsset里增加了新办法 ,这个办法能够用一下: public NativeArray<T> GetData<T>() where T : struct;对于大量数据表只须要用一个ByteBuffer来解决。如果存储的数据能够分块读取,那内存也不会占用很多。 感激李伟@UWA问答社区提供了答复 封面图来源于网络 明天的分享就到这里。生有涯而知无涯,在漫漫的开发周期中,咱们遇到的问题只是冰山一角,UWA社区愿伴你同行,一起摸索分享。欢送更多的开发者退出UWA社区。 UWA官网:www.uwa4d.comUWA社区:community.uwa4d.comUWA学堂:edu.uwa4d.com官网技术QQ群:465082844

July 10, 2023 · 1 min · jiezi

关于loading:关于切换场景加载耗时的优化问题

1)对于切换场景加载耗时的优化问题2)SpriteAtlas打包AssetBundle后运行时内存大小和什么有关系3)手机上应用MRT的限度和兼容性4)如何管制粒子系统组件数量的下限 这是第317篇UWA技术常识分享的推送。明天咱们持续为大家精选了若干和开发、优化相干的问题,倡议浏览工夫10分钟,认真读完必有播种。 UWA 问答社区:answer.uwa4d.comUWA QQ群2:793972859(原群已满员) LoadingQ:在切场景时,为什么调整Application.backgroundLoadingPriority为High能够缩小加载耗时呢? A:Application.backgroundLoadingPriority这个API会限度主线程的集成的工夫,默认设置是ThreadPriority.BelowNormal,也就是每一帧执行主线程集成的操作耗时不能超过4毫秒,这将会导致每一帧残余的工夫用于期待或者渲染,而这些期待和渲染的工夫都是无意义的耗时。如果把这个设置改为ThreadPriority.High,那么每一帧执行主线程集成的操作耗时能够减少到50毫秒,大大减少了所须要的帧数。 这里主线程的集成工夫体现在函数Application.Integrate Assets in Background下面,它会受到加载Texture、Mesh的Texture.AwakeFromLoad、Mesh.AwakeFromLoad的影响,另外对于比较复杂的Prefab(父子节点多,层数多)须要进行拼装,耗时也会体现在这个函数当中。 感激龙粲@UWA问答社区提供了答复,欢送大家转至社区交换 AssetBundleQ:SpriteAtlas打包AssetBundle后运行时内存大小和什么有关系?明明只有2张Atlas,2个纹理只有6MB,最终AssetBundle的内存却达到11.7MB。 A:运行时AssetBundle大小会受到打包Sprite数量的影响,打包了1700个Sprite的状况下AssetBundle内存11.7MB。 测试了同样大小的SpriteAtlas,打包了6个1024*1024大小的Sprite,AssetBundle大小却只有4.9KB。 感激宗卉轩@UWA问答社区提供了答复 RenderingQ:手机上应用MRT有什么限度吗,兼容性如何? A:在反对OpenGL ES 3.0的手机上就反对MRT,可参考:https://www.informit.com/arti... https://registry.khronos.org/... 另外实测过,在红米4X这样十分低端的手机上,MRT也是能够失常运行的。红米4X的GPU是Adreno 505。 感激Xuan@UWA问答社区提供了答复 RenderingQ:我的项目Overdraw有点高,咱们想做一个与MMO游戏里同屏角色数量下限相似的,粒子系统下限的性能。 也就是低端分档下,当粒子数量达到某个值当前,就不再往画面里画新的粒子了。但只对特效Prefab数量做限度还是比拟含糊,毕竟不同特效的粒子数量不同,所以想准确到播放状态的粒子组件数量。 A1:用Find必定很耗时,尽量用逻辑做。比方,粒子系统个别都做缓存池治理,能够间接把相干逻辑写在这里:做一个计数器C,某个特效E要实例化或出池时,同时传入它的子粒子数量A(提前配置好),加给C;入池再减掉A;对C作监控看是否在某个值内,否则进行所有实例化和出池操作。同理,给每个特效E挂一个触发器脚本,激活或暗藏的时候把子粒子数量A传给写在某处逻辑的计数器C即可。 另外理论做的时候,还能够给不同的粒子特效加上不同的计算影响系数,这样能够让某些特效(比方多人游戏中玩家角色的技能)呈现的概率更高一些,而其余特效(其余玩家的技能)显示得少一些。 感激Faust@UWA问答社区提供了答复 A2:提个思路,对于每一个特效Prefab,能够离线给这个Prefab做一个评分。对于场上存在的特效Prefab,记录总的特效评分,能够设置一个总的下限评分,超过就不显示。<br/>这样问题就抛到了离线剖析每个特戏Prefab评分上,评分参考维度能够参考:特效粒子发射器数量、特效overdraw、特效Batches、特效内存等等。感激范世青@UWA问答社区提供了答复 封面图来源于网络 明天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题兴许都只是冰山一角,咱们早已在UWA问答网站上筹备了更多的技术话题等你一起来摸索和分享。欢送酷爱提高的你退出,兴许你的办法恰能解他人的当务之急;而他山之“石”,也能攻你之“玉”。 官网:www.uwa4d.com官网技术博客:blog.uwa4d.com官网问答社区:answer.uwa4d.comUWA学堂:edu.uwa4d.com官网技术QQ群:793972859(原群已满员)

December 15, 2022 · 1 min · jiezi

关于loading:React-loading

需要家喻户晓,利用如果少了loading,交互就显得生硬。 本文分享如何在React中从零到一实现并应用loading。 实现一个loading,应该始终呈现在视口的正中。 同时为了示意加载过程的动态性,须要适当的动画。 以及,胜利和失败的提醒与loading其实实质上是一回事,所以,实现loading的同时,也顺便将另外两个一并实现。 严格意义上,这是toast的实现。 Toast.jsximport l from './toast.module.css'import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'import {faCheck, faSpinner, faTriangleExclamation} from '@fortawesome/pro-solid-svg-icons'import PropTypes from 'prop-types'import {memo} from 'react';function Toast({type, message}) { return ( <div className={l.toast} > <div className={l.container} > { type === 'success' ? <FontAwesomeIcon icon={faCheck} /> : type === 'fail' ? <FontAwesomeIcon icon={faTriangleExclamation} /> : <FontAwesomeIcon icon={faSpinner} className={l.loading} /> } </div> { message ? message : type === 'success' ? '实现' : type === 'fail' ? '失败' : '加载中' } </div> )}Toast.propTypes = { type: PropTypes.oneOf(['loading', 'success', 'fail']), message: PropTypes.string}export default memo(Toast)toast.module.css:root { --toastSize: 136px; --containerSize: 40px;}.toast { width: var(--toastSize); height: var(--toastSize); position: fixed; top: calc(50vh - var(--toastSize)/2); left: calc(50vw - var(--toastSize)/2); background: #4C4C4C; border-radius: 12px; display: flex; flex-direction: column; align-items: center; justify-content: center; color: rgba(255, 255, 255, .9);}.container { width: var(--containerSize); display: inherit; flex-direction: inherit; align-items: inherit; margin: 0 auto 16px;}.container > svg { height: var(--containerSize);}.loading { animation: rotate linear 1s infinite;}@keyframes rotate { from { transform: rotate(0); } to { transform: rotate(360deg); }}应用路由loading的首要利用场景就是页面间的切换。 ...

August 6, 2022 · 2 min · jiezi

Loading图总有一款适合你

总有一款适合你的Loadinggithub地址,喜欢的可以star下哦 插件预览图 使用教程1.插件代码拷贝 下载后把components目录下countUp.vue文件拷贝到自己项目目录下2.插件全局配置 在项目里main.js中配置如下代码import loading from './components/loading/loading.vue'Vue.component('loading',loading)3.插件使用 vue页面使用<template> <view> <loading ></loading> </view></template><script></script><style></style>还收集了一些其他的Loding Loaders.css css-loaders SVG-Loaders 暂时收集了这些,我相信能满足大部分人的需求了 兼容性uni-app项目中使用都兼容

July 5, 2019 · 1 min · jiezi

vue2-http拦截器-实现每次请求地址都显示loading

实现效果图1、这里使用的是element-ui框架显示的loading,当然使用方法大同小异您可以使用其他框架或者自己写样式。 main.js引入element-ui框架 import ElementUI from 'element-ui'Vue.use(ElementUI)2、main.js中配置拦截器 // 开始拦截Vue.http.interceptors.push((request, next) => { if (request.url.length > 0) { // 判断请求地址 let loadingInstance = ElementUI.Loading.service({ target: document.getElementById('loading'), fullscreen: false, text: '玩命加载中...' }) next((response) => { // 请求完成,关闭loading框 // this.$nextTick(() => { // 以服务的方式调用的 Loading 需要异步关闭 loadingInstance.close() // }) return response }) }})配置参数 target Loading 需要覆盖的 DOM 节点。可传入一个 DOM 对象或字符串;若传入字符串,则会将其作为参数传入 document.querySelector以获取到对应 DOM 节点 object/string — document.bodybody 同 v-loading 指令中的 body 修饰符 boolean — falsefullscreen 同 v-loading 指令中的 fullscreen 修饰符 boolean — truelock 同 v-loading 指令中的 lock 修饰符 boolean — falsetext 显示在加载图标下方的加载文案 string — —spinner 自定义加载图标类名 string — —background 遮罩背景色 string — —customClass Loading 的自定义类名 string — —

June 12, 2019 · 1 min · jiezi

Vue自定义全局Toast和Loading

如果我们的Vue项目中没有用到任何UI框架的话,为了更好的用户体验,肯定会用到loading和toast。那么我们就自定义这两个组件吧。1、Toast组件首先,在common下新建global文件夹,存放我们的toast.vue和toast.js两个文件(当然文件的具体位置你可以自行安排)。(1). toast.vue<template lang=“html”> <div v-if=“isShowToast” class=“toast-container” @touchmove.prevent> <!– 这里content为双花括号 –> <span class=“loading-txt”>{content}</span> </div></template><script>export default { data () { return { isShowToast: true, content: ’’ } }}</script><!– Add “scoped” attribute to limit CSS to this component only –><style scoped>.toast-container { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255, 255, 255, 0.1);}.toast-msg { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 60%; padding: 35px; border-radius: 10px; font-size: 28px; line-height: 36px; background: #eee; color: #666;}</style>(2). toast.jsimport Vue from ‘Vue’import ToastComponent from ‘./Toast.vue’const Toast = {}let showToast = false // 存储loading显示状态let toastNode = null // 存储loading节点元素const ToastConstructor = Vue.extend(ToastComponent)Toast.install = function (Vue, options) { // 参数 var opt = { duration: ‘1200’ } for (var property in options) { opt[property] = options[property] } Vue.prototype.$toast = function (tips, type) { if (type === ‘hide’) { toastNode.isShowToast = showToast = false } else { if (showToast) { // 如果toast还在,则不再执行 return } toastNode = new ToastConstructor({ data: { isShowToast: showToast, content: tips } }) toastNode.$mount() // 挂在实例,为了获取下面的toastNode.$el document.body.appendChild(toastNode.$el) toastNode.isShowToast = showToast = true setTimeout(function () { toastNode.isShowToast = showToast = false }, opt.duration) } }; [‘show’, ‘hide’].forEach(function (type) { Vue.prototype.$toast[type] = function (tips) { return Vue.prototype.$toast(tips, type) } })}export default Toast然后,我们需要把写好的组件在/src/main.js中引用一下。import Toast from ‘./components/common/global/toast’Vue.use(Toast)最后,怎么使用呢?只需在要用的地方this.$toast.show(‘hello world’)2、Loading组件loading组件只需要照着toast组件搬过来,稍微改下就可以了。首先,在common下新建global文件夹,存放我们的loading.vue和loading.js两个文件。(1). loading.vue<template lang=“html”> <div v-if=“isShowLoading” class=“loading-container”> <div class=“loading-box”> <img class=“loading-img” :src=“require(’../../../assets/images/loading.png’)"> <!– 这里content为双花括号 –> <span class=“loading-txt”>{content}</span> </div> </div></template><script>export default { data () { return { isShowLoading: false, content: ’’ } }}</script><!– Add “scoped” attribute to limit CSS to this component only –><style scoped>.loading-container { display: flex; justify-content: center; align-items: center; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0); z-index: 1000;}.loading-box { display: flex; flex-direction: column; justify-content: center; align-items: center; width: 150px; height: 150px; border-radius: 10px; background: #e5e5e5;}.loading-img { width: 70px; height: 70px; animation: rotating 2s linear infinite;}@keyframes rotating { 0% { transform: rotate(0deg); } 100% { transform: rotate(1turn); }}.loading-txt { display: flex; justify-content: center; align-items: center; font-size: 24px; color: #666;}</style>(2). loading.jsimport Vue from ‘Vue’import LoadingComponent from ‘./Loading.vue’const Loading = {}let showLoading = false // 存储loading显示状态let loadingNode = null // 存储loading节点元素const LoadingConstructor = Vue.extend(LoadingComponent)Loading.install = function (Vue) { Vue.prototype.$loading = function (tips, type) { if (type === ‘hide’) { loadingNode.isShowLoading = showLoading = false } else { if (showLoading) { // 如果loading还在,则不再执行 return } loadingNode = new LoadingConstructor({ data: { isShowLoading: showLoading, content: tips } }) loadingNode.$mount() // 挂在实例,为了获取下面的loadingNode.$el document.body.appendChild(loadingNode.$el) loadingNode.isShowLoading = showLoading = true } }; [‘show’, ‘hide’].forEach(function (type) { Vue.prototype.$loading[type] = function (tips) { return Vue.prototype.$loading(tips, type) } })}export default Loading然后,在/src/main.js中引用一下loading组件。import Loading from ‘./components/common/global/loading’Vue.use(Loading)最后,只需在要用的地方this.$loading.show(‘hello world’)、 this.$loading.hide() ...

April 17, 2019 · 2 min · jiezi

Android 自定义 View 之 LeavesLoading

1.前言前天的浏览 GitHub 时发现一个模仿 Gif 的 Loading 特效的项目,感觉效果很不错,也比较有创意,如下:GitHub 上好几个做这个效果的项目,但是很少有完全实现的,有的还有 Bug,于是花了 2 天实现了一下。效果如下:GitHub 项目在这里 LeavesLoading2. 分析实现要求:叶子随机产生飘动轨迹为正弦函数,并且随机振幅飘动时伴随自旋转,更符合物理规律遇到进度条似乎是融入的风扇可旋转Loading == 100% 时显示一个动画细节风扇和叶子自适应 View 大小叶子在视觉上不能飘出 RountRect 边界3. 核心实现3.1 随机产生叶子本质是事先产生一定数量叶子,这些叶子的漂动时的振幅、相位、旋转方向等等都是随机的,并且飘动是周期性地即叶子飘动到最左边时,又重新回到最右边。Leaf 类: private class Leaf{ float x,y;//坐标 AmplitudeType type;//叶子飘动振幅 int rotateAngle;//旋转角度 RotateDir rotateDir;//旋转方向 long startTime;//起始时间 int n;//初始相位 }Leaf 生成方法: Leaf generateLeaf(){ Leaf leaf = new Leaf(); //随机振幅 int randomType = mRandom.nextInt(3); switch (randomType){ case 0: //小振幅 leaf.type = AmplitudeType.LITTLE; break; case 1: //中等振幅 leaf.type = AmplitudeType.MIDDLE; break; default: //大振幅 leaf.type = AmplitudeType.BIG; break; } //随机旋转方向 int dir = mRandom.nextInt(2); switch (dir){ case 0: //逆时针 leaf.rotateDir = RotateDir.ANTICLOCKWISE; break; default: //顺时针 leaf.rotateDir = RotateDir.CLOCKWISE; break; } //随机起始角度 leaf.rotateAngle = mRandom.nextInt(360); leaf.n = mRandom.nextInt(20); mAddTime += mRandom.nextInt((int)mLeafFloatTime); leaf.startTime = System.currentTimeMillis() + mAddTime; return leaf; }3.2 叶子飘动轨迹为正弦函数确定 Leaf 在某个时刻的坐标 ( x , y ): /** * 获取叶子的(x,y)位置 * @param leaf 叶子 * @param currentTime 当前时间 / private void getLeafLocation(Leaf leaf,long currentTime){ long intervalTime = currentTime - leaf.startTime;//飘动时长 if (intervalTime <= 0){ // 此 Leaf 还没到飘动时间 return; }else if (intervalTime > mLeafFloatTime){ // Leaf 的飘动时间大于指定的飘动时间,即叶子飘动到了最左边,应回到最右边 leaf.startTime = currentTime + new Random().nextInt((int)mLeafFloatTime); } // 计算移动因子 float fraction = (float) intervalTime / mLeafFloatTime; leaf.x = (1-fraction)mProgressLen; leaf.y = getLeafLocationY(leaf); if (leaf.x <= mYellowOvalHeight / 4){ //叶子飘到最左边,有可能会超出 RoundRect 边界,所以提前特殊处理 leaf.startTime = currentTime + new Random().nextInt((int)mLeafFloatTime); leaf.x = mProgressLen; leaf.y = getLeafLocationY(leaf); } }要想让 Leaf 飘动轨迹为正弦函数,关键在于确定 Leaf 的 Y 轴坐标: / * 获取叶子的Y轴坐标 * @param leaf 叶子 * @return 经过计算的叶子Y轴坐标 / private float getLeafLocationY(Leaf leaf){ float w = (float) (Math.PI * 2 / mProgressLen);//角频率 float A;//计算振幅值 switch (leaf.type){ case LITTLE: A = mLeafLen/3; break; case MIDDLE: A = mLeafLen2/3; break; default: A = mLeafLen; break; } // (mHeight-mLeafLen)/2 是为了让 Leaf 的Y轴起始位置居中 return (float) (A * Math.sin(w * leaf.x + leaf.n)+(mHeight-mLeafLen)/2); }3.3 叶子飘动时自旋转这里就涉及到了 Leaf 的绘制,其实 Gif 中的叶子和风扇都可以使用 Canves 直接绘制图案,但是这样就会有两个问题:难画:想要画出满意图形,并且还要旋转、缩放、平移可要下一番功夫。灵活性低:如果想换其他样式又得重新设计绘制过程。因此这里采用 Canves.drawBitmap() 的方式绘制,直接使用已有的图片作为叶子和风扇,同时利用 Canves.drawBitmap() 的一个重载的方法可以很方便的实现旋转、缩放、平移:void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) ;就是通过这里的 Matrix 矩阵,它内部封装了 postScale()、postTranslate 、postRotate() 等方法,可以帮助我们快速的对 Bitmap 进行旋转、缩放、平移还有其他操作。使用时要记得配合 Canves 的 save() 和 restore() 使用,否则达不到想要的效果。对这方面不熟的朋友可以看看 HenCoder 的自定义 View 教学 1-4 。绘制 Leaf 的方法: private void drawLeaves(Canvas canvas){ long currentTime = System.currentTimeMillis(); for (Leaf leaf : mLeafList) { if (currentTime > leaf.startTime && leaf.startTime != 0){ // 获取 leaf 当前的坐标 getLeafLocation(leaf,currentTime); canvas.save(); Matrix matrix = new Matrix(); // 缩放 自适应 View 的大小 float scaleX = (float) mLeafLen / mLeafBitmapWidth; float scaleY = (float) mLeafLen / mLeafBitmapHeight; matrix.postScale(scaleX,scaleY); // 位移 float transX = leaf.x; float transY = leaf.y; matrix.postTranslate(transX,transY); // 旋转 // 计算旋转因子 float rotateFraction = ((currentTime - leaf.startTime) % mLeafRotateTime) /(float)mLeafRotateTime; float rotate; switch (leaf.rotateDir){ case CLOCKWISE: //顺时针 rotate = rotateFraction * 360 + leaf.rotateAngle; break; default: //逆时针 rotate = -rotateFraction * 360 + leaf.rotateAngle; break; } // 旋转中心选择 Leaf 的中心坐标 matrix.postRotate(rotate,transX + mLeafLen / 2,transY + mLeafLen / 2); canvas.drawBitmap(mLeafBitmap,matrix,mBitmapPaint); canvas.restore(); } }3.4 Loading == 100% 出现动画增加一个判断字段 isLoadingCompleted ,在 onDraw() 中选择对应绘制策略。isLoadingCompleted 在 setProgress() 中根据 progress 设置: /** * 设置进度(自动刷新) * @param progress 0-100 */ public void setProgress(int progress){ if (progress < 0){ mProgress = 0; }else if (progress > 100){ mProgress = 100; }else { mProgress = progress; } if (progress == 100){ isLoadingCompleted = true; }else { isLoadingCompleted = false; } // 255 不透明 mCompletedFanPaint.setAlpha(255); postInvalidate(); }LeavesLoading.onDraw() 部分实现: @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); …… if (isLoadingCompleted){ //绘制加载完成特效 drawCompleted(canvas); }else { //绘制扇叶 drawFan(canvas,mFanLen,mBitmapPaint); } //刷新 postInvalidate(); }drawCompleted() 实现: private void drawCompleted(Canvas canvas) { // 每次绘制风扇透明度递减10 int alpha = mCompletedFanPaint.getAlpha() - 10; if (alpha <= 0){ alpha = 0; } mCompletedFanPaint.setAlpha(alpha); // 文字透明度刚好与风扇相反 mCompletedTextPaint.setAlpha(255-alpha); // 计算透明因子 float fraction = alpha / 255f; // 叶片大小 和 文字大小 也是相反变化的 float fanLen = fraction * mFanLen; float textSize = (1 - fraction) * mCompletedTextSize; mCompletedTextPaint.setTextSize(textSize); //测量文字占用空间 Rect bounds = new Rect(); mCompletedTextPaint.getTextBounds( LOADING_COMPLETED, 0, LOADING_COMPLETED.length(), bounds); // 与 drawLeaf() 相似,不再赘述 drawFan(canvas, (int) fanLen, mCompletedFanPaint); //画文字 canvas.drawText( LOADING_COMPLETED, 0, LOADING_COMPLETED.length(), mFanCx-bounds.width()/2f, mFanCy+bounds.height()/2f, mCompletedTextPaint); }流程:计算风扇和文字透明度 -> 计算风扇和文字大小以及文字占用空间 -> 绘制 ,风扇逐渐变透明和变小,文字逐渐变清晰和变大,注释写得比较清楚就不赘述了。4. 结束文章中如有出现任何错误,欢迎大家到评论区留言指正。如果觉得 LeavesLoading 对您有任何帮助,希望可以在 GitHub 得到您的 Star !Thanks:GAStudio 的这篇文章提供的核心代码参考 ...

February 1, 2019 · 3 min · jiezi