Android实现炫酷跳动的闪屏LOGO

news2025/7/19 9:07:29

前言:

在日常开发中,经常会遇到各种视觉效果,有的效果可能一眼看去会让人觉得很复杂,但是我们必须明确一点:

所有复杂动效都是可以分解成单一的基础动作,比如缩放,平移,旋转这些基础单元,然后将所有基础单元动作进行组合,就会产生让人眼前一亮的视觉动效。

图效果:

思路分析:

1、Logo的名称LitePlayer被拆分为单个文字;

2、所有文字随机打散在屏幕各个位置;

3、中间的Logo被隐藏;

4、Logo文字从随机位置平移到页面固定位置;

5、中间的Logo图片逐渐显示,并且附带从下往上平移一小段位移;

6、Logo被打散的文字组合成名称;

7、Logo组合成名称后,有个渐变的光晕照射效果从左往右移动;

8、动画结束;

当我们把动画拆解后,就可以针对每个拆解单元去构造实现方案了。

代码实现:

首先我们先对logo文字动画进行实现:

1、首先对于数据来源,我们期望传入一个logo的字符串,内部将字符串拆解为单个文字数组:

// fill the text to array
private void fillLogoTextArray(String logoName) {
    if (TextUtils.isEmpty(logoName)) {
        return;
    }
    if (mLogoTexts.size() > 0) {
        mLogoTexts.clear();
    }
    for (int i = 0; i < logoName.length(); i++) {
        char c = logoName.charAt(i);
        mLogoTexts.put(i, String.valueOf(c));
    }
}

2、所有文字需要随机打散在屏幕各个位置,因为涉及到坐标,我们可以在onSizeChanged中进行logo文字随机位置的初始化,同时我们构建两个集合存储每个文字被打散和组合后的坐标状态:

    // 最终合成logo后的坐标
    private SparseArray<PointF> mQuietPoints = new SparseArray<>();
    // logo被随机打散的坐标
    private SparseArray<PointF> mRadonPoints = new SparseArray<>();

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        initLogoCoordinate();
    }

    private void initLogoCoordinate() {
        float centerY = mHeight / 2f + mPaint.getTextSize() / 2 + mLogoOffset;
        // calculate the final xy of the text
        float totalLength = 0;
        for (int i = 0; i < mLogoTexts.size(); i++) {
            String str = mLogoTexts.get(i);
            float currentLength = mPaint.measureText(str);
            if (i != mLogoTexts.size() - 1) {
                totalLength += currentLength + mTextPadding;
            } else {
                totalLength += currentLength;
            }
        }
        // the draw width of the logo must small than the width of this AnimLogoView
        if (totalLength > mWidth) {
            throw new IllegalStateException("This view can not display all text of logoName, please change text size.");
        }
        float startX = (mWidth - totalLength) / 2;
        if (mQuietPoints.size() > 0) {
            mQuietPoints.clear();
        }
        for (int i = 0; i < mLogoTexts.size(); i++) {
            String str = mLogoTexts.get(i);
            float currentLength = mPaint.measureText(str);
            mQuietPoints.put(i, new PointF(startX, centerY));
            startX += currentLength + mTextPadding;
        }
        // generate random start xy of the text
        if (mRadonPoints.size() > 0) {
            mRadonPoints.clear();
        }
        // 构建随机初始坐标
        for (int i = 0; i < mLogoTexts.size(); i++) {
            mRadonPoints.put(i, new PointF((float) Math.random() * mWidth, (float) Math.random() * mHeight));
        }
    }

3、构建动画过程,定义一个属性动画从0-1计算进度,在动画过程通过重绘实现文字从凌乱打散的坐标到最终组合坐标进行移动:

// init the translation animation
private void initOffsetAnimation() {
    mOffsetAnimator = ValueAnimator.ofFloat(0, 1);
    mOffsetAnimator.setDuration(mOffsetDuration);
    mOffsetAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
    mOffsetAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            if (mQuietPoints.size() <= 0 || mRadonPoints.size() <= 0) {
                return;
            }
            mOffsetAnimProgress = (float) animation.getAnimatedValue();
            invalidate();
        }
    });
}

@Override
protected void onDraw(Canvas canvas) {
    if (!isOffsetAnimEnd) {// offset animation
        mPaint.setAlpha((int) Math.min(255, 255 * mOffsetAnimProgress + 100));
        for (int i = 0; i < mQuietPoints.size(); i++) {
            PointF quietP = mQuietPoints.get(i);
            PointF radonP = mRadonPoints.get(i);
            float x = radonP.x + (quietP.x - radonP.x) * mOffsetAnimProgress;
            float y = radonP.y + (quietP.y - radonP.y) * mOffsetAnimProgress;
            canvas.drawText(mLogoTexts.get(i), x, y, mPaint);
        }
    }
}

4、此时我们已经把logo文字动画实现了,接下来看我们拆解的第7步,还有个光照效果。对于这种光照效果,首选方案是通过Gradient+Shader实现。

因为绘制渐变也涉及到坐标,所以动画的初始化我们也放到了onSizeChanged中进行:

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mWidth = w;
    mHeight = h;
    initLogoCoordinate();// 初始化坐标动画
    initGradientAnimation(w);// 初始化渐变动画
}

// init the gradient animation
private void initGradientAnimation(int width) {
    mGradientAnimator = ValueAnimator.ofInt(0, 2 * width);
    if (mGradientListener != null) {
        mGradientAnimator.addListener(mGradientListener);
    }
    mGradientAnimator.setDuration(mGradientDuration);
    mGradientAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mMatrixTranslate = (int) animation.getAnimatedValue();
            invalidate();
        }
    });
    mLinearGradient = new LinearGradient(-width, 0, 0, 0, new int[]{mTextColor, mGradientColor, mTextColor},
            new float[]{0, 0.5f, 1}, Shader.TileMode.CLAMP);
    mGradientMatrix = new Matrix();
}

5、渐变动画是在文字移动动画结束后自动播放的,所以我们可以在初始化文字移动动画时对动画结束进行监听处理,同时在绘制onDraw中对文字进行绘制:

// init the translation animation
private void initOffsetAnimation() {
    ...
    // 初始化移动动画
    ...
    mOffsetAnimator.addListener(new AnimatorListenerAdapter() {

        @Override
        public void onAnimationEnd(Animator animation) {
            if (mGradientAnimator != null && isShowGradient) {
                isOffsetAnimEnd = true;
                mPaint.setShader(mLinearGradient);
                mGradientAnimator.start();
            }
        }
    });
}

@Override
protected void onDraw(Canvas canvas) {
    if (!isOffsetAnimEnd) {// offset animation
        ...
        // 文字移动动画
        ...
    } else {// gradient animation
        for (int i = 0; i < mQuietPoints.size(); i++) {
            PointF quietP = mQuietPoints.get(i);
            canvas.drawText(mLogoTexts.get(i), quietP.x, quietP.y, mPaint);
        }
        mGradientMatrix.setTranslate(mMatrixTranslate, 0);
        mLinearGradient.setLocalMatrix(mGradientMatrix);
    }
}

6、到此,文字动画已经实现了。剩下来就是一些自定义属性的定义,对外提供一些属性的setter和getter方法了,同时需要考虑在页面生命周期过程中动画的资源释放。

好了,看下我们实现的效果:

7、对于上面Logo图片的动画可以单独对一个ImageView进行平移+透明度动画实现,这里就不花篇幅去描述了。

自定义View我相信大部分同学都已经掌握熟练,但是对于复杂动画,是否能够将这些熟练的能力用在刀刃上呢,也许会有部分同学看到一个华丽的效果就不知所措了。

本文没有对动画进行深入的分析,也没涉及到复杂的数据运算,只是通过一个简单的例子,阐述了一种通用的动效分析实现的方式,通过这种思维方式,你可以很清晰的了解自己每一步的实现以及目标。

最后总结一下,对于自定义动效而言,我们首先可以让UI提供最终视觉效果,通过工具进行单帧解析,观察其中的每一帧之间的动作关系,将其拆解为一个个基础单元。

接着针对每个单元步骤进行实现,最后整合到一起,就能够实现一个连贯的效果了。这是一种思想,当你熟练掌握这种思想后,还需要对一些数学知识有一定的了解,比如三角函数,矩阵运算等等。只要培养好这两方面能力,日常开发中,任何复杂的动效都不足以为惧。

源码地址:

https://github.com/seagazer/animlogoview

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/368967.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

最新BlackArch发布,提供1400款渗透测试工具

近日&#xff0c;BlackArch Linux新版本发布&#xff0c;此版本为白帽子和安全研究人员提供了大约1400款渗透测试工具&#xff0c;如果你是一位白帽子或者安全研究人员&#xff0c;这个消息无疑会让你很感兴趣。BlackArch Linux是一款基于Arch Linux的发行版&#xff0c;主要面…

luckysheet的使用——07.二次开发自动插入批注功能

在单元格编辑完成后&#xff0c;需要自动在这个单元格上新增批注&#xff0c;此时需要改造旧代码&#xff0c;首先找到路径为 src/controllers/postil.js的文件&#xff0c;找到新增批注时触发的方法&#xff0c;如下&#xff1a; 2.对方法进行改造&#xff0c;新增传入变量co…

深入探讨下,IPC产品与智能家居融合的无限开创性

IPC还有哪些新玩法&#xff1f;随着摄像头的应用场景增加&#xff0c;IPC作为一种能力&#xff0c;正在融入到越来越多的智能设备中&#xff0c;形成了一批富有创意的智能 IPC 融合类产品。 比如&#xff0c;扫地机结合智能 IPC 后&#xff0c;能实现可视化精准识别障碍物&…

C++实现日期类

文章目录前言1.日期类的功能分析1.大致分析2.接口设计2.具体实现1.日期类的成员函数和成员变量2.初始化(构造函数&#xff09;3.对日期进行天数推算4.比较相关的运算符重载5.前置后置自增或自减6.日期相减与流插入流提取1.日期相减2.重载流插入和流提取3.总结前言 之前介绍了C…

数据结构与算法—链表list

目录 链表 链表类型 链表插入 链表删除 写程序注意点 与数组区别 链表应用 LRU 实现思想 链表 链表&#xff0c;一种提高数据读取性能的技术&#xff0c;在硬件设计、软件开发中有广泛应用。常见CPU缓存&#xff0c;数据库缓存&#xff0c;浏览器缓存等。缓存满时&#…

mongoDB5以上实现单机事务

原理就是因为目前mongodb只有副本&#xff0c;分片支持事务。我们就让单机变成单节点副本&#xff0c;同时又是主节点&#xff0c;可以读写一、修改bin目录下的mongod.cfg新增配置replication:replSetName: rs0二、重启mongodb服务、初始化&#xff08;这是windows下的启动命令…

2月24日作业

题目&#xff1a;通过操作Cortex-A7核&#xff0c;串口输入相应的命令&#xff0c;控制LED灯进行工作--->上传CSDN 1.例如在串口输入led1on,开饭led1灯点亮 2.例如在串口输入led1off,开饭led1灯熄灭 3.例如在串口输入led2on,开饭led2灯点亮 4.例如在串口输入led2off,开饭led…

day20_Map

今日内容 上课同步视频:CuteN饕餮的个人空间_哔哩哔哩_bilibili 同步笔记沐沐霸的博客_CSDN博客-Java2301 零、 复习昨日 一、作业 二、比较器排序 三、Collections 四、Map 五、HashMap 六、TreeMap 零、 复习昨日 HashSet 不允许重复元素,无序 HashSet去重原理: 先比较hashco…

Java知识复习(二)Java集合

1、List、Set和Map的区别 List&#xff1a;存储的顺序是有序的、可重复的Set&#xff1a;存储的顺序是无序的、不可重复的Map&#xff1a;使用键值对存储&#xff0c;Key和Value都是无序的&#xff0c;其中Key不可重复&#xff0c;而Value可重复 2、ArrayList和LinkedList的区…

node报错

记录bug:运行 npx -p storybook/cli sb init 时报错gyp info spawn C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exegyp info spawn args [gyp info spawn args build/binding.sln,gyp info spawn args /nologo,gyp info spawn args…

OpenCV只含基本图像模块编译

编译OpenCV4.5.5只含基本图像模块&#xff0c;环境为Windows10 x64CMake3.23.3VS2019。默认编译选项编译得到的OpenCV库往往大几百MB甚至上GB&#xff0c;本文配置下编译得到的库压缩后得到的zip包大小仅6.25MB&#xff0c;适合使用OpenCV基本图像功能模块的项目移植而不牵涉其…

电子技术——伯德图与反馈

电子技术——伯德图与反馈 增益和相位边距 从上两节我们知道环路增益 AβA\betaAβ 可以决定一个系统的稳定性&#xff0c;一个更加简单和有效的方法是我们可以绘制 AβA\betaAβ 的伯德图&#xff08;因为相位可以达到360度&#xff0c;因此这是一个四阶响应系统&#xff09;…

行锁、表锁、主键外键、表之间的关联关系

Java知识点总结&#xff1a;想看的可以从这里进入 目录2.4、行锁、表锁2.5、主键、外键2.5.1、主键2.5.2、外键2.6、表的关联关系2.4、行锁、表锁 MyISAM默认采用表级锁&#xff0c;InnoDB默认采用行级锁。 表锁&#xff1a;开销小&#xff0c;加锁快&#xff0c;不会出现死锁…

KTV「消亡史」:辉煌、挫折与新生

【潮汐商业评论/原创】这是Ina工作的第五年&#xff0c;疫情之后&#xff0c;第一场大学同学聚会就定在了周末。同学群里大家热烈地讨论着聚会的地点&#xff0c;“要不咱们去KTV吧&#xff0c;哈哈哈哈哈哈”&#xff0c;突然有人提议到。“谁还去KTV啊”“多没意思啊”……随…

按字典序排列的最小的等价字符串[拆解并查集]

并查集前言一、按字典序排列的最小的等价字符串二、并查集总结参考文献前言 并查集有什么用&#xff1f;并查集是什么&#xff1f;搞懂这两个问题&#xff0c;相关的并查集问题就变得非常easy&#xff01; 一、按字典序排列的最小的等价字符串 二、并查集 有一种方法&#x…

工单模型的理解与应用

工单&#xff08;任务单&#xff09;模型的定义 工单模型是一种分派任务的方法&#xff0c;可以用来跟踪、评估和报告任务的完成情况。它通常用于针对特定目标的重复性任务或项目&#xff0c;以确保任务能够按时完成并符合期望的标准。   工单模型的基本流程为&#xff1a;提…

GroupDocs.Merger for Java

GroupDocs.Merger for Java GroupDocs.Merger for Java是一个文档操作API&#xff0c;可帮助您合并、拆分、交换或删除文档页面。API通过启用或禁用密码提供保护&#xff0c;并允许开发人员加入PDF、Microsoft Word、Excel和Powerpoint文档。 支持的文件格式 Microsoft Office格…

常见激活函数Activation Function的选择

Activation Function激活函数一般会神经网络中隐层和输出层上&#xff0c;其中作用在输出层主要用于适配输出&#xff0c;比如sigmoid函数可用于生成[0,1]之间的概率估计值。而作用于隐层主要用于增加神经网络的非线性&#xff0c;增加了网络的表达能力&#xff0c;本文主要介绍…

测试员拿到新项目怎么着手测试?不要慌,照做准没错

一、目标 结合公司现有的项目情况制定合理规范的测试流程&#xff0c;提高测试效率和产品质量&#xff0c;尽可能减少客户对产品的问题反馈&#xff0c; 核心还是要加强项目组成员之间的工作交流和沟通&#xff0c;保证整个项目的高效率的按质按量的交付。 二、测试流程说明…

【git】git介绍与安装

Git是什么 Git是目前世界上最先进的分布式版本控制系统 git是由linux的创始人用c语言写的 和集中式比较 历史记录&#xff1a;Git 更加轻量级&#xff0c;每次提交只记录变化&#xff0c;而 SVN 每次提交会存储完整的文件&#xff1b; 版本管理&#xff1a;Git 更加灵活&…