1. 程式人生 > >Ionic2中使用iframe製作一個頁面“瀏覽器”

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在Android下的效果 ThemeableBrowser在iOS下的效果
  使用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使用效果

  開啟一個頁面:
使用BrowserPage載入一個頁面

  開啟選單:
使用BrowserPage開啟選單

  呼叫分享,分享頁面連結:
使用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。那就會出現這樣的問題了。