这是一个 android 动画特效库 可以实现各种炫酷动画。
github地址: ht t ps:// gith  u b.co m/g pl ib s/an dro id- ma gic-s ur fac e-view

1. 安装

gradle:

dependencies {    compile 'com.gplibs:magic-surface-view:1.0.0'}



2. 一些示例效果

此文档只做一些简单说明, 具体使用方法还请参考其示例项目 (上面github地址可以找到示例项目) .

以下是一些示例效果:

启动及退出动画 :

模仿MacWindow动画 :

其他几个示例效果可以下载其示例项目运行查看。



3. 概述

一个MagicSurfaceView只能同时渲染一个MagicScene
一个MagicScene可以包含多个MagicSurface
一个MagicSurface可以对应一个View或者Bitmap对象

场景创建及渲染

// 创建一个Surface对象MagicSurface surface = new MagicSurface(view) // view为要进行动画操作的View
        .setVisible(true)                // 设置模型是否要渲染 (默认为true)
        .setShininess(64)                // 设置模型材质光泽度,默认64; 数值越大越光滑, 只对光照生效,无光照效果可忽略.
        .setGrid(30, 40)                 // 设置网格模型行列数,行列数越多效果越精致,但也更耗性能; 默认 30,30
        .setEnableBlend(true)            // 是否开启混合为透明对象时需开启(默认为开启)
        .setEnableDepthTest(true)        // 是否开启深度测试开启后会按三维坐标正常显示如果关闭绘制时将覆盖之前已经绘制的东西(默认为开启)
        .setModelUpdater(modelUpdater)   // 设置模型更新器, 可以执行顶点坐标及颜色相关动画操作; 详情见 "5. 模型更新器 MagicSurfaceModelUpdater"
        .setMatrixUpdater(matrixUpdater) // 设置矩阵更新器, 可以执行矩阵变换相关动画操作; 详情见 "6. 矩阵更新器 MagicSurfaceMatrixUpdater"
        .drawGrid(false);                // 设置绘制时是否只绘制网络默认false. (调试动画找问题时可以只画网格可能有点帮助)// 创建场景MagicScene scene = new MagicSceneBuilder(mSurfaceView)
        .addSurfaces(surface)            // 添加Surface对象可以添加多个 如: addSurfaces(surface, surface1, surface2)
        .ambientColor(0XFF222222)        // 设置场景环境光, 默认为0XFFFFFFFF
        .addLights(light)                // 添加光源对象,类型可以为PointLight或者DirectionalLight; 可以添加多个 如: addLights(light, light1, light2)
        .setUpdater(sceneUpdater)        // 添加场景更新器, 可以执行场景相关变量的动画操作; 详情见 "4. 场景更新器 MagicSceneUpdater"
        .build();// 渲染myMagicSurfaceView.render(scene);

模型更新器 MagicSurfaceModelUpdater 动画原理:

MagicSurfaceModelUpdater 原理相对麻烦些单独说明如下

渲染使用openGL每个MagicSurface对象都包含一个SurfaceModel这个SurfaceModel即是openGl要绘制的曲面模型;
SurfaceModel中包含着openGL绘制时需要的一些必要元素, 顶点坐标集合、顶点索引集合、法向量集合等;
构造MagicSurface时传入View后会转为Bitmap 作为纹理绑定到曲面模型上。
曲面模型的生成及纹理绑定过程都由此库自动完成。

我们可以做的是
SurfaceModel中的顶点集合是一个 r(行) * c(列) 的一个矩形网格
我们修改这个矩形网格上每个点的属性渲染效果就会有相应的变化(网格的点与点之间每像素点的属性由openGL根据我们设置的网格关键点的属性进行插值计算自动完成)
网格上每个点可以修改的属性包含:

  1. 顶点坐标。修改网格上某一点的顶点坐标后绑定在该点上的纹理也会跟着发生相应的形变。

  2. 顶点颜色。修改网格上某一点的顶点颜色后该点上的最终颜色会按 "顶点最终颜色计算过程" 那样进行变化。

顶点最终颜色计算过程 为:

顶点最终颜色 = 原始颜色 * 顶点颜色 * 场景环境光颜色 + 原始颜色 * 顶点颜色 * 灯光颜色
原始颜色构建MagicSurface时传入的View或者Bitmap生成的纹理在对应坐标的颜色值.
顶点颜色默认为rgba(1,1,1,1); 可以由 模型更新器 修改.
场景环境光颜色默认为rgba(1,1,1,1); 可以由 MagicSceneUpdater 修改.
灯光颜色构建MagicScene时传入的灯光对象集合在模型对应顶点产生的光照颜色值; 如果未设置灯光灯光颜色值为rgba(0,0,0,0); 可以由 MagicSceneUpdater 修改.

颜色相乘算法为(其中r,g,b,a都为0~1的浮点数, 对应×××颜色值0~255)
color1(r1, g1, b1, a1) * color2(r2, g2, b2, a2) == color3(r1* r2, g1* g2, b1* b2, a1* a2)
颜色相加算法为(其中r,g,b,a都为0~1的浮点数, 对应×××颜色值0~255)
color1(r1, g1, b1, a1) + color2(r2, g2, b2, a2) == color3(r1+r2, g1+g2, b1+b2, a1+a2)

场景坐标(即openGL坐标):

跟坐标相关的动画操作都使用场景坐标与Android的View的坐标系无关
MagicSurfaceView的中心即为场景坐标原点(0,0,0); x轴向右 y轴向上 z轴向屏幕外.
MagicSurface网格模型各点坐标z轴默认为0
MagicSurfaceView 及 MagicSurface 相关点场景坐标获取方法 参考 "5. 模型更新器 MagicSurfaceModelUpdater"

关于偏移量:

当一个MagicSurface上同时使用模型更新器和矩阵更新器时.
两个只能有一个应用偏移量不然位置会出现偏差。
偏移量具体说明参考 模型更新器 和 矩阵更新器。

Updater性能优化:

MagicSceneUpdater, MagicSurfaceModelUpdater及MagicSurfaceMatrixUpdater 都为 MagicUpdater 的子类
当同时有多个MagicUpdater运行时可以考虑进行分组
MagicUpdater都有一个可以传入分组参数的构造函数和一个setGroup方法可以对他们进行分组; (setGroup必须要在调用render方法之前执行)
如果不进行分组它们会独自开启一线程更新。将一些计算量较少的MagicUpdater分到同一组他们将在一个线程中执行这样可以节约资源提升性能。



4. 场景更新器 MagicSceneUpdater

MagicSceneUpdater 对场景变量进行修改; 场景变量包含 环境光和光源。

调用过程为 willStart -> didStart -> (update [此部分通过notifyChanged触发循环调用直到 调用Updater stop方法]) -> didStop

public class MySceneUpdater extends MagicSceneUpdater {    public MySceneUpdater(int group) {        super(group);
    }    // 在绘制第一帧之前调用 (可以在此方法里进行一些初始化操作)
    @Override
    protected void willStart(MagicScene scene) {
    }    // 在开始绘制后调用绘制第一帧后调用一般动画可以在此开始 
    // 动画有更新时需调用 notifyChanged()方法 通知框架可以调用 update 相关方法进行更新。
    @Override
    protected void didStart(MagicScene scene) {
    }    // 当调用Updater 的 stop() 方法之后真正停止后会回调此方法
    @Override
    protected void didStop(MagicScene scene) {
    }    // 更新环境光及灯光
    @Override
    protected void update(MagicScene scene, Vec outAmbientColor) {        // 修改环境光
        // outAmbientColor.setColor(...)

        // 获取第0个光源并修改 假设是点光源
        // PointLight pl = scene.getLight(0);
        // pl.setColor(...);
        // pl.setPosition(...);

        // 获取第1个光源并修改 假设是方向光源
        // DirectionalLight dl = scene.getLight(1);
        // dl.setColor(...);
        // dl.setDirction(...);

        // 根据需要还可以使用如下方法获取某个MagicSurface对象
        // scene.getSurface(index);
    }
}



5. 模型更新器 MagicSurfaceModelUpdater

MagicSurfaceModelUpdater 对MagicSurface网格模型各顶点坐标及颜色值进行修改

调用过程为 willStart -> didStart -> (updateBegin -> (updatePosition [遍历网格每个点]) -> updateEnd [此部分通过notifyChanged触发循环调用直到 调用 Updater stop方法]) -> didStop

public class MyModelUpdater extends MagicSurfaceModelUpdater {    public MyModelUpdater() {
    }    // 在绘制第一帧之前调用 (可以在此方法里进行一些初始化操作)
    @Override
    protected void willStart(MagicSurface surface) {
    }    // 在开始绘制后调用绘制第一帧后调用一般动画可以在此开始 
    // 动画有更新时需调用 notifyChanged()方法 通知框架可以调用 update 相关方法进行更新。
    @Override
    protected void didStart(MagicSurface surface) {
    }    // 当调用 updater stop方法之后真正停止后会回调此方法
    @Override
    protected void didStop(MagicSurface surface) {
    }    // 每次顶点更新之前调用
    @Override
    protected void updateBegin(MagicSurface surface) {
    }    /**     * 修改网格模型 r行, c列处 的坐标及颜色 修改后的值存到 outPos 和 outColor     * (只需要修改网格模型各行各列点及可点与点之间的坐标和颜色由 openGL 自动进行插值计算完成)     * 注此方法执行频率非常高一般不要有分配新的堆内存的逻辑会频繁产生gc操作影响性能     *     * @param surface     * @param r 行     * @param c 列     * @param outPos 默认值为 r行c列点包含偏移量的原始坐标, 计算完成后的新坐标要更新到此变量     * @param outColor 默认值为 rgba(1,1,1,1), 计算完成后的新颜色要更新到此变量     */
    @Override
    protected void updatePosition(MagicSurface surface, int r, int c, Vec outPos, Vec outColor) {
    }    // 每次所有顶点更新完成后调用
    @Override
    protected void updateEnd(MagicSurface surface) {        // 有光照效果时顶点坐标值更新后, 需要更新法向量以得到正常光照效果但按标准法向量计算方法开销过大顶点数较少时可以使用
        // surface.getModel().updateModelNormal();

        //可以在此方法里判断动画是否结束结束需调用 stop()方法以结束updater.
    }
    
}

MagicSurfaceModelUpdater相关方法说明:

在Updater生命周期内 以下这些方法都是可以有效调用的。

// 获取 MagicSurfaceView 在openGL坐标系中的宽度surface.getScene().getWidth();// 获取 MagicSurfaceView 在openGL坐标系中的高度surface.getScene().getHeight();// 获取 MagicSurfaceView 某点在openGL坐标系中的坐标并存入pos// 比如 surface.getScene().getPosition(0.0f, 0.0f, pos); 获取 MagicSurfaceView 左上角的坐标// 比如 surface.getScene().getPosition(0.0f, 1.0f, pos); 获取 MagicSurfaceView 左下角的坐标// 比如 surface.getScene().getPosition(1.0f, 0.0f, pos); 获取 MagicSurfaceView 右上角的坐标// 比如 surface.getScene().getPosition(1.0f, 1.0f, pos); 获取 MagicSurfaceView 右下角的坐标surface.getScene().getPosition(ratioX, ratioY, pos);// 获取 MagicSurface 在openGL坐标系中的宽度surface.getModel().getWidth();// 获取 MagicSurface 在openGL坐标系中的高度surface.getModel().getHeight();// 获取 MagicSurface 网格模型总行数surface.getModel().getRowLineCount();// 获取 MagicSurface 网格模型总列数surface.getModel().getColLineCount();// 获取 MagicSurface 网格模型 r行 c列 在openGL坐标系中的坐标并存入pos// 注: 此方法包含偏移量模型中心在 场景中心+偏移量 的位置; 偏移量是在创建SurfaceView时根据传入的View与MagicSurfaceView相对位置自动生成.surface.getModel().getPosition(r, c, pos);// 获取 MagicSurface 网格模型 r行 c列 在openGL坐标系中的坐标并存入pos// 注: 此方法不包含偏移量模型中心在场景中心处surface.getModel().getPositionExcludeOffset(r, c, pos);



6. 矩阵更新器 MagicSurfaceMatrixUpdater

MagicSurfaceMatrixUpdater 对MagicSurface网格模型进行各种矩阵变换(缩放旋转平移).

调用过程为 willStart -> didStart -> (updateMatrix [此部分通过notifyChanged触发循环调用直到 调用 Updater stop方法]) -> didStop

public class MyMatrixUpdater extends MagicSurfaceMatrixUpdater {    public MyMatrixUpdater(int gorup) {        super(group);
    }    // 在绘制第一帧之前调用 (可以在此方法里进行一些初始化操作)
    @Override
    protected void willStart(MagicSurface surface) {
    }    // 在开始绘制后调用;绘制第一帧后调用一般动画可以在此开始 
    // 动画有更新时需调用 notifyChanged()方法 通知框架可以调用 update 相关方法进行更新。
    @Override
    protected void didStart(MagicSurface surface) {
    }    // 当调用 updater stop方法之后真正停止后会回调此方法
    @Override
    protected void didStop(MagicSurface surface) {
    }    /**     * 矩阵变换     * @param surface 需更新的MagicSurface对象     * @param offset offse为模型相对场景中心的坐标偏移量, 如果不进行 offset 位移 model 就会显示在场景中心     *     *               当使用 View 构造 MagicSurface 时     *               View中心位置 相对 MagicSurfaceView中心位置的坐标偏移量 在场景坐标系中的表现就是 offset。     *     * @param matrix 矩阵     */
    @Override
    protected void updateMatrix(MagicSurface surface, Vec offset, float[] matrix) {        // 重置matrix
        reset(matrix);        // 缩放 
        scale(matrix, xScale, yScale, zScale);        // 包含偏移量的位移
        translate(matrix, x + offset.x(), y + offset.y(), z + offset.z());        // 位移
        // translate(matrix, x, y, z);

        // 绕 axis轴 旋转 angle度。
        rotate(matrix, axis, angle);        // 注: 当同时有位移和旋转操作时位移操作要放在前面。
    }
}