纯JS分布式视频播放应用 原创 精华
一, 前言
今时今日,不管是大人,还是小孩,都喜欢刷视频,生活中刷视频的APP也多得是,如:抖音,快手,视频号,今日头条,火山… 数也不数不清了,然而华为论坛鸿蒙版块搞活动,做一个属于自己的视频应用,说真的,看到这个活动我很开心,又可以用所学的鸿蒙知识来做一个小应用了,看了小提示,都是JS组件来实现的,当我看到分布式也可以用JS来写时,当时觉得JS也太强大了,因为之前写的Demo都是用Java来写分布式的,本人工作也是从事Java后台开发,对于JS前端知识,也就是入门级水平,然后就在想是用Java来写这个视频应用,还是用JS来写呢,通过看了JS参考API实例后,决定使用JS来写,简单易懂,同时也希望现在还在观望鸿蒙应用开发的前端开发人员,不要怕自己不会Java开发,而一直在观望,没有踏出第一步来写Demo,我写的这个视频应用取名为 爱视频 ,99%是用JS前端知识完成的,只有1%Java代码是复制过来的,也就是动态授权代码,所以希望还在观望的前端开发者,就从这个 爱视频 APP开始你们的第一个鸿蒙应用吧!
二, 实现效果
开发环境下视频:https://www.bilibili.com/video/BV1kf4y177h2/?spm_id_from=333.788
手机下视频:https://www.bilibili.com/video/BV1qr4y127s5?spm_id_from=333.999.0.0
三, 创建工程
在这当作你已经安装好最新版本DevEco-Studio开发工具, 点击File -> New -> New Project… 弹出Create HarmonyOS Project窗口, 这里我选择空白JS模板创建, 写界面还是JS比较方便些, 对于有一定前端知识的小伙伴来说.
四, 主界面开发
在展示源代码之前,先介绍一下使用到了JS哪些组件:滑动容器(swiper),视频播放(video),可滑动面板(panel),列表组件(list),图片组件(image),文本组件(text),交互式组件(input),按钮组件(button) 通过查看JS API参考文档,就可以做出你喜欢的视频应用了。
先介绍简单的1% Java代码,如果之前做过分布式Demo,直接复制过来就可以使用:
JAVA代码:
public class MainAbility extends AceAbility {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
// 动态判断权限
if (verifySelfPermission("ohos.permission.DISTRIBUTED_DATASYNC") != IBundleManager.PERMISSION_GRANTED) {
// 应用未被授予权限
if (canRequestPermission("ohos.permission.DISTRIBUTED_DATASYNC")) {
// 是否可以申请弹框授权(首次申请或者用户未选择禁止且不再提示)
requestPermissionsFromUser(new String[]{"ohos.permission.DISTRIBUTED_DATASYNC"}, 0);
}
}
}
@Override
public void onStop() {
super.onStop();
}
}
HML代码: 重要界面布局文件
<div class="container">
<swiper class="swiper" id="swiper" index="{{ continueAbilityData.currentIndex }}" indicator="false" loop="true" digital="false" vertical="true" onchange="changeSwiper">
<div class = "swiperContent" >
<video id='videoOne' src='{{ continueAbilityData.videoList[0] }}' muted='false' autoplay='true'ontimeupdate='timeupdateCallback' style="object-fit:fill; width:100%; height: 100%;" controls="false" onclick="change_start_pause_one" loop='true' starttime = '{{ ontinueAbilityData.timeupdatetime }}'></video>
</div>
<div class = "swiperContent">
<video id='videoTwo' src='{{ continueAbilityData.videoList[1] }}' muted='false' autoplay='false' ontimeupdate='timeupdateCallback' style="object-fit:fill; width:100%; height: 100%;" controls="false" onclick="change_start_pause_two" loop='true' starttime = '{{ ontinueAbilityData.timeupdatetime }}'></video>
</div>
<div class = "swiperContent">
<video id='videoThree' src='{{ continueAbilityData.videoList[2] }}' muted='false' autoplay='false' ontimeupdate='timeupdateCallback' style="object-fit:fill; width:100%; height: 100%;" controls="false" onclick="change_start_pause_three" loop='true' starttime = '{{ continueAbilityData.timeupdatetime }}'></video>
</div>
</swiper>
<div class="btn-footer">
<image class="comment-icon icon" src="/common/army_icon.jpg"></image>
<text class="footer-label">#HarmonyOS挑战赛第二期#</text>
<image class="comment-icon" src="/common/share.png" onclick="tryContinueAbility"></image>
<image class="comment-icon" src="/common/bxs-message.png" onclick="showPanel"></image>
</div>
<panel id="simplepanel" type="foldable" mode="half" miniheight="400px">
<div class="panel-div">
<list class="todo-wrapper">
<list-item for="{{continueAbilityData.todolist}}" class="todo-item">
<image class="todo-icon" src="/common/avatar04.png"></image>
<text class="todo-title">{{$item.title}}</text>
</list-item>
</list>
<div class="inner-btn">
<input id="input" class="input" type="text" value="{{continueAbilityData.comment}}" maxlength="20" enterkeytype="send" placeholder="请输入评论内容" onchange="changeValue" onenterkeyclick="enterkeyClick" style="margin-right: 10px;"></input>
<button type="capsule" value="关闭" onclick="closePanel"></button>
</div>
</div>
</panel>
</div>
JS代码: 重要逻辑代码,各组件事件
// @ts-nocheck
import app from '@system.app';
export default {
data: {
img: "resources/media/pic_tv.png",
continueAbilityData: {
currentIndex: 0,
videoList: [
"/common/000001.mp4",
"/common/000002.mp4",
"/common/000003.mp4"
],
timeupdatetime: 2,
isStart: true,
todolist: [
{title: 'HDC2021活动门票进行中'},
{title: '我期待HarmonyOS生态越来越完善'},
{title: 'HarmonyOS你值得拥有'}],
comment: ''
}
},
onInit() {
},
changeSwiper(e) {
console.info("onRestoreData:changeSwiper");
this.switchPlay(e.index);
},
switchPlay(index) {
console.info("onRestoreData:onShow <> " + index);
this.continueAbilityData.currentIndex = index;
if(index == 0) {
this.$element('videoOne').start();
this.$element('videoTwo').pause();
this.$element('videoThree').pause();
console.info("onRestoreData:videoOne <> start " + this.$element('videoOne').starttime);
}else if(index == 1) {
this.$element('videoOne').pause();
this.$element('videoTwo').start();
this.$element('videoThree').pause();
console.info("onRestoreData:videoTwo <> start " + this.$element('videoTwo').starttime);
}else if(index == 2) {
this.$element('videoOne').pause();
this.$element('videoTwo').pause();
this.$element('videoThree').start();
console.info("onRestoreData:videoThree <> start " + this.$element('videoThree').starttime);
}
},
//流转事件
tryContinueAbility: async function() {
// 应用进行迁移
let result = await FeatureAbility.continueAbility();
console.info("result:" + JSON.stringify(result));
},
onStartContinuation() {
// 判断当前的状态是不是适合迁移
console.info("onStartContinuation");
return true;
},
onCompleteContinuation(code) {
// 迁移操作完成,code返回结果
console.info("CompleteContinuation: code = " + code);
app.terminate();
},
onSaveData(saveData) {
// 数据保存到savedData中进行迁移。
var data = this.continueAbilityData;
console.info("onSaveData:" + JSON.stringify(data));
Object.assign(saveData, data)
},
onRestoreData(restoreData) {
console.info("onRestoreData:" + JSON.stringify(restoreData));
// 收到迁移数据,恢复。
this.continueAbilityData = restoreData;
var currentIndex = this.continueAbilityData.currentIndex;
var currentTime = this.continueAbilityData.timeupdatetime;
this.$element('videoOne').pause();
this.$element('videoTwo').pause();
this.$element('videoThree').pause();
this.$element('videoOne').starttime = currentTime;
this.$element('videoTwo').starttime = currentTime;
this.$element('videoThree').starttime = currentTime;
this.switchPlay(currentIndex);
},
//评论事件
showPanel() {
this.$element('simplepanel').show()
},
closePanel() {
this.$element('simplepanel').close()
},
changeValue(e) {
this.continueAbilityData.comment = e.value;
},
enterkeyClick(e) {
this.continueAbilityData.todolist.push({title: this.continueAbilityData.comment});
this.continueAbilityData.comment = "";
},
timeupdateCallback:function(e){ this.continueAbilityData.timeupdatetime = e.currenttime;},
change_start_pause_one: function() {
if(this.continueAbilityData.isStart) {
this.$element('videoOne').pause();
this.continueAbilityData.isStart = false;
} else {
this.$element('videoOne').start();
this.continueAbilityData.isStart = true;
}
},
change_start_pause_two: function() {
if(this.continueAbilityData.isStart) {
this.$element('videoTwo').pause();
this.continueAbilityData.isStart = false;
} else {
this.$element('videoTwo').start();
this.continueAbilityData.isStart = true;
}
},
change_start_pause_three: function() {
if(this.continueAbilityData.isStart) {
this.$element('videoThree').pause();
this.continueAbilityData.isStart = false;
} else {
this.$element('videoThree').start();
this.continueAbilityData.isStart = true;
}
}
}
CSS代码: 重要化妆技术
.container {
width: 100%;
height: 100%;
flex-direction: column;
}
.img {
object-fit: cover;
background-color: #808080;
}
.swiper {
flex-direction: column;
align-content: center;
align-items: center;
width: 100%;
height: 100%;
background-color: black;
}
.swiperContent {
height: 100%;
justify-content: center;
background-color: black;
}
.btn-footer {
height: 60px;
line-height: 60px;
width: 100%;
background-color: black;
flex-direction: row;
}
.footer-label {
font-size: 16px;
color: white;
padding-top: 0px;
flex-weight: 1;
line-height: 20px;
}
.comment-icon {
width: 32px;
height: 32px;
margin: 8px;
}
.icon {
border-radius: 16px;
}
.panel-div {
flex-direction: column;
align-content: center;
align-items: center;
width: 100%;
height: 100%;
}
.inner-btn {
height: 70px;
padding: 10px;
}
.todo-wrapper {
width: 100%;
height: 100%;
}
.todo-item {
width: 100%;
height: 30px;
padding-left: 10px;
padding-right: 10px;
}
.todo-icon {
width: 16px;
height: 16px;
margin-top: 10px;
margin-right: 10px;
}
.todo-title {
width: 100%;
height: 100%;
text-align: left;
font-size: 14fp;
}
代码就写到此了,不要忘记了config.json文件的权限配置哦,在module下添加
"reqPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
}
]
五, 总结
说实存的,当看到这个活动时间才几天时,感觉时间不太够用,要滑动视频,要评论功能,要分布式的,加上都是用空闲时间来做的,然而当深入去理解相关组件用法后,发现应该一天时间就可以了。
有兴趣的小伙伴可以下载源码查看, 项目代码写得还不算靓仔, 很多为了实现效果,都是Hardcode的,有空可以把重复代码抽出来,视频源也可以放到服务器上,然后就可以更愉快的刷更多视频了,源码同步到gitee码云,项目素材没有上传,自行添加。
纯JS分布式视频播放应用,没得说,超棒的。
代码量也超级少了,真的是程序员注重逻辑就可以了。
完美🥰 🥰