【问题标题】:Angular 2 - Preload background image?Angular 2 - 预加载背景图片?
【发布时间】:2016-09-23 08:38:51
【问题描述】:

我有一个有角度的项目,我有一个填充页面的大背景图像和一个带有链接的简单侧边栏,单击该链接时,将使用另一个图像(来自 cdn)更改背景的 url。由于这些图像相当大,它们需要一两秒钟才能加载并且很明显,我想添加一个预加载器,但我不确定如何在 angular 2 中完成。

在我的 html 中我有这个:

<section class="fullsizebg image-bg" [ngStyle]="{'background-image': 'url(' + urlImage + ')'}"></section>

变量 urlImage 填充在组件的构造函数中,侧边栏链接通过一个简单的函数在点击时更改它的值,如下所示:

generateImage(data: any){
    this.urlImage = 'http://example.com/mycdn/'+this.data.url+'.jpg';
}

所以网址实际上是立即更改的,但图像需要一些时间来加载。我想添加一个加载 gif 或类似的东西,以保持图像对用户的平滑变化,而不是像现在这样跳跃。

【问题讨论】:

    标签: javascript angular


    【解决方案1】:

    一种方法是使用Blob 获取图像并将其存储在img 组件中,这样您就可以亲自参与加载过程并添加加载gif:

    @Component({
       selector:'loading-image',
       template:'<img alt="foo" [src]="src"/>'
    })
    export class ExampleLoadingImage{
    
       public src:string = "http://example.com/yourInitialImage.png";
    
       constructor(private http:Http){}
    
       generateImage(data: any): void {
          this.src = 'http://www.downgraf.com/wp-content/uploads/2014/09/01-progress.gif'; //Just a random loading gif found on google.
          this.http.get('http://example.com/mycdn/'+this.data.url+'.jpg')
             .subscribe(response => {
                let urlCreator = window.URL;
                this.src = urlCreator.createObjectURL(response.blob());
             });
        }
    }
    

    注意:您应该输入数据参数,输入是确保代码一致性的好方法,any 只能用作小丑,例如 Java 中的 Object .

    【讨论】:

    • 我会尝试这样做,但它会破坏我的 css 以使背景大小被覆盖,我不知道如何在角度上做到这一点,我一直使用 jquery。
    • 你应该寻找 css 来使 img 成为一个固定的背景,因为实现你想要的唯一方法就是这样做,使用 blob。
    • 好的,这比背景问题更重要,我会看看我如何排序,会尝试你的方法
    • 我明白了:platform-b​​rowser.umd.js:1900 例外:“blob()”方法未在响应超类上实现
    • 哦,看来您没有使用 Angular 最终版本 (2.0.X)
    【解决方案2】:

    此解决方案利用了 Angular 和浏览器已经提供的功能。图片加载由浏览器完成,不需要自己去处理任何数据或 DOM。

    我已经在 Chrome 53 上对此进行了测试,并且运行良好。

    这是你的元素,正在改变它的背景:

    <div class="yourBackgroundClass" [style.background-image]="'url(' + imgUrl + ')'"></div>
    

    为了预取图像,我们使用了一个未显示的图像标签。最好另外设置position: absolute 并将其移出视图或使其非常小,以免干扰您的实际内容。

    <img [src]="imgPreloadUrl" (load)="imgUrl = imgPreloadUrl" hidden>
    

    通过设置imgPreloadUrl,img的src通过角度更新,浏览器将图像加载到不可见的img标签中。完成后,onload 触发,我们设置imgUrl = imgPreloadUrl。 Angular 现在会更新实际背景的style.background-image,并且背景图像会立即切换,因为它已经加载到隐藏图像中。

    虽然imgUrl !== imgPreloadUrl 我们可以显示一个微调器来指示加载:

    <div class="spinner" *ngIf="imgUrl !== imgPreloadUrl"></div>
    

    测试:

    <button (click)="imgPreloadUrl = 'https://upload.wikimedia.org/wikipedia/commons/2/24/Willaerts_Adam_The_Embarkation_of_the_Elector_Palantine_Oil_Canvas-huge.jpg'">test</button>
    

    【讨论】:

    • 非常优雅。我将分拆这一点,以预取系列中的图像。下拉照片滑动工具的索引更改时的 3 个前索引和 3 个后索引。
    • 这会使图片下载两次。
    【解决方案3】:

    使用图像对象 (Plunker Demo &neArr;)

    tmpImg: HTMLImageElement; // will be used to load the actual image before showing it
    
    generateImage(data: any){
     this.urlImage = 'http://example.com/mycdn/'+ 'loading_GIF_url';  // show loading gif
    
     let loaded = () => { // wait for image to load then replace it with loadingGIF
       this.urlImage = 'http://example.com/mycdn/' + this.data.url+'.jpg';
     }
    
     // background loading logic
     if(this.tmpImg){
       this.tmpImg.onload = null; // remove the previous onload event, if registered
     }
     this.tmpImg = new Image();
     this.tmpImg.onload = loaded;  // register the onload event
     this.tmpImg.src = 'http://example.com/mycdn/'+this.data.url+'.jpg';
    }
    

    【讨论】:

      【解决方案4】:

      您需要的东西很少,例如 http、解析器和消毒剂。这里有一个link 来解释如何从头开始实现它。

      例如,您有一个请求,然后将返回的 blob 转换为安全样式,以便我们能够在样式指令中使用它

      this.http.get('assets/img/bg.jpg', { responseType: 'blob' }).pipe(
        map( image => {
          const blob: Blob = new Blob([image], { type: 'image/jpeg' });
          const imageStyle = `url(${window.URL.createObjectURL(blob)})`;
          return this.sanitizer.bypassSecurityTrustStyle(imageStyle);
        })
      )
      

      【讨论】:

      • 你能解释一下如何在这个问题的上下文中使用它吗?
      • @user3836415 建议我们可以先使用解析器加载图片,这样如果图片是着陆页的背景图片,我们可以避免延迟加载图片。
      【解决方案5】:

      我最近将其实现为structural directive:

      import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
      
      @Directive({ selector: '[appPreloadImage]'})
      export class PreloadImageDirective {
      
        @Input("appPreloadImage") imageUrl : string;
      
        constructor( private templateRef : TemplateRef<any>,
                     private viewContainer : ViewContainerRef) {
        }
      
        private showView() {
          this.viewContainer.createEmbeddedView(this.templateRef);
        }
      
        ngOnInit() {
          var self = this;
          self.viewContainer.clear();
          var tmpImg = new Image();
          tmpImg.src = self.imageUrl;
          tmpImg.onload = function() {
              self.showView();
          }
          tmpImg.onerror = function() {
              self.showView();
          }
        }
      }
      

      你可以这样使用它:

      <div *appPreloadImage="'/url/of/preloaded/image.jpg'">
        <!-- Nothing in here will be displayed until the 
             request to the URL above has been completed 
            (even if that request fails) -->
      </div>
      

      (注意单引号嵌套在双引号中 - 这是因为我们传递的是字符串文字。)

      【讨论】:

        猜你喜欢
        • 2017-08-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-19
        • 1970-01-01
        • 2017-04-16
        相关资源
        最近更新 更多