前文再续,书接上一回,上上回说到自定义组件,本节应该讲怎么做自定义组件的同时怎么调用的,只是本实例应做自定义组件的有好几个,不可能每个都写出来讲解,这会显得冗余了,所以决定只把其中一个较有代表性的做成自定义组件。在写这个组件前,我们按照页面的顺序先逐个实现。不知道一个小时能写完不,赶上班…
仍然是先上UI设计图:
image.png
观察之,发布者和内容的文本信息可以用ionic现有的组件实现,而视频播放和图片画廊(如果是轮播图可以用slides组件,它是阉割并封装过的swiper,好像现在这样的效果不能实现)需要使用第三方组件。步骤如下:
一、准备素材:
鸡蛋两只,油两茶匙……呃,说错,应该是这些:
- 食物照片;
- 人物头像;
- 视频封面;
- 视频;
这些可以上素材网下,也可以简单点找度娘(不过小心图片版权问题):
百度食物图片
如果是远程地址则先保存地址,如果是放本地的就在src/assets
里面找个地方放,呆会要把地址作为数据一部分,我这选择存放本地。
二、准备数据:
我仍是图省事,这里采用本地数据,在src/assets
里面新建data
文件夹,再在里面建一个friend-news.json
文件,为了便于观察,整理了内容如下:
{
"success": "true",
"result":[{
"headImg": "assets/imgs/girl2.jpg",
"name": "Amy Hinez", "createTime": "May 24,2016", "content": "Some content", "favorites": "1225", "comments": "198",
"type":"0",
"cover": "",
"medias":[
{"id": "1", "src": "../assets/imgs/foods/1.jpg"},
{"id": "2", "src": "../assets/imgs/foods/2.jpg"},
{"id": "3", "src": "../assets/imgs/foods/3.jpg"},
{"id": "4", "src": "../assets/imgs/foods/4.jpg"}
]
},{
"headImg": "../assets/imgs/girl2.jpg",
"name": "Amy Hinez", "createTime": "May 24,2016", "content": "Some content", "favorites": "1225", "comments": "198",
"type":"1",
"cover": "../assets/imgs/foods/cover1.jpg",
"medias":[
{"id": "1", "src": "../assets/data/oceans.mp4"}
]
},{
"headImg": "../assets/imgs/girl2.jpg",
"name": "Amy Hinez", "createTime": "May 24,2016", "content": "Some content", "favorites": "1225", "comments": "198",
"type":"0",
"cover": "",
"medias":[
{"id": "1", "src": "../assets/imgs/foods/1.jpg"},
{"id": "2", "src": "../assets/imgs/foods/2.jpg"},
{"id": "3", "src": "../assets/imgs/foods/3.jpg"},
{"id": "4", "src": "../assets/imgs/foods/4.jpg"}
]
}]
}
这类似一个服务接口返回来的数据,success属性是为了和上一节的基本网络服务格式一致而设定的属性,type属性为0表示画廊,1为视频,其它的应该从字段名称大致知道是什么东西吧?
接着我们执行命令新建一个HomeProvider用来统一管理首页的数据处理方法(这里按页面逻辑来划分的,当然你也可以按业务类型来划分):
代码语言:javascript复制ionic g provider home
命令执行完成,打开文件写入一个方法:
代码语言:javascript复制 getFriendNews(){
//第二个参数为false表示使用相对路径
return this.commonProvider.get("../assets/data/friend-news.json", false);
}
最后记得在app.module.ts
里面的providers里添加配置它,至此数据就准备好了。
三、安装视频播放组件
用的是videogular2,可参考我另一篇文章:《【技巧】ionic3视频播放》
四、安装swiper组件
代码语言:javascript复制npm install swiper --save
四、实现首页
打开home.ts文件,修改内容为:
代码语言:javascript复制import { Component, ViewChildren, ChangeDetectorRef } from '@angular/core';
import { NavController } from 'ionic-angular';
import { HomeProvider } from '../../providers/home/home';
import Swiper from 'swiper';
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
frendNews: any[] = [];
constructor(public navCtrl: NavController, private cd: ChangeDetectorRef, private homeProvider: HomeProvider) {
}
ionViewDidLoad(){
this.getFriendNews();
}
//获取数据
getFriendNews(){
this.homeProvider.getFriendNews().then((res: any)=>{
if(res.success){
this.frendNews = res.result;
this.cd.detectChanges();
this.initSwiper();
}else{
//如提示框等错误提示
console.log(res.msg);
}
});
}
//初始化Swiper
initSwiper(){
new Swiper('.swiper-container', {
paginationClickable: true,
slidesPerView: 2,
width: 280,
spaceBetween : 10,
watchActiveIndex: true,
initialSlide: 0,//初始化显示第几个
zoom: true,//双击,手势缩放
loop: false,//循环切换
lazyLoading: true,//延迟加载
lazyLoadingOnTransitionStart: true,
lazyLoadingInPrevNext : true
});
}
}
代码看上去长,其实没啥内容,其中图片画廊这个效果有点特别,要显示后一张的一部分,好让别人知道后面还有,所以参数要特别设定下。initSwiper方法貌似要在页面渲染完成后才能使用,而getFriendNews获取数据后未必渲染完成,故不能马上调用,所以调用
this.cd.detectChanges()
来处理下,关于这个可以查看我另一篇文章。此外getFriendNews没有后续操作,所以异步调用省掉return。
打开home.html文件,把<ion-content>的内容更改为下面内容:
代码语言:javascript复制<ion-content>
<ion-list>
<div *ngFor="let item of frendNews">
<!-- 发布人信息 -->
<ion-item>
<ion-avatar item-start>
![]({{item.headImg}})
</ion-avatar>
<h2><strong><span color="dark">{{item.name}}</span></strong></h2>
<ion-note item-right>{{item.createTime}}</ion-note>
</ion-item>
<!-- 视频播放器 -->
<vg-player class="video-container" *ngIf="item.type == 1; else elseBlock">
<vg-overlay-play></vg-overlay-play>
<vg-buffering></vg-buffering>
<vg-controls>
<vg-play-pause></vg-play-pause>
<vg-playback-button></vg-playback-button>
<vg-time-display vgProperty="current" vgFormat="mm:ss"></vg-time-display>
<vg-time-display vgProperty="left" vgFormat="mm:ss"></vg-time-display>
<vg-time-display vgProperty="total" vgFormat="mm:ss"></vg-time-display>
<vg-mute></vg-mute>
<vg-volume></vg-volume>
<vg-fullscreen></vg-fullscreen>
</vg-controls>
<video #myMedia [vgMedia]="myMedia" class="video-js vjs-default-skin vjs-fluid" height="400" width="600" preload="auto" poster="{{item.cover}}"
crossorigin playsinline webkit-playsinline>
<source *ngFor="let cItem of item.medias" src="{{cItem.src}}" type="video/mp4">
</video>
</vg-player>
<!-- 画廊组件 -->
<ng-template #elseBlock>
<div class="swiper-container">
<div class="swiper-wrapper">
<div class="swiper-slide" *ngFor="let cItem of item.medias">
![]({{cItem.src}})
</div>
</div>
</div>
</ng-template>
<p padding-left padding-right>{{item.content}}</p>
<!-- 按钮组 -->
<div class="item-block">
<div>
<button ion-button clear icon-left small outline (click)="onTest()">
<ion-icon name="heart" ></ion-icon>
<div>{{item.favorites}}</div>
</button>
<button ion-button clear icon-left small>
<ion-icon name="text-outline"></ion-icon>
<div>{{item.comments}}</div>
</button>
</div>
<button ion-button clear icon-only small item-right>
<ion-icon name="more"></ion-icon>
</button>
</div>
<!-- 分隔符 -->
<div class="item-divider-sm"></div>
</div>
</ion-list>
</ion-content>
看上去内容也很多,但按注释看每个部分的话就相对好理解些了。用了结构指令
ngIf
来选择显示视频还是画廊;按钮组可以换用grid布局;此外,为了性能考虑,应用virtualScroll
,但我简单一用时布局有变形,赶时间没分析,换用现在方式。
打开home.scss文件,修改如下:
代码语言:javascript复制page-home {
.swiper-slide img {
height: 120px;
}
}
五、微调
- variables.scss文件修改文字颜色和稍微调大头像:
$list-ios-text-color: color($colors, gray);
$item-ios-avatar-size: 42px;
- app.scss里添加分隔栏的样式
.item-divider-sm {
height: 10px;
background-color: color($colors, light-gray);
}
最后看下实际效果图:
实际效果图
因为赶着上班,写得有点急,微调没怎么调,特别是swiper的参数配置中的宽高有点诡异,还没摸清,另外说明内容还要补充一下,晚些再完善。