如何在 Ionic 2 或 3 应用程序中加载实际图像之前显示占位符图像
Posted
技术标签:
【中文标题】如何在 Ionic 2 或 3 应用程序中加载实际图像之前显示占位符图像【英文标题】:How to show a placeholder image before the actual image loads in an Ionic 2 or 3 app 【发布时间】:2018-03-11 06:01:53 【问题描述】:早安,
我正在开发一个 Ionic 应用程序,我正在从 JSON 提要动态加载图像。我希望当从占位符图像显示在其位置的外部 URL 中提取图像时,占位符图像应在加载后被“真实”图像替换。
我目前情况的一个例子是:
<ion-card *ngFor="let item of items"...
<img [src]="item.picture" />
感谢和问候。
【问题讨论】:
你可以给item.picture
(你的占位符图像src)一个默认值,当图像准备好时用实际值替换它。
@Kaddath 说起来容易做起来难 :) 你能证明你的方法吗,所以我能完全理解吗?谢谢!
你可以看看 sampath 的回答,我在之前的项目中做了几乎相同的事情,除了我有一个控制器函数作为 src 值(这个函数和他做的一样,检查图片是否有一个值,如果不是,则返回默认 src)。在我的情况下,我正在下载图片,并且异步响应只是在文件路径已知时设置值。当值改变时显示应该自动更新
你能解决这个@TCRoberts 吗?
@sebaferreras 还没有。我没有测试这些方法。我很快就会在我的项目中进入这一部分。我不得不在我的项目中退后几步
【参考方案1】:
您能否修改您的 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,隐藏占位符图像并显示加载的图像。
如果您无法更改此 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
)
);
);
这将使用图片 url 和加载的属性将一个新元素推送到您的数组。如果它无法识别.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
);
);
希望这会有所帮助。
【讨论】:
是的,我可以修改我的 JSON 文件。这是我当前的 JSON 结构,如何修改它以具有loaded
布尔属性? "picture" : "my/picture/url"
@TCRoberts 您有两个选择:您可以在 API/数据库中修改 JSON 的每个值,也可以按照我的代码进行操作。当您从 http 调用中获得响应时,您可以使用 forEach 循环对其进行迭代,并在每个元素中插入加载的属性,并将其设置为 false。
@TCRoberts 你如何得到你的 JSON?是http调用吗?它在服务中?在资产文件中?如果您发布如何获取 JSON,我可以更新我的答案并向您展示如何修改。
您好,我正在通过 Firebase 获取我的 JSON 数据。在我的ts file
我有我的导入import firebase from 'firebase';
然后在我的constructor
我有:firebase.database().ref('myDataNode').on('value', snapshot => this.myDataNode = snapshot.val(); );
并且我在我的页面export class
下有myDataNode = [];
的属性myDataNode = [];
- 你帮助我进行了检索不久前来自 Firebase 的数据(如果你还记得的话),所以我仍在关注你关于检索 JSON 数据的方法。那里一切都很好。
哦,对不起,我不记得帮助过你,但我很高兴我帮助过你。更新了我的答案。【参考方案2】:
大部分答案都是关于在同一视图中添加已加载检查的逻辑,但似乎不合适。相反,您可以创建自己的指令来处理它。您可以查看 this amazing article 了解如何操作。
首先将this GIF复制到您的src/assets
文件夹中,并将其命名为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;
然后将新指令添加到您的应用模块中,如下所示:
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" >
这就是它的样子:
然后您可以设置图像容器的高度/宽度,因此预加载器 gif 和最终图像具有相同的尺寸。
【讨论】:
如何将第二个参数传递给该指令?例如像下面这样,你想在 onError 事件的情况下添加另一个图像:<img img-preloader="https://images.unsplash.com/photo-1413781892741-08a142b23dfe" [onErrorImage]="assets/error.png" alt="">
@Ari 你只需要在指令中再添加一个@Input('onErrorImage') errorImgSource: string;
,然后在出现错误时使用:this.downloadingImage.onerror = () => this.finalImage = this.errorImgSource;
哥们你太棒了!
谢谢一百万!这很简洁,太有用了!
我们可以为 Ionic 1 也这样做吗?【参考方案3】:
据我所知,没有 Ionic 方法可以做到这一点。但是你仍然可以通过 javascript 和 css 来实现。
CSS方式:
创建一个 div 覆盖您的图像并将占位符图像设置为该 div 的background-image
。
<div class="image">
<img src="your_main_image"/>
</div>
.image
background-image: url('your_placeholder_image');
Javascript方式:
您可以在***中轻松找到几种javascript方法,因此我不会在这里重新回答。 This one 就是一个例子
【讨论】:
【参考方案4】:试试这个:
.ts
export class MyPage
placeHolder:string = './assets/img/placeholder.png';
constructor()
.html
<img [src]=item.image ? item.image : placeHolder>
【讨论】:
【参考方案5】:希望您可以尝试如下所示。您可以将占位符图像存储在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()
【讨论】:
这会按预期工作吗?因为(我认为)总会有一个图像item?.picture
将永远是!= null。然后它会获取图像 url 并开始加载,并且永远不会显示占位符图像。以上是关于如何在 Ionic 2 或 3 应用程序中加载实际图像之前显示占位符图像的主要内容,如果未能解决你的问题,请参考以下文章
在 Ionic 应用程序中加载 Cloudfront 图像时出错 - iOS 9