Java开发知识点!手把手讲解-一个复杂动效的自定义绘制
诶?心形是怎么绘制的?
诶?波浪是怎么画出来的,又是如何动起来的?
诶? 文字是怎么呈现出同一时刻的两种颜色的?
不知道是不是有人有这样的疑惑````请继续往下看.
#效果拆解
拿到一个复杂特效,第一件事不要慌,先仔细分析一下,这个特效里面具体有哪些细节可以拆分出来。复杂的东西都是由简单的细节 组合而成。
开始拆解。
1、绘制区域是一个心形
2、波浪从最下面开始,逐渐
用绿色填充了整个心形
3、中间有文字内容“一条大灰狼
”,并且在波浪增长的过程中,文字存在一段时间的上下两部分颜色不同
的状态.
#本案例用到的知识点:
1、
canvas.clipPath
画布裁剪
2、canvas.save
画布状态保存
3、canvas.restore
恢复
4、canvas.translate
画布平移
6、path.rCubicTo
构建三阶贝塞尔曲线(相当于上一个点位置)
5、属性动画ValueAnimator
/AnimatorSet
#开始撸码
按照之前拆解的步骤,
###第1
步:构建一个心形区域
当一个复杂图形摆在我们面前,而且还是不规则图形,我们首先应该想到的,就是
android.graphics.Path
类,它可以记录复杂图形的全部点组成的路径。
关键代码:
/*** 构建心形* <p>* 注意,它这个是以 矩形区域中心点为基准的图形,所以绘制的时候,必须先把坐标轴移动到 区域中心*/private void initHeartPath(Path path) {List<PointF> pointList = new ArrayList<>();pointList.add(new PointF(0, Utils.dp2px(-38)));pointList.add(new PointF(Utils.dp2px(50), Utils.dp2px(-103)));pointList.add(new PointF(Utils.dp2px(112), Utils.dp2px(-61)));pointList.add(new PointF(Utils.dp2px(112), Utils.dp2px(-12)));pointList.add(new PointF(Utils.dp2px(112), Utils.dp2px(37)));pointList.add(new PointF(Utils.dp2px(51), Utils.dp2px(90)));pointList.add(new PointF(0, Utils.dp2px(129)));pointList.add(new PointF(Utils.dp2px(-51), Utils.dp2px(90)));pointList.add(new PointF(Utils.dp2px(-112), Utils.dp2px(37)));pointList.add(new PointF(Utils.dp2px(-112), Utils.dp2px(-12)));pointList.add(new PointF(Utils.dp2px(-112), Utils.dp2px(-61)));pointList.add(new PointF(Utils.dp2px(-50), Utils.dp2px(-103)));path.reset();for (int i = 0; i < 4; i++) {if (i == 0) {path.moveTo(pointList.get(i * 3).x, pointList.get(i * 3).y);} else {path.lineTo(pointList.get(i * 3).x, pointList.get(i * 3).y);}int endPointIndex;if (i == 3) {endPointIndex = 0;} else {endPointIndex = i * 3 + 3;}path.cubicTo(pointList.get(i * 3 + 1).x, pointList.get(i * 3 + 1).y,pointList.get(i * 3 + 2).x, pointList.get(i * 3 + 2).y,pointList.get(endPointIndex).x, pointList.get(endPointIndex).y); //你的心形就是用贝塞尔曲线来画的吗}path.close();path.computeBounds(mHeartRect, false);//把path所占据的最小矩形区域,返回出去}
传入一个
Path
引用,然后在方法内部对path
进行各种api
调用改变其属性. 这里需要提及一个重点:最后一行代码path.computeBounds(mHeartRect, false);
意思是,无论什么样的path
,它都会占据一个最小矩形区域,computeBounds
方法可以获取这个矩形区域,设置给入参mHeartRect
.
###第2
步:将心形区域裁剪出来, 裁剪之后,后续的绘制都只会显示在这个区域之内
(为了作图方便,我们通常先把坐标轴原点移动到 绘制区域的正中央)
@Overrideprotected void onDraw(Canvas canvas) {int width = getWidth();int height = getHeight();canvas.translate(width / 2, height / 2);//为了作图方便,我们通常先把坐标轴原点移动到 绘制区域的正中央...省略无关代码canvas.clipPath(mMainPath);//裁剪心形区域canvas.save();//保存画布状态...省略无关代码}
###第3
步:绘制波浪区域
这里有两点细节
1)波浪区域分为两块,top
和bottom
上下两块
2) 整个波浪区域的长度为 心形矩形范围宽度的2
倍
(?为什么是2倍?
因为上面的波浪动画,其实是整个波浪区域平移造成的视觉效果,为了让这个动画可以无限执行,设计两倍宽度,当一半的宽度向右移动刚好触及心形矩形区域的右边框的时候,让它还原到原始位置,这样就能无缝衔接。)
######关键代码1 - 波浪path的构建
:
/*** @param ifTop 是否是上部分; 上下部分的封口位置不一样* @param r 心形的矩形区域* @param process 当前进度值*/private void resetWavePath(boolean ifTop, RectF r, float process, Path path) {final float width = r.width();final float height = r.width();path.reset();if (ifTop) {path.moveTo(r.left - width, r.top);} else {path.moveTo(r.left - width, r.bottom); //下部,初始位置点在 下}float waveHeight = height / 8f;//波动的最大幅度//找到矩形区域的左边线中点path.lineTo(r.left - width, r.bottom - height * process);//做两个周期的贝塞尔曲线for (int i = 0; i < 2; i++) {float px1, py1, px2, py2, px3, py3;px1 = width / 4;py1 = -waveHeight;px2 = width / 4 * 3;py2 = waveHeight;px3 = width;py3 = 0;path.rCubicTo(px1, py1, px2, py2, px3, py3);}if (ifTop) {path.lineTo(r.right, r.top);} else {path.lineTo(r.right, r.bottom);}path.close();}
######关键代码2- 属性动画 改变两个全局变量 波浪的向上增长系数 以及 横向波浪动画系数
:
AnimatorSet animatorSet;// 动起来public void startAnimator() {if (animatorSet == null) {animatorSet = new AnimatorSet();ValueAnimator growAnimator = ValueAnimator.ofFloat(0f, 1f);growAnimator.addUpdateListener(animation -> growProcess = (float) animation.getAnimatedValue());growAnimator.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {animatorSet.cancel();}});growAnimator.setInterpolator(new DecelerateInterpolator());growAnimator.setDuration((long) (4000 / animatorSpeedCoefficient));ValueAnimator waveAnimator = ValueAnimator.ofFloat(0f, 1f);waveAnimator.setRepeatCount(ValueAnimator.INFINITE);waveAnimator.setRepeatMode(ValueAnimator.RESTART);waveAnimator.addUpdateListener(animation -> {waveProcess = (float) animation.getAnimatedValue();invalidate();});waveAnimator.setInterpolator(new LinearInterpolator());waveAnimator.setDuration((long) (1000 / animatorSpeedCoefficient));animatorSet.playTogether(growAnimator, waveAnimator);animatorSet.start();} else {animatorSet.cancel();animatorSet.start();}}
######关键代码3- 利用属性动画改变的全局变量,构建动态效果
@Overrideprotected void onDraw(Canvas canvas) {int width = getWidth();int height = getHeight();canvas.translate(width / 2, height / 2);//为了作图方便,我们通常先把坐标轴原点移动到 绘制区域的正中央curXOffset = waveProcess * mHeartRect.width();//当前X轴方向上 波浪偏移量canvas.clipPath(mMainPath);canvas.save();mainRect = new Rect();... 省略无关代码// 上波浪区域resetWavePath(true, mHeartRect, growProcess, topWavePath);canvas.translate(curXOffset, 0);canvas.clipPath(topWavePath);canvas.drawPath(topWavePath, mTopPaint);... 省略无关代码//下波浪区域resetWavePath(false, mHeartRect, growProcess, bottomWavePath);canvas.restore();canvas.translate(curXOffset, 0);canvas.clipPath(bottomWavePath);canvas.drawPath(bottomWavePath, mBottomPaint);## 总结在清楚了各个大厂的面试重点之后,就能很好的提高你刷题以及面试准备的效率,接下来小编也为大家准备了最新的互联网大厂资料。> **[资料领取:点我即可免费领取](https://gitee.com/vip204888/java-p7)**![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/25a3b885454d37327e5bdb3af8506264.png)![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/8828fee8ce9a85311f9e2567697e51e7.png)![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/bf4e388512366b3d75080d24f4cedd0c.png)以及面试准备的效率,接下来小编也为大家准备了最新的互联网大厂资料。> **[资料领取:点我即可免费领取](https://gitee.com/vip204888/java-p7)**[外链图片转存中...(img-IBI2XtRt-1628134811223)][外链图片转存中...(img-XbeTksXa-1628134811226)][外链图片转存中...(img-GDnHn72b-1628134811227)]![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/5817ced066f0cc9032533d791b056f10.png)
Java开发知识点!手把手讲解-一个复杂动效的自定义绘制相关推荐
- 手把手讲解-一个复杂动效的自定义绘制3,膜拜大佬
#前言 手把手讲解系列文章,是我写给各位看官,也是写给我自己的. 文章可能过分详细,但是这是为了帮助到尽量多的人,毕竟工作5,6年,不能老吸血,也到了回馈开源的时候. 这个系列的文章: 1.用通俗易懂 ...
- button按钮onclick触发不了_手把手教你深入CSS实现一个粒子动效的按钮
按钮(button)可能是网页中最常见的组件之一了,大部分都平淡无奇,如果你碰到的是一个这样的按钮,会不会忍不住多点几次呢? 转载链接: https://github.com/XboxYan/note ...
- 怎么实现hover_web前端CSS实现一个粒子动效的按钮
按钮(button)可能是网页中最常见的组件之一了,大部分都平淡无奇,如果你碰到的是一个这样的按钮,会不会忍不住多点几次呢? 通常这类效果第一反应可能就是借助canvas了,比如下面这个案例点击预览( ...
- 基于JAVA开发使用IDEA兼容Eclipse的动漫视屏网站
基于IDEA兼容Eclipse开发的动漫视屏网站 这是一个适合毕设和课程设计的网站开发 需求分析: 效果图 本地搭建教程 数据库的逆向模型 主要的代码 这是一个适合毕设和课程设计的网站开发 需求分析: ...
- 用eclipse玩转Python,让习惯java开发的童鞋拥有一个更爽的开发体验
#0>>>>>>>预准备工作:(a标签貌似不能用,,只好比较lowbi的直接放地址) IDE eclipse 下载地址: http://ftp.yz.yama ...
- java 开发vr_手把手教你搭建虚拟现实AR/VR开发环境
❗前情提要: 注意!软件安装路径下不能有中文! 软件提取: 1.安装Unity3d 先后安装: UnitySetup64-5.6.2f1 UnityStandardAssetsSetup-5.6.2f ...
- java开发台球的图片_Java模拟桌球打击处理及绘制
Java模拟桌球打击处理及绘制 由于Java可以很轻易的完成比较复杂的数学运算,所以我们经常能看到物理领域的问题借助Java实现效果演示,下面我给出一个桌球碰撞的处理及绘制实例. package or ...
- java开发简单解释器,实现一个简单的解释器(5)
你如何处理和了解像创建解释器或编译器这样复杂的事情?在开始时,一切看上去都像是一团乱七八糟的纱线,你需要解开缠结才能得到完美的球. 到达那里的方法是将它解开一个线,一次解开一个结.不过有时候,你可能会 ...
- Java开发知识点!mysql运行sql文件很慢
MySQL为何不选择平衡二叉树 既然平衡二叉树解决了普通二叉树的问题,那么mysql为何不选择平衡二叉树作为索引呢? 索引需要存储什么 让我们想一想,如果我们要把索引存起来,那么应该存哪些信息呢,它应 ...
最新文章
- SQL基础:常用SQL语句详解(转)
- JZOJ__Day 10:【普及模拟】【USACO】横幅
- MybatisPlus代码生成器配置
- 打开高效文本编辑之门_Linux Sed插入追加转换退出等命令应用
- 把linux制作成内存系统,把内存当硬盘,提速你的linux系统
- hibernate防止sql注入对参数赋值传参数的例子
- 快来加入阿里云大学【云学院】班级助理招募—机会稍纵即逝,错过遥遥无期!...
- vtkSuperquadricSource:创建以原点为中心的多边形超二次曲面
- linux 服务管理
- python项目实践_python实践项目
- Linux创建用户、用户组 及 删除
- Eclipse中与CVS相连的工程中的文件,不显示版本信息时,如何处理(重启Eclipse)!
- 再谈设计模式之-1.单例模式
- Android与物联网设备通信-网络模型分层
- 决胜新能源汽车战场:价格拖死战、舆论声量战、产业兼并战
- 《企业IT架构转型之道》阿里巴巴中台战略思想和架构实战-书摘整理
- 图解机器学习算法(1) | 机器学习基础知识(机器学习通关指南·完结)
- java4.25生成车牌号_泸牌16年涨882倍 超25万人拍一张车牌为哪般?
- 查理芒格推荐的100个思维模型
- 教你10分钟电脑配置挑选装机速成攻略
热门文章
- Tensor for ‘out‘ is on CPU, Tensor for argument #1 ‘self‘ is on CPU
- hk.module must be initialized inside an hk.transform
- MediaSource 非当前窗口
- pytorch nan解决方法笔记
- NoBrokersAvailableError
- 图像的低频是轮廓,高频是噪声和细节 小波变换
- python中文转拼音
- Freetype library not found问题解决
- 基于标记的AR的OpenCV实现
- php如何实现用户报警,php封装实现钉钉机器人报警接口的示例代码