前言

Parallax Scrolling (视差滚动),是一种常见的动画效果。视差一词来源于天文学,但在日常生活中也有它的身影。在疾驰的动车上看风景时,会发现越是离得近的,相对运动速度越快,而远处的山川河流只是缓慢的移动着,这就是最常见的视差效果。视差动画独有的层次感能带来极为逼真的视觉体验,iOS、Android Launcher、Website 都将视差动画作为提升用户视觉愉悦度的不二选择。

客户端应用第一次打开出现引导页也不是什么新鲜的事儿,ViewPager 配上几张设计师精心绘制的图片,分分钟即可了事。但是总有人把平凡的事情做到不平凡,如本文的知乎客户端,亦或是新浪微博贺岁版,百度贴吧某版等众多应用里都出现了视差动画的身影,随着用户手指的滑动,反馈以灵动、贴近真实的视觉以及操作体验,对应用的初始印象登时被提升到一个极高的点。

给我印象最深的是去年新浪微博的贺岁版,引导页是一系列的年画,里面有红色剪纸的小孩儿,滑动界面的时候感觉这些元素在『动』,是真正的灵动,能勾起人童年的回忆,年味儿十足。不过话说我年怎么过跟新浪微博一毛钱关系都没有,但是这个启动页却是深得我意。只是这个版本的微博找不到了,正好前两天看到知乎的启动页做的也不错,就正好拿来练练手吧。

本文就知乎 Android 客户端启动页面为例,教你如何实现视差滚动效果。

界面分析

细心把玩下知乎的启动页,不难分析出来,视差动画主要体现在背景层渐变、内容层元素差异滚动上,动画内容分别是:

- 内容:元素差异滚动,形成视差效果()

- 背景:随着界面的滑动,颜色由深蓝色渐变为浅蓝色()

- 文字:底部提示文案会随页面变动而切换,有简单的淡入淡出效果

- 界面动画:界面打开,元素的出场动画(第一页以及最后一页)

鉴于其它几项比较简单,本文主要讲视差动画以及背景渐变的实现,其它几项请自行参阅代码,见后文。

Parallax Scrolling

这里的视差滚动效果,主要表现为内容元素滚动速率的差异上。比如在 ViewPager 中滑动了 1px ,而 A 元素移动 2px , B 元素移动 1.5px ,这种移动差距的比率,我称之为 parallaxCofficient ,即 视差系数 或者 视差速率 ,正是同一个界面中的元素,由于层级不同,赋予的视差系数不同,在移动速度上的差异形成了视差的错觉,这就是我们要追求的效果。

那知道原理就好办了,使用 ViewPager.OnPageChangeListener ,动态计算不就得了。 no no no ! 后面完成背景渐变效果确实需要计算这个,但是 ViewPager 已经为我们准备好了变形元素 transformium : ViewPager.PageTransformer ,它有一个抽象方法 transformPage(View page, float position) ,正是为我们完成视差动画量身定制的。

ViewPager.PageTransformer

PageTransformer 在 ViewPager 滑动时被触发,它为我们自定义页面中进行视图变换打开了一扇大门。

javapublic abstract void transformPage (View page, float position)

在 ViewPager 源码中,我们可以很直观的看到它的调用过程:

java// ViewPager#onPageScrolled

if (mPageTransformer != null) {

final int scrollX = getScrollX();

final int childCount = getChildCount();

for (int i = 0; i < childCount; i++) {

final View child = getChildAt(i);

final LayoutParams lp = (LayoutParams) child.getLayoutParams();

if (lp.isDecor) continue;

final float transformPos = (float) (child.getLeft() - scrollX) / getClientWidth();

mPageTransformer.transformPage(child, transformPos);

}

}

Param 1: View page

从上面的代码中,不难看出,page 就是当前被滑动的页面,调试得知,每一个 child view 被 NoSaveStateFrameLayout 包装,也就是说 page.getChildAt(0) 即是每个 page 实际的 child view 。

Param 2: float position

position 这个参数不看代码或者文档,总会误以为就是我们熟知的 integer position ,不过它实际上是滑动页面的一个相对比例,本质跟 1、2、3、4 这种 position 是一样的。

比如知乎启动页共有 6 个页面,分别是 A、B、C、D、E、F 初始状态也就是 A 页面静止时,A 页面的 position 正好是 0 ,B 页面是 1 。而后滑动页面(B -> A),在这个过程中 A 的 position 是间于 [-1, 0] ,B 页面则是间于 [0, 1] 。

不过这个参数的文档却是简单不够直观,对照上面的例子,现在应该很清晰了。

Position of page relative to the current front-and-center position of the pager. 0 is front and center. 1 is one full page position to the right, and -1 is one page position to the left.

ParallaxTransformer

根据上面的分析,我们可以得出一个相对简单的自定义 transformer ,对 page view 进行遍历,递增或者递减其 parallaxCofficient ,以得到我们预期的效果,具体的系数设置请参考代码。

javaclass ParallaxTransformer implements ViewPager.PageTransformer {

float parallaxCoefficient;

float distanceCoefficient;

public ParallaxTransformer(float parallaxCoefficient, float distanceCoefficient) {

this.parallaxCoefficient = parallaxCoefficient;

this.distanceCoefficient = distanceCoefficient;

}

@Override

public void transformPage(View page, float position) {

float scrollXOffset = page.getWidth() * parallaxCoefficient;

// ...

// layer is the id collection of views in this page

for (int id : layer) {

View view = page.findViewById(id);

if (view != null) {

view.setTranslationX(scrollXOffset * position);

}

scrollXOffset *= distanceCoefficient;

}

}

}

背景渐变

留心才会发现,从第一页滑动到最后一页,背景色会平滑的从深蓝色过度到浅蓝色,这种效果又该怎么实现呢?

用过 Property Animation 的同学应该知道,以前的 Animation 只能用在 View 上,而 Property Animation 却可以用在任意类型属性值上,这归功于 TypeEvaluator 。

正好我们有 ArgbEvaluator ,它可以估算两个颜色值之间,任意部分的色值。因此,只需要指定起始色值以及最终的色值,传入滑动所对应的 fraction 即当前位置相对总距离的比例值,即可获得相应的色值。

javapublic class ArgbEvaluator implements TypeEvaluator {

public Object evaluate(float fraction, Object startValue, Object endValue) {

// ...

}

}

当然,前面说到需要使用 ViewPager.OnPageChangeListener 的:

javaclass GuidePageChangeListener implements ViewPager.OnPageChangeListener {

ArgbEvaluator mColorEvaluator;

int mPageWidth, mTotalScrollWidth;

int mGuideStartBackgroundColor, mGuideEndBackgroundColor;

public GuidePageChangeListener() {

mColorEvaluator = new ArgbEvaluator();

mPageWidth = getWindowManager().getDefaultDisplay().getWidth();

mTotalScrollWidth = mPageWidth * mAdapter.getCount();

mGuideStartBackgroundColor = getResources().getColor(R.color.guide_start_background);

mGuideEndBackgroundColor = getResources().getColor(R.color.guide_end_background);

}

@Override

public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

float ratio = (mPageWidth * position + positionOffsetPixels) / (float) mTotalScrollWidth;

Integer color = (Integer) mColorEvaluator.evaluate(ratio, mGuideStartBackgroundColor, mGuideEndBackgroundColor);

mPager.setBackgroundColor(color);

}

@Override

public void onPageSelected(int position) {}

@Override

public void onPageScrollStateChanged(int state) {}

}

源码

代码已经 push 到 Github 了,诸位自取。不过请注意,其素材均取自于知乎 Android 客户端(你懂的),学习交流即可,请勿用作商业用途。

还求更优雅的实现方式,欢迎发起 pull request 。

参考

android 开机动画 渐变,[Parallax Animation]实现知乎 Android 客户端启动页视差滚动效果...相关推荐

  1. 【Parallax Animation】实现知乎 Android 客户端启动页视差滚动效果

    欢迎转载,但请务必注明出处!http://ryanhoo.github.io/blog/2014/07/16/step-by-step-implement-parallax-animation-for ...

  2. 实现知乎 Android 客户端启动页视差滚动效果

    http://ryanhoo.github.io/blog/2014/07/16/step-by-step-implement-parallax-animation-for-splash-screen ...

  3. android开机动画和图片在那个目录下,Android开机动画bootanimation.zip是怎么制作和使用的?...

    很多玩安卓手机的朋友都喜欢对自己的手机进行个性化的美化,下下主题,换换开机动画什么的,可是网上下载的开机动画千篇一律没有什么个性,是不是很想自己制作一个属于你的独一无二的开机动画呢? 制作安卓开通动画 ...

  4. android 刷机动画,Android开机动画修改方法

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 Android开机动画有两种修改方法,android 2.0及之后,使用bootanimation程序显示开机画面,如需修改开机画面,不用修改代码,只需按 ...

  5. android动画知乎,GitHub - ryanhoo/Zhihu-Parallax-Animation: 知乎 Android 客户端启动页的视差动画效果实现...

    Parallax Animation 知乎 Android 客户端启动页的视差动画效果实现 Blog Declaration 项目中图片等素材均取自于知乎 Android 客户端,仅作学习交流之用,请 ...

  6. Android开机动画bootanimation

    android开机动画详细分析可以参见http://blog.csdn.net/luoshengyang/article/details/7691321 引用老罗的文章,写的太好了. 以下介绍一些相关 ...

  7. android开机动画bootanimation 分析

    转载地址:http://blog.csdn.net/myvest/article/details/50856199 android开机动画详细分析可以参见http://blog.csdn.net/lu ...

  8. android+动画打包命令,Android 开机动画客制化

    Android开机动画总共有三个过程.第一个开机动画是在Kenel启动时显示的,第二个开机动画是在init进程启动时显示的,这两个都是静态图片.第三个动画是在系统服务启动过程中显示的,他是一个动态图片 ...

  9. Android 开机动画流程介绍

    前言 简单介绍了安卓开机动画流程 流程 [开机动画启动流程]: // Bootanim.rc (frameworks\base\cmds\bootanimation)service bootanim ...

最新文章

  1. ubunntu安装php7.0_在 Ubuntu/Debian 下安装 PHP7.3 教程
  2. UICollectionView的无限滚动---妥协做法
  3. 不一样的假期,到底哪里不一样?
  4. MySQL笔记-解决Can't connect to local MySQL server through socket '/xx/xx/mysql.sock' (111)及(2)
  5. Eclipse调试Java程序技巧
  6. 前台JS事件与服务器事件的执行顺序
  7. RemoteApp登录到特定的域
  8. matlab 随机函数的使用
  9. linux tcp自动重连,LabVIEW TCP/IP 断开重连问题
  10. PAT (Advanced Level) Practice 1001 A+B Format (20分)
  11. 苹果手机怎么打印wps文档_苹果手机WPS怎么打印?
  12. 北风修仙笔记—2020年6月
  13. 4. Applepay 详解
  14. 一篇文章带你了解大数据生态圈---大数据组件图谱
  15. 《手把手教你读财报》读后感
  16. 邢台职业技术学院计算机学费,邢台职业技术学院各专业学费标准
  17. AM335X EDMA3
  18. thinkpad T430 :Intel 7系主板在XP下装AHCI驱动的方法
  19. by which, in which, from which...
  20. 基本类型包装 基本数据类型包装类 Data 日期类 SimpleDateFormat类

热门文章

  1. 省培计算机实践作业,计算机软件基础强化实践能力培养实践部分考核作业.doc...
  2. java衍生作用_关于JAVA衍生出的一点想法
  3. java 读取控制台_Java从控制台读入数据的几种方法总结
  4. ssm_layui_billmanagersystem账单管理系统(全栈开发)
  5. java写出http数据包_java用jpcap怎么识别出http和https的数据包?
  6. oracle修改窗口字体大小,jQuery之字体大小的设置方法
  7. java创建读取文件_Java实现文件的创建、读取、写入操作-Fun言
  8. python元编程运用_Python 中的元编程
  9. vue修改入口文件名字_webpack打包vue项目,可修改配置文件
  10. c语言贪吃蛇黑方框,[求助][贪吃蛇]源程序,请教其中一个问题。