背景
这是一个特别小巧的鸿蒙掉emoji表情包实现。效果类似于微信中发送"生日快乐"和"么么哒"之类的词语时触发的动画效果。
功能展示
基于鸿蒙系统,通过自定义控件属性方式实现了EmojiRain组件,同时支持java代码设置。
原理解析
如图所示,表情控件Image初始位置在屏幕的上方,EmojiRainLayout充满整个屏幕。
表情包开始掉落前,从指定表情包集合中获取对应元素,计算该元素本次掉落时的起始位置、终止位置、宽度、高度。
然后根据位置坐标及高度创建对应的动画对象,设置Animator.CurveType.ACCELERATE_DECELERATE动画插值器。
根据表情包掉落数量将Image元素准备完成后,通过调用addComponent()将Image添加到EmojiRainLayout上,达到覆盖在屏幕上的效果。
最后执行先加速后减速的动画效果,开启表情雨模式。
- //使用rxjava控制动画执行间隔、执行顺序、执行对象
- Subscription subscription = Observable.interval(mDropFrequency, TimeUnit.MILLISECONDS)
- .take(mDuration / mDropFrequency)
- .flatMap(flow -> Observable.range(0, mEmojiPer))
- .map(image -> mEmojiPool.acquire())
- .filter(ep -> ep != null)
- .observeOn(OhosSchedulers.mainThread())
- .subscribe(this::startDropAnimationForSingleEmoji, Throwable::printStackTrace);
- mSubscriptions.add(subscription);
-
- //为Image创建动画对象
- AnimatorProperty animatorProperty = emoji.createAnimatorProperty();
- //设置动画执行时长
- animatorProperty.setDuration((int)
- (mDropAverageDuration * Randoms.floatAround(1, RELATIVE_DROP_DURATION_OFFSET)));
- float startX = Randoms.getStartX();
- float endX = Randoms.getStopX();
- //指定动画开始的坐标及终止坐标
- animatorProperty.moveFromX(startX).moveToX(endX).moveFromY(-imageHeight).moveToY(mWindowHeight);
- animatorProperty.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);
- animatorProperty.start();
-
- //初始化Image,随机赋予等比例缩放高度、宽度,指定图片加载模式
- Image emoji = new Image(getContext());
- emoji.setImageElement(emojiDrawable);
- emoji.setScaleMode(Image.ScaleMode.CENTER);
- double positive = Randoms.positiveGaussian() * 0.6;
- final int width = (int) (emoji_standard_size * (1.0 + positive));
- final int height = (int) (emoji_standard_size * (1.0 + positive));
- final LayoutConfig params = new LayoutConfig(width, height);
- params.setMarginTop(-height);
- params.setMarginLeft((int) (-0.5F * width));
- emoji.setLayoutConfig(params);
使用说明
参数配置
per
- 每一波掉落的emoji个数,默认6个
duration
- 掉落动画持续的总时长,默认8000ms
dropDuration
- 每个emoji掉落时长的平均值,默认2400ms
dropFrequency
- 掉落频率,即每两拨的时间间隔,默认500ms
在layout中配置 EmojiRainLayout继承自StackLayout,你完全可以把它当做原生的StackLayout使用。
- xmlns:ohos="http://schemas.huawei.com/res/ohos"
- xmlns:app="http://schemas.huawei.com/res/ohos-auto"
- xmlns:tools="http://schemas.android.com/tools"
- ohos:height="match_parent"
- ohos:width="match_parent"
- app:dropDuration="2400"
- app:dropFrequency="500"
- app:duration="7200"
- app:per="10">
-
-
- ohos:height="match_content"
- ohos:width="match_content"
- ohos:text="Hello world!" />
-
-
- public class MainAbilitySlice extends AbilitySlice {
-
- private EmojiRainLayout mContainer;
-
- @Override
- protected void onStart(Intent intent) {
- super.onStart(intent);
- super.setUIContent(ResourceTable.Layout_ability_main);
-
- // bind view
- mContainer = (EmojiRainLayout) findComponentById(ResourceTable.Id_group_emoji_container);
-
- // add emoji sources
- mContainer.addEmoji(ResourceTable.Media_emoji_1_3);
- mContainer.addEmoji(ResourceTable.Media_emoji_2_3);
- mContainer.addEmoji(ResourceTable.Media_emoji_3_3);
- mContainer.addEmoji(ResourceTable.Media_emoji_4_3);
- mContainer.addEmoji(ResourceTable.Media_emoji_5_3);
-
- // set emojis per flow, default 6
- mContainer.setPer(10);
-
- // set total duration in milliseconds, default 8000
- mContainer.setDuration(7200);
-
- // set average drop duration in milliseconds, default 2400
- mContainer.setDropDuration(2400);
-
- // set drop frequency in milliseconds, default 500
- mContainer.setDropFrequency(500);
- }
- }
开始掉落
- mContainer.startDropping();
停止掉落
- mContainer.stopDropping();