如何在实际图像加载到Ionic 2或3应用程序之前显示占位符图像

时间:2017-09-29 10:34:25

标签: angular typescript ionic-framework ionic2 ionic3

美好的一天,

我正在开发一个Ionic应用程序,我正在从JSON提要中动态加载图像。我希望当从外部URL中提取图像时,占位符图像显示在其中,此后,占位符图像一旦被加载就应该被“真实”图像替换。

我目前的情况是:

<ion-card *ngFor="let item of items"...
 <img [src]="item.picture" />

谢谢&amp;问候。

5 个答案:

答案 0 :(得分:16)

大多数答案都是关于在同一视图中添加加载检查的逻辑,但似乎不合适。相反,您可以创建自己的指令来处理它。您可以查看 this amazing article ,看看如何完成。

首先在src/assets文件夹中复制 this GIF ,并将其命名为preloader.gif

然后添加此自定义指令。代码几乎是不言自明的:

// An image directive based on http://blog.teamtreehouse.com/learn-asynchronous-image-loading-javascript
import {Directive, Input, OnInit} from '@angular/core';

// Define the Directive meta data
@Directive({
  selector: '[img-preloader]', //E.g <img mg-img-preloader="http://some_remote_image_url"
  host: {
    '[attr.src]': 'finalImage'    //the attribute of the host element we want to update. in this case, <img 'src' />
  }
})

//Class must implement OnInit for @Input()
export class ImagePreloader implements OnInit {
  @Input('img-preloader') targetSource: string;

  downloadingImage : any; // In class holder of remote image
  finalImage: any; //property bound to our host attribute.

  // Set an input so the directive can set a default image.
  @Input() defaultImage : string = 'assets/preloader.gif';

  //ngOnInit is needed to access the @inputs() variables. these aren't available on constructor()
  ngOnInit() {
    //First set the final image to some default image while we prepare our preloader:
    this.finalImage = this.defaultImage;

    this.downloadingImage = new Image();  // create image object
    this.downloadingImage.onload = () => { //Once image is completed, console.log confirmation and switch our host attribute
      console.log('image downloaded');
      this.finalImage = this.targetSource;  //do the switch 
    }
    // Assign the src to that of some_remote_image_url. Since its an Image Object the
    // on assignment from this.targetSource download would start immediately in the background
    // and trigger the onload()
    this.downloadingImage.src = this.targetSource;
  }

}

然后将新指令添加到您的app模块,如下所示:

import { NgModule, ErrorHandler } from '@angular/core';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { MyApp } from './app.component';
import { ImagePreloader } from '../components/img-preload/img-preload';

@NgModule({
  declarations: [
    MyApp,
    ImagePreloader, // <------- Here!
    // ...
  ],
  imports: [
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    // ...
  ],
  providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}]
})
export class AppModule {}

这是您使用新自定义指令的方法:

<img img-preloader="https://images.unsplash.com/photo-1413781892741-08a142b23dfe" alt="">

这就是它的样子:

Result

然后您可以设置图像容器的高度/宽度,因此预加载器gif和最终图像都具有相同的尺寸。

答案 1 :(得分:0)

希望您可以尝试如下所示。您可以将占位符图像存储在assets/images文件夹中。

html的

<ion-card *ngFor="let item of items">
 <img [src]="item?.picture!= null ? item.picture : myImgUrl" />
</ion-card>

.TS

export class MyPage{
   myImgUrl:stirng='./assets/images/myimage.png;

   constructor(){}
}

答案 2 :(得分:0)

你能修改你的JSON以获得loaded布尔属性吗?如果你能做到这一点很简单,那么就这样做:

<ion-card *ngFor="let item of items">
  <img [src]="item.picture" (load)="item.loaded = true" [hidden]="!item.loaded" />
  <img src="../path/to/your/placeholer/image.jpg" [hidden]="item.loaded">
</ion-card>

您的加载属性始终被初始化为false,当图像加载时,将触发(load)事件并将图像加载属性更改为true,隐藏占位符图像并显示已加载IMAGEM。

如果您无法更改此JSON(或者如果它太大而无法编辑),您可以使用上述代码,但请在页面.ts

中执行以下操作
public functionWhoseIsLoadingYourJson(){
  // HTTP REQUEST FOR JSON
  .subscribe(response => {
    response.forEach(element => {
      element.loaded = false;
    });
    this.items = response;
  });
}

只需迭代您的响应,并在每个对象中将加载的属性设置为false。如果这会引发一些错误,您可以将<ion-card>置于带有* ngIf

的div中
<div *ngIf="this.items != null">
  <!-- ion card -->
</div>

编辑 - 更新JSON

由于它来自Firebase,因此有三个选项可用于更新您的JSON。第一个是一个函数,你只执行一次更新数据库,第二个仍然是我给出的代码(迭代结果并推送布尔值)但是因为你使用的是Firebase所以它会有所不同。

第一个解决方案:

在代码中的某个地方,它可以是任何页面,您只需要执行一次:

firebase.database().ref('myDataNode').on('value', snapshot => {
  for(let key in snapshot.val()){
    firebase.database().ref('myDataNode/' + key + '/loaded').set(false);
  }
});

看到我正在循环浏览myDataNode中的所有结果并使用其密钥将loaded属性设置为false。但是在使用set时要小心,因为您可以覆盖所有节点,在执行此方法之前,请记住导出Firebase节点以进行备份。如果您要将代码从代码中的任何位置推送到Firebase,请记住将其更改为同时设置loaded属性。

如上所述,第二个是我在这个答案中所做的,但现在在Firebase调用中使用它并使用随快照附带的forEach方法:

firebase.database().ref('myDataNode').on('value', snapshot => {
  snapshot.forEach(element =>{
    this.myDataNode.push({
      picture: element.val(), // maybe it needs to be element.val().nameOfYourProperty
      loaded: false
    })
  });
});

这将使用图片网址和已加载的属性将新元素推送到您的数组。如果它无法识别.push()方法,只需声明您的变量类型为数组:

public myDataNode: any[];

第三个是本地更新,通过您的firebase快照进行更新,将结果保存在本地变量中,创建一个新属性并将其推送到您的阵列:

firebase.database().ref('myDataNode').on('value', snapshot => {
  for (let key in snapshot.val()){
    snapshot.forEach(item => { // iterate snapshot
      let record = item.val(); // save the snapshot item value in a local variable
      record.loaded = true; // add the property
      this.newArray.push(record); // push it to your array that'll  be used in ng-for
      return true; // return true to foreach
    });
  }
});

希望这有帮助。

答案 3 :(得分:0)

据我所知,没有离子方法可以做到这一点。但你仍然可以通过javascript和css来实现。

CSS方式:

创建一个div覆盖您的图像,并将占位符图像设置为该div的.HandleEventWithThisMethod

background-image

Javascript方式:

你可以在stackoverflow中轻松找到javascript的几种方法,所以我不会在这里重新回答。 {{3}}就是一个例子

答案 4 :(得分:-2)

试试这个:

.TS

export class MyPage {

  placeHolder:string = './assets/img/placeholder.png';

  constructor(){}
}

html的

   <img [src]=item.image ? item.image : placeHolder>