高级UI特效之酷炫水波纹动画

    xiaoxiao2021-03-25  62

    最近研究了一个特效动画: 一个水波纹动画的动态效果。有兴趣的童鞋们可以去了解一下。定义组件大家都非常熟悉。不可以参考如不熟悉童鞋可以去参考:http://blog.csdn.net/androidstarjack/article/details/42567301

    水波纹动画展示效果:

    针对于这样的效果我们可能想起来的绘制方式有:

    第一种方式: 正弦曲线 计算x,y。VauleAnimation 控制x的行走画无数个直线–利用高等数学微积分path.lineTo()方法存储微小的直线最后canvas.drawLine(x1,y1,x2,y2) -第二种方式: 利用贝塞尔曲线 path.quadTo()//二阶贝塞尔曲线path.cubicTo()//三阶贝塞尔曲线canvas.drawPath(path,paint)

    话不多说,先看代码:

    加速后和起风时的效果:

    MainActivity.java代码如下:

    注意这里用到了贝塞尔曲线和Region两个知识点。

    贝塞尔曲线

      贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。贝塞尔曲线是计算机图形学中相当重要的参数曲线,在一些比较成熟的位图软件中也有贝塞尔曲线工具,如PhotoShop等。在Flash4中还没有完整的曲线工具,而在Flash5里面已经提供出贝塞尔曲线工具。

      贝塞尔曲线于1962,由法国工程师皮埃尔·贝塞尔(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。贝塞尔曲线最初由Paul de Casteljau于1959年运用de Casteljau演算法开发,以稳定数值的方法求出贝兹曲线。

    公式

    线性公式

      给定点P0、P1,线性贝兹曲线只是一条两点之间的直线。这条线由下式给出:

    且其等同于线性插值。

    二次方公式

      二次方贝兹曲线的路径由给定点P0、P1、P2的函数B(t)追踪:

    TrueType字型就运用了以贝兹样条组成的二次贝兹曲线。

    三次方公式

      P0、P1、P2、P3四个点在平面或在三维空间中定义了三次方贝兹曲线。曲线起始于P0走向P1,并从P2的方向来到P3。一般不会经过P1或P2;这两个点只是在那里提供方向资讯。P0和P1之间的间距,决定了曲线在转而趋进P3之前,走向P2方向的“长度有多长”。

    曲线的参数形式为:

      现代的成象系统,如PostScript、Asymptote和Metafont,运用了以贝兹样条组成的三次贝兹曲线,用来描绘曲线轮廓。

    一般参数公式

    阶贝兹曲线可如下推断。给定点P0、P1、…、Pn,其贝兹曲线即:

    如上公式可如下递归表达:

    用表示由点P0、P1、…、Pn所决定的贝兹曲线。

    用平常话来说,阶的贝兹曲线,即双阶贝兹曲线之间的插值。

    公式说明

    1.开始于P0并结束于Pn的曲线,即所谓的端点插值法属性。2.曲线是直线的充分必要条件是所有的控制点都位在曲线上。同样的,贝塞尔曲线是直线的充分必要条件是控制点共线。3.曲线的起始点(结束点)相切于贝塞尔多边形的第一节(最后一节)。4.一条曲线可在任意点切割成两条或任意多条子曲线,每一条子曲线仍是贝塞尔曲线。5.一些看似简单的曲线(如圆)无法以贝塞尔曲线精确的描述,或分段成贝塞尔曲线(虽然当每个内部控制点对单位圆上的外部控制点水平或垂直的的距离为时,分成四段的贝兹曲线,可以小于千分之一的最大半径误差近似于圆)。6.位于固定偏移量的曲线(来自给定的贝塞尔曲线),又称作偏移曲线(假平行于原来的曲线,如两条铁轨之间的偏移)无法以贝兹曲线精确的形成(某些琐屑实例除外)。无论如何,现存的启发法通常可为实际用途中给出近似值。

    使用方法

      贝塞尔曲线跟PS里的钢笔的意思大概差不多,不过贝塞尔曲线没有选取的功能。在这里,要切记,不要和轮廓工具弄混,前者是通过调节点调节形状,后者是调节形状轮廓的粗细以及样式。

    补充几点:

    1、在任意工具情况下,在曲线上双击都可以换为形状工具对曲线进行编辑;2、在曲线上用形状工具双击可以增加一个节点;3、在曲线的节点上双击形状工具可以删除一个节点;4、位图可以用形状工具点击再拖动某一点可以进行任意形状的编辑;5、用形状工具同时选中几个节点可以进行移动;6、在微调距离中设定一个数值再用形状工具选中曲线的某一节点敲方向箭头可以进行精确位移;7、将某一个汉字或字母转换为曲线就可以用形状工具进行修理如将“下”的右边的点拿掉等。

    介绍Region类

    Region,中文意思即区域的意思,它表示的是canvas图层上的某一块封闭的区域。

    /**构造方法*/ public Region() //创建一个空的区域 public Region(Region region) //拷贝一个region的范围 public Region(Rect r) //创建一个矩形的区域 public Region(int left, int top, int right, int bottom) //创建一个矩形的区域 /**一系列set方法,这些set方法,和上面构造方法形式差不多*/ public void setEmpty() { public boolean set(Region region) public boolean set(Rect r) public boolean set(int left, int top, int right, int bottom) /*往一个Region中添加一个Path只有这种方法,参数clip代表这个整个Region的区域,在在里面裁剪出path范围的区域*/ public boolean setPath(Path path, Region clip) //用指定的Path和裁剪范围构建一个区域 /**几个判断方法*/ public native boolean isEmpty();//判断该区域是否为空 public native boolean isRect(); //是否是一个矩阵 public native boolean isComplex();//是否是多个矩阵组合 /**一系列的getBound方法,返回一个Region的边界*/ public Rect getBounds() public boolean getBounds(Rect r) public Path getBoundaryPath() public boolean getBoundaryPath(Path path) /**一系列的判断是否包含某点 和是否相交*/ public native boolean contains(int x, int y);//是否包含某点 public boolean quickContains(Rect r) //是否包含某矩阵 public native boolean quickContains(int left, int top, int right, int bottom) //是否没有包含某矩阵 public boolean quickReject(Rect r) //是否没和该矩阵相交 public native boolean quickReject(int left, int top, int right, int bottom); //是否没和该矩阵相交 public native boolean quickReject(Region rgn); //是否没和该矩阵相交 /**几个平移变换的方法*/ public void translate(int dx, int dy) public native void translate(int dx, int dy, Region dst); public void scale(float scale) //hide public native void scale(float scale, Region dst);//hide /**一系列组合的方法*/ public final boolean union(Rect r) public boolean op(Rect r, Op op) { public boolean op(int left, int top, int right, int bottom, Op op) public boolean op(Region region, Op op) public boolean op(Rect rect, Region region, Op op)

    上面几乎是Region的所有API,很好理解,主要说明一下最后的一组关于Region组合的方式。组合即当前的Region和另外的一个Region组合,可以用不同的Op方式来进行组合。

    Op是一个枚举,定义在Region类中。

    假设用region1 去组合region2
    ``` public enum Op { DIFFERENCE(0), //最终区域为region1 与 region2不同的区域 INTERSECT(1), // 最终区域为region1 与 region2相交的区域 UNION(2), //最终区域为region1 与 region2组合一起的区域 XOR(3), //最终区域为region1 与 region2相交之外的区域 REVERSE_DIFFERENCE(4), //最终区域为region2 与 region1不同的区域 REPLACE(5); //最终区域为为region2的区域 } ```

    ApiDemo中已经提供了一个关于组合的例子,在最后面给出。 Android还提供了一个RegionIterator来对Region中的所有矩阵进行迭代,可以使用该类,获得某个Region的所有矩阵。

    了解了这些,你会体验到Region的好处。

    布局,xml:

    <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:appWave="http://schemas.android.com/apk/res/com.example.administrator.waterwavedemo" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#7CC1E0" tools:context="com.example.administrator.waterwavedemo.MainActivity"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:layout_width="@dimen/dp60" android:layout_height="@dimen/dp60" android:background="@drawable/yang" android:layout_alignParentRight="true" android:layout_marginRight="10dp" android:layout_marginTop="10dp" android:layout_gravity="right" android:id="@+id/imageView" /> <com.example.administrator.waterwavedemo.view.MyWaterWaveView android:layout_width="match_parent" android:id="@+id/myWaterWaveView" android:layout_height="500dp" appWave:rise="false" appWave:originY="500px" appWave:waveLegth="500px" appWave:waveHeight="200px" appWave:diration="3000" appWave:waveView_boatBitmap="@drawable/chuan" android:layout_alignParentBottom="true" android:layout_alignParentStart="true" /> </RelativeLayout> <LinearLayout android:layout_height="@dimen/dp80" android:layout_gravity="bottom" android:gravity="center" android:layout_width="match_parent"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btn_increave_rice" android:text="加速"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btn_del_rice" android:text="减速"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btn_add_wind" android:text="起风"/> </LinearLayout> </FrameLayout>

    项目连接 地址: https://github.com/androidstarjack/WaterWaveDemo

    如果你觉得此文对您有所帮助,欢迎入群 QQ交流群 :232203809   

    微信公众号:终端研发部

    (欢迎关注学习和交流)

    转载请注明原文地址: https://ju.6miu.com/read-35952.html

    最新回复(0)