6

如何用程序员的方式,营造七夕氛围感?

 3 years ago
source link: https://my.oschina.net/u/4956408/blog/5180708
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

一个“冷”知识,又双叒叕到一年七夕了!

每年此时,送礼指南满天飞,选择困难症都给逼出来了,疑了个惑,能搞定爱情的人,却在送礼这件事儿上纠结万分! 要我说,七夕这种有情人的浪漫佳节,最重要的是图一个氛围!今天给大家介绍一个适合程序员的七夕“氛围感十足”的小礼物——借助华为图像服务,开发个超有爱七夕动图,它支持输入特殊关键词,触发“七夕”特效;同时,画面还可跟随指尖触碰产生动效……效果先睹为快↓↓↓

Demo效果

有对象的秀给对象看,单身的朋友也可先做个七夕氛围组成员,方法到手,也许下次就用上啦。

话不多说,开整!

1. 关键字动画播放

第一步:素材准备

首先找到一个合适的图片,这里我们选择了一张牛郎织女的图片:

然后将其中有动画效果的部件从图中取出来。我们取出了云朵,牛郎,织女,红心四个元素。

第二步:集成准备

先按照下面的指导完成开发者注册,创建应用,签名配置: https://developer.huawei.com/consumer/cn/doc/development/Media-Guides/config-agc-0000001050199019?ha_source=hms1

再按照如下方式进行代码仓和编译依赖的配置:

  1. 在项目级“build.gradle”文件中配置:
buildscript {
    repositories {
        google()
        jcenter()
        // 配置HMS Core SDK的Maven仓地址。
        maven {url 'https://developer.huawei.com/repo/'}
    }
    dependencies {
        ...
        // 增加agcp插件配置。
        classpath 'com.huawei.agconnect:agcp:1.4.2.300'
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        // 配置HMS Core SDK的Maven仓地址。
        maven {url 'https://developer.huawei.com/repo/'}
    }
}

  1. 在应用级“build.gradle”文件配置编译依赖(当前最新版本1.0.3.301):
dependencies { 
implementation 'com.huawei.hms:image-render: 1.0.3.301' 
  implementation 'com.huawei.hms:image-render-fallback: 1.0.3.301'
}

在“AndroidManifest.xml”文件中配置应用所需权限。

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

第三步:功能开发

这里用最简单的界面,一个FrameLayout中配置输入框和按钮:

我们将在这个FrameLayout中调试和展现动画。

  1. 配置存储权限申请

在MainActivity的onCreate()方法中,检查是否拥有写存储的权限,如果缺少,就调用requestPermission方法,对WRITE_EXTERNAL_STORAGE权限进行申请:

int permissionCheck = ContextCompat.checkSelfPermission(ImageKitRenderDemoActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
initData();
initImageRender();
} else {
ActivityCompat.requestPermissions(ImageKitRenderDemoActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE);
}

如果已有权限,或权限申请成功后,对Image渲染模块进行初始化

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode == PERMISSION_REQUEST_CODE) {
        if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // The permission is granted.
            initData();
            initImageRender();
        } else {
            // The permission is rejected.
            Log.w(TAG, "permission denied");
            Toast.makeText(ImageKitRenderDemoActivity.this, "Please grant the app the permission to read the SD card", Toast.LENGTH_SHORT).show();
        }
    }
}

  1. Image渲染模块初始化

获取渲染实例,初始化,并获取渲染视图。 这里会指定动画元素的目录:

ImageRender.getInstance(context, new ImageRender.RenderCallBack() {
        // 获取场景动效服务实例成功回调,返回场景动效服务实例
        @Override
        public void onSuccess(ImageRenderImpl imageRender) {
            imageRenderAPI = imageRender;
            if (imageRenderAPI != null) {
                int initResult = imageRenderAPI.doInit(sourcePath, Utils.getAuthJson());
                Log.i(TAG, "DoInit result == " + initResult);
                if (initResult == 0) {
                    // Obtain the rendered view.
                    RenderView renderView = imageRenderAPI.getRenderView();
                    if (renderView.getResultCode() == ResultCode.SUCCEED) {
                        View view = renderView.getView();
                        if (null != view) {
                            // Add the rendered view to the layout.
                            contentView.addView(view);
                            hashCode = String.valueOf(view.hashCode());
                        } else {
                            Log.w(TAG, "GetRenderView fail, view is null");
                        }
            }
        }
        // 获取场景动效服务实例失败回调,返回错误码
        @Override
        public void onFailure(int errorCode) {
        ...
        }
    });

  1. 配置关键字播放动画

还记得前面留下的输入框和按钮吗?我们使用关键字“Love”来进行动画播放。只需要通过imageRenderAPI.playAnimation()即可触发:

wordInput = findViewById(R.id.textinput);
enterBtn = findViewById(R.id.enter);
enterBtn.setOnClickListener(v -> {
    String inputContent = wordInput.getText().toString();
    if (inputContent.contentEquals("Love")) {
        if (null != imageRenderAPI) {
            imageRenderAPI. playAnimation();;
            wordInput.setVisibility(View.GONE);
            enterBtn.setVisibility(View.GONE);
        }
    } else {
        Toast.makeText(this,"再想想?",Toast.LENGTH_SHORT).show();
    }
});

框架搭好了,现在来到动画的部分。Image Kit提供了5种基础动效和9种高级动效,可以满足绝大部分场景的使用要求。 在这里我们使用到了透明度动画,位移动画,缩放动画,以及下落动效。

Image Kit的动画配置均在manifest.xml文件中完成。可不要和AndroidManifest.xml文件搞混了哦。

首先配置虚拟屏宽及背景图片。配置虚拟屏宽之后,系统会根据不同的分辨率对动画进行缩放,使效果保持一致。

<Root screenWidth="1080">
<Image src="background.png"/>

我们希望牛郎织女能够逐渐靠近直至相会,此时加入牛郎织女两个元素,分别配置动线,此时使用到位移动画的特效:

 <Image x="1000" y="1450" src="man.png">
       <PositionAnimation repeat="1">
           <Position x="0" y="0" time="0"/>
           <Position x="-450" y="-100" time="4000"/>
       </PositionAnimation>
    </Image>
    <Image x="-600" y="800" src="woman.png">
        <PositionAnimation repeat="1">
            <Position x="0" y="0" time="0"/>
            <Position x="700" y="300" time="4000"/>
        </PositionAnimation>
    </Image>

这样牛郎织女将从屏幕两侧向中间靠近,直至相会。

相会之后在中心将出现跳动的红心,这里用到了透明度动画及缩放动画的叠加特效:

 <Image x="520" y="1350"  src="heart.png" visibility="#show1">
        <AlphaAnimation delay="4000" repeat="1">
            <Alpha a="255" time="0"/>
            <Alpha a="0" time="3000"/>
        </AlphaAnimation>
        <SizeAnimation delay="4000" repeat="1">
            <Size w="127" h="95" time="0"/>
            <Size w="508" h="380" time="3000"/>
        </SizeAnimation>
    </Image>

到现在,关键元素已经有了,可是看起来还是有点干巴,要找点什么点缀一下。 天上的云朵也可以动起来,让它在小范围里动一下更灵动一些:

 <Image x="150" y="200" src="cloud.png">
        <PositionAnimation repeat="0">
            <Position x="0" y="0" time="0"/>
            <Position x="-50" y="0" time="3000"/>
            <Position x="0" y="0" time="6000"/>
        </PositionAnimation>
    </Image>

想再罗曼蒂克一些,撒一些花瓣,这里用到的就是下落动效:

 <DropPhysicalView gravityX="3" gravityY="10" airDensity="1000" delay="8800" visibility="#show2">
        <ItemGroup x="0" y="0" width="#screen_width" height="#screen_height">
            <Alpha x="0" y="#screen_height-1000" width="#screen_width" height="#screen_height" value="20"/>
            <Item count="20" src="follow.png">
                <Velocity isRandom="true" velocityX="0" velocityY="5"/>
                <Position isRandom="true"/>
                <AngleVelocity isRandom="true" angleVelocity="5"/>
                <Weight isRandom="true" value="0.5"/>
            </Item>
        </ItemGroup>
    </DropPhysicalView>

  1. 到这里已经大功告成。最后可以再将动画保留下来
// 开始录制
int resultCode = imageRenderAPI.startRecord(json, new IStreamCallBack () {
    // 在录制成功回调中,将视频或GIF字节数组保存成mp4或GIF文件
    @Override
    void onRecordSuccess(HashMap<String, Object> map) {
        ...
        String recordType = (String) hashMap.get("recordType");
        byte[] videoBytes = (byte[]) hashMap.get("videoBytes");
        byte[] gifBytes = (byte[]) hashMap.get("gifBytes");
        try {
            if (recordType.equals("1")) {
                if (videoBytes != null) {
                    // 保存mp4文件
                    saveFile(videoBytes, mp4Path);
                }
            } else if (recordType.equals("2")) {
                ...
            } else if (recordType.equals("3")) {
                ...
            }
        } catch (IOException e) {
            ...
        }
        ...
    }

    // 录制失败回调
    @Override
    void onRecordFailure(HashMap<String, Object> map) {
        ...
    }

    // 录制进度回调,progress取值范围为0-100
    @Override
    void onProgress(int progress) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                textProgress.setText("当前录制进度:" + progress + "%");
            }
        });
    }
});


以上就得到最终效果啦~

除了动效之外,Image Kit还提供了滤镜功能,可以给图片添上浪漫的色彩,贴纸花字的功能,也可以给用户的图片增加爱情的元素。

了解更多>>

访问华为图像服务官网

访问华为开发者联盟官网

获取开发指导文档

华为图像服务开源仓库地址:GitHubGitee

关注我们,第一时间了解HMS Core 最新技术资讯~


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK