Ionic2中使用iframe製作一個頁面“瀏覽器”
目錄
前言
我們會遇到這樣的一個需求:像是在 Ionic2 中載入一個網頁,如:在Ionic2中載入一個H5頁面,頁面的內容可以為資訊的詳情或者活動詳情等等。那麼在Ionic2中就需要有這樣一個東西,用來載入H5頁面。在Android原生中有WebView。那在Ionic2中有什麼呢?
2017-07-21 補充:注意,文中後續採用iframe方式實現模擬了一個“瀏覽器”。但是實際使用過程中,如果iframe載入的頁面遇到 target=”_top” 的話可能會跳出APP。如點選含target="_top"
的連結。
ionic-native
在Ionic2中,ionic-native包中提供了,如 InAppBrowser 和 ThemeableBrowser 等這兩個cordova外掛用來開啟一個“WebView”類似的視窗,用來載入頁面。
ThemeableBrowser的使用
如使用ThemeableBrowser外掛,開啟一個新的視窗載入頁面,效果如下:
使用ThemeableBrowser可以參考,我寫過的一篇文章《 ionic2中ThemeableBrowser外掛的使用——App內嵌瀏覽器》。
ThemeableBrowser的缺點
1. 使用ThemeableBrowser時,在定義頂部標題欄樣式的時候顯得繁瑣,不容易定義與當前APP主題一致de樣式;
2. 瀏覽器除錯時無法呼叫cordova外掛,需要真機執行。
3. 在標題欄新增一些自定義的選單項也顯得不那麼容易。
使用iframe仿瀏覽器載入頁面
為了讓這個iframe變得更加通用,同時更像一個“瀏覽器”。在這裡,在Ionic2中定義一個BrowserPage頁面,我們在開啟該頁面的時候需要傳遞browser的配置引數,配置如:頁面標題、訪問的頁面連結、定義分享配置等。
在這個瀏覽器中定義了一個popover選單,預設只擁有 “重新整理”(重新整理頁面)、“關閉”(關閉當前頁面)的功能。
browser.html
這裡定義了“瀏覽器”的頁面,其中包含了載入頁面時顯示的進度條(可以說是偽進度條,沒有計算真正訪問頁面時候的進度。)
<ion-header no-shadow >
<ion-navbar class="page-navbar">
<ion-title>{{browser.title}}</ion-title>
<ion-buttons end>
<button ion-button icon-only (click)="presentPopover($event)">
<ion-icon name="more"></ion-icon>
</button>
</ion-buttons>
</ion-navbar>
</ion-header>
<ion-content class="content"> <!--scroll="true" overflow-scroll="true"-->
<div class="progress" [hidden]="browser.isLoaded">
<div class="progress-inner" id="progress"></div>
</div>
<iframe id="iframe" class="iframe"
sandbox="allow-scripts allow-top-navigation allow-pointer-lock allow-same-origin allow-popups allow-forms"
[src]="browser.secUrl"
(load)="loaded()">
</iframe>
</ion-content>
<panel-share [(isShow)]="shareConfig.isShow" [share]="browser.share"></panel-share>
注:這裡定義了一個 panel-share 元件,用於顯示分享功能(如:微信分享、朋友圈分享、QQ分享等)。使用者可以自己定義 點選選單欄上“分享”後顯示的分享元件。這裡只是舉個栗子。
browser.scss
定義browser的樣式,進度條樣式。
page-browser {
$progress-height: 0.2rem;
//$progress-bg: #d43f3a;
//$progress-bg: linear-gradient(-45deg, #333 100%, #324512 60%,rgba(255,255,255,0.5) 0%);
//$progress-bg: linear-gradient(left, #5bd8ff, #ff0000);
//$progress-bg: linear-gradient(-45deg, rgba(255,255,255,0.5)0%, #324512 60%,#333 100%);
$progress-bg: #77b6ff;
/*.scroll-content {
overflow: hidden;
}*/
.content {
height: 100%;
}
.progress{
position: absolute;
top: 0;
right: 0;
left: 0;
height: $progress-height;
background: #f5f5f5;
z-index: 200;
.progress-inner{
width: 0;
background: $progress-bg;
position: absolute;
top: 0;
left: 0;
bottom: 0;
box-shadow: 0 0 10px rgba(119,182,255,0.7);
-webkit-transition: width 0.4s ease;
transition: width 0.4s ease;
}
}
.iframe {
width: 100%;
height: 100%;
position: absolute;
overflow: auto;
border: none;
}
}
browser.ts定義瀏覽器功能
主要是載入頁面時顯示進度條、重新整理頁面、分享回撥(如果開啟browser頁面時,傳入的分享的配置引數)等功能。
/**
* Created by DreamBoy on 2016/11/21.
*/
import { Component } from '@angular/core';
import { NavController, NavParams, PopoverController } from 'ionic-angular';
import { DomSanitizer } from "@angular/platform-browser";
import { BrowserPopoverPage } from "./browser-popover";
@Component({
selector: 'page-browser',
templateUrl: 'browser.html'
})
export class BrowserPage {
browser: any = {
isLoaded: false, // 網頁是否被載入
proObj: null, // 進度條物件
progress: 0, // 網頁訪問的進度條
secUrl: '', // 安全連結
title: '載入中',
url: '',
share: null // 是否具有分享功能(傳遞一個分享物件ShareModel過來)
};
shareConfig: any = {
isShow: false
}; // 分享控制的配置
constructor(public navCtrl: NavController,
private params: NavParams,
private sanitizer: DomSanitizer,
private popoverCtrl: PopoverController) {
let browser = this.params.get('browser');
if(browser) {
this.browser.title = browser.title;
this.browser.url = browser.url;
this.browser.secUrl = this.sanitizer.bypassSecurityTrustResourceUrl(browser.url);
if(browser.share) {
this.browser.share = browser.share;
}
} else {
this.browser.secUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.browser.url);
}
this.reload();
}
ionViewDidLoad() {
if(!this.browser.proObj) {
this.browser.proObj = document.getElementById('progress');
}
this.onprogress();
}
// 生成隨機數
private random(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1) + min);
}
// 網頁訪問進度
private onprogress() {
// 隨機時間
let timeout = this.random(10, 30);
let timer = setTimeout(() => {
if(this.browser.isLoaded) {
this.browser.proObj.style.width = '100%';
clearTimeout(timer);
return;
}
// 隨機進度
this.browser.progress += this.random(1, 5);
// 隨機進度不能超過 90%,以免頁面還沒載入完畢,進度已經 100% 了
if(this.browser.progress > 90){
this.browser.progress = 90;
}
this.browser.proObj.style.width = this.browser.progress + '%';
this.onprogress();
}, timeout);
}
// 如果iframe頁面載入成功後
loaded() {
this.browser.isLoaded = true;
}
// 顯示Popver選項
presentPopover(myEvent) {
let cb = {
refresh: () => {
this.reload();
},
close: () => {
this.navCtrl.pop();
},
share: null
};
if(this.browser.share) {
cb.share = () => {
this.share();
}
}
let popover = this.popoverCtrl.create(BrowserPopoverPage, {
callback: cb
});
popover.present({
ev: myEvent
});
}
// 重新載入頁面
reload() {
let title = this.browser.title;
let url = this.browser.secUrl;
this.browser.title = '載入中';
this.browser.secUrl = this.sanitizer.bypassSecurityTrustResourceUrl('');
setTimeout(() => {
this.browser.isLoaded = false;
this.browser.progress = 0;
if(!this.browser.proObj) {
this.browser.proObj = document.getElementById('progress');
}
this.onprogress();
this.browser.title = title;
this.browser.secUrl = url;
}, 10);
}
// 分享頁面(作為popover的回撥)
share() {
this.shareConfig.isShow = true;
/*if(this.browser.share) {
SocialSharing.share(this.browser.share.title, this.browser.share.content, '', this.browser.share.url).then(() => {
}, (err) => {
// Error!
alert('錯誤:分享失敗!' + err);
});
}*/
}
}
browser-popover.ts選單項
點選對應的功能,呼叫 browser.ts 中的方法。
/**
* Created by admin on 2016/11/22.
*/
import { Component } from '@angular/core';
import { ViewController, NavParams } from "ionic-angular";
@Component({
template: `
<ion-list>
<button ion-item detail-none (click)="refresh()">重新整理</button>
<button ion-item detail-none (click)="share()" *ngIf="parentCallback.share">分享</button>
<button ion-item detail-none (click)="close()">關閉</button>
</ion-list>
`
})
export class BrowserPopoverPage {
parentCallback: {refresh: () => void, share?: () => void, close: () => void};
constructor(public viewCtrl: ViewController,
private navParams: NavParams) {
this.parentCallback = this.navParams.data.callback;
}
// 重新整理
refresh() {
this.parentCallback.refresh();
this.viewCtrl.dismiss();
}
// 分享
share() {
this.parentCallback.share();
this.viewCtrl.dismiss();
}
close() {
this.viewCtrl.dismiss();
this.parentCallback.close();
}
}
定義 ShareModel 模型
這裡的share模型主要用於呼叫分享時作為傳入browser的引數的型別。
/**
* Created by admin on 2017/1/18.
*/
export class ShareModel { // 分享
title: string; // 標題
banner: string; // 標語提示
descr: string; // 描述
thumb: string; // 顯示的縮圖
url: string; // 連結
}
BrowserPage的使用示例
基本使用:
this.navCtrl.push(this.browserPage, {
browser: {
title: '頁面名稱',
url: '這裡放置訪問的頁面連結'
}
});
新增“分享”功能:
let share: ShareModel = new ShareModel();
share.title = '標題';
share.banner = '標語提示';
share.thumb = '縮圖';
share.descr = '描述';
share.url = '分享的連結';
this.navCtrl.push(this.browserPage, {
browser: {
title: '頁面名稱',
url: '這裡放置訪問的頁面連結',
share: share
}
});
BrowserPage使用效果
開啟一個頁面:
開啟選單:
呼叫分享,分享頁面連結:
注意事項
在iOS中iframe載入不了網頁的問題(iframe白屏)
需要在config.xml新增“allow-navigation”,允許設定多個域下的訪問,如(下面這個是初始專案給出的栗子):
<allow-navigation href="http://ionic.local/*"/>
通用的寫法有(當前安全性就低一些咯,可以設定允許訪問的幾個域):
<allow-navigation href="*" />
在iOS中iframe載入頁面後頁面不可滾動的問題
在iframe的容器,如div,增加一個 -webkit-overflow-scrolling: touch;
屬性。如:
.scroll-wrapper {
-webkit-overflow-scrolling: touch;
overflow-y: scroll;
/* 提示: 請在此處加上需要設定的大小(dimensions)或位置(positioning)資訊! */
}
.scroll-wrapper iframe {
/* 你自己指定的樣式 */
}
在Ionic2中的.scroll-content類樣式包含了 -webkit-overflow-scrolling: touch;
屬性,所以不會出現這樣的問題(頁面不可滾動的問題)。那如果我們定義 .scroll-content 的overflow屬性為 hidden。那就會出現這樣的問題了。