Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(module:image): add nz-image component #6572

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions components/core/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,10 @@ export interface ImageConfig {
nzDisablePreview?: string;
nzCloseOnNavigation?: boolean;
nzDirection?: Direction;
nzPreload?: boolean;
nzOptimize?: boolean;
nzOptimizeSizes?: number[];
nzLoader?(params: { src: string; width: number }): string;
}

export interface PopConfirmConfig {
Expand Down
14 changes: 14 additions & 0 deletions components/image/demo/optimization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
order: 6
title:
zh-CN: 优化
en-US: Optimization
---

## zh-CN

添加 `nzOptimize` 属性优化高分辨率屏幕图片加载。

## en-US

Add the `nzOptimize` property to optimize high-resolution screen image loading.
17 changes: 17 additions & 0 deletions components/image/demo/optimization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Component } from '@angular/core';
import { NzImageLoader } from 'ng-zorro-antd/image';

export const loader: NzImageLoader = ({ src, width }) => {
return `http://image-demo.oss-cn-hangzhou.aliyuncs.com/${encodeURIComponent(src)}?x-oss-process=image/resize,w_${width}`;
};

@Component({
selector: 'nz-demo-image-optimization',
template: `
<nz-image [nzSrc]="src" nzWidth="200" nzHeight="140" [nzLoader]="loader" nzOptimize></nz-image>
`
})
export class NzDemoImageOptimizationComponent {
src = 'example.jpg';
loader = loader;
}
14 changes: 14 additions & 0 deletions components/image/demo/preloading.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
order: 7
title:
zh-CN: 预先加载
en-US: Preloading
---

## zh-CN

添加 `nzPreload` 可以在服务端渲染下预先加载该图片。
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
添加 `nzPreload` 可以在服务端渲染下预先加载该图片。
添加 `nzPreload` 可以在服务端渲染下添加 [preload](https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content)


## en-US

Adding `nzPreload` will preload the image under server-side rendering.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Adding `nzPreload` will preload the image under server-side rendering.
Using `nzPreload` will add (preload)[https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content] under server-side rendering.

11 changes: 11 additions & 0 deletions components/image/demo/preloading.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Component } from '@angular/core';

@Component({
selector: 'nz-demo-image-preloading',
template: `
<nz-image [nzSrc]="src" nzWidth="200" nzHeight="200" nzPreload></nz-image>
`
})
export class NzDemoImagePreloadingComponent {
src = 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png';
}
26 changes: 24 additions & 2 deletions components/image/doc/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Previewable image.

## When To Use

- When you need to display pictures.
- Use when you need to show pictures.
- Display when loading a large image or fault tolerant handling when loading fail.

```ts
Expand All @@ -22,7 +22,7 @@ import { NzImageModule } from 'ng-zorro-antd/image';

| Property | Description | Type | Default | Global Config |
| --- | --- | --- | --- | --- |
| nzSrc | Image path | `string` | - | - |
| nzSrc | URL | `string` | - | |
| nzFallback | Load failure fault-tolerant src | `string` | - | ✅ |
| nzPlaceholder | Load placeholder src | `string` | - | ✅ |
| nzDisablePreview | Whether to disable the preview | `boolean` | `false` | ✅ |
Expand All @@ -31,6 +31,18 @@ import { NzImageModule } from 'ng-zorro-antd/image';

Other attributes [<img\>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#Attributes)

### nz-image

| Property | Description | Type | Default | Global Config |
| --- | --- | --- | --- | --- |
|nzSrc | URL | `string` | - | |
|nzAlt | Alt | `string` | - | |
|nzWidth | Width | `number\|string` | `auto` | |
|nzHeight | Height | `number\|string` | `auto` | |
|nzLoader | Loader | `NzImageLoader` | `defaultLoader` | ✅ |
|nzOptimize | Whether to optimize image loading | `boolean` | `false` | ✅ |
|nzPreload | Whether to preload image | `boolean` | `false` | ✅ |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
|nzPreload | Whether to preload image | `boolean` | `false` ||
|nzPreload | Whether to add [preload](https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content) (only SSR) | `boolean` | `false` ||

|nzOptimizeSizes | Optimized loading size | `number[]` | `[16, 32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840]` | ✅ |

### NzImageService

Expand Down Expand Up @@ -69,3 +81,13 @@ Other attributes [<img\>](https://developer.mozilla.org/en-US/docs/Web/HTML/Elem
| prev(): void | Previous image |
| next(): void | Next image |
| close(): void | Close image preview |

### NzImageLoader
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More details are needed here. And we also need the built-in loaders.


```ts
export type NzImageLoader = (params: {src: string; width: number}) => string;

export const defaultLoader: NzImageLoader = ({ src }) => {
return encodeURIComponent(src);
};
```
26 changes: 24 additions & 2 deletions components/image/doc/index.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { NzImageModule } from 'ng-zorro-antd/image';

| 参数 | 说明 | 类型 | 默认值 | 支持全局配置 |
| ----------- | ---------------------------------- | ---------------- | ------ | ----- |
| nzSrc | 图片地址 | `string` | - | - |
| nzSrc | url | `string` | - | |
| nzFallback | 加载失败容错地址 | `string` | - | ✅ |
| nzPlaceholder | 加载占位地址 | `string` | - | ✅ |
| nzDisablePreview | 是否禁止预览 | `boolean` | `false` | ✅ |
Expand All @@ -32,6 +32,19 @@ import { NzImageModule } from 'ng-zorro-antd/image';

其他属性见 [<img\>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#Attributes)

### nz-image

| 参数 | 说明 | 类型 | 默认值 | 支持全局配置 |
| --- | --- | --- | --- | --- |
|nzSrc | url | `string` | - | |
|nzAlt | alt | `string` | - | |
|nzWidth | 宽度 | `number\|string` | `auto` | |
|nzHeight | 高度 | `number\|string` | `auto` | |
|nzLoader | 图片加载器 | `NzImageLoader` | `defaultLoader` | ✅ |
|nzOptimize | 是否优化图片加载 | `boolean` | `false` | ✅ |
|nzPreload | 是否预加载图片 | `boolean` | `false` | ✅ |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
|nzPreload | 是否预加载图片 | `boolean` | `false` ||
|nzPreload | 是否添加 [preload](https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content) | `boolean` | `false` ||

|nzOptimizeSizes | 优化加载尺寸 | `number[]` | `[16, 32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840]` | ✅ |

### NzImageService

| 参数 | 说明 | 类型 | 默认值 |
Expand Down Expand Up @@ -68,4 +81,13 @@ import { NzImageModule } from 'ng-zorro-antd/image';
| switchTo(index: number): void | 设置预览索引 |
| prev(): void | 上一张 |
| next(): void | 下一张 |
| close(): void | 关闭预览 |
| close(): void | 关闭预览 |

### NzImageLoader
```ts
export type NzImageLoader = (params: { src: string; width: number }) => string;

export const defaultLoader: NzImageLoader = ({ src }) => {
return encodeURIComponent(src);
};
```
9 changes: 9 additions & 0 deletions components/image/image-loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/
export type NzImageLoader = (params: { src: string; width: number }) => string;

export const defaultLoader: NzImageLoader = ({ src }) => {
return encodeURIComponent(src);
};
143 changes: 143 additions & 0 deletions components/image/image.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/**
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

import { DOCUMENT, isPlatformServer } from '@angular/common';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
Inject,
Input,
OnChanges,
OnDestroy,
OnInit,
PLATFORM_ID,
Renderer2,
SimpleChanges,
ViewEncapsulation
} from '@angular/core';
import { NzConfigKey, NzConfigService, WithConfig } from 'ng-zorro-antd/core/config';
import { BooleanInput, NzSafeAny } from 'ng-zorro-antd/core/types';
import { InputBoolean } from 'ng-zorro-antd/core/util';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { defaultLoader, NzImageLoader } from './image-loader';

const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'image';

@Component({
selector: 'nz-image',
exportAs: 'nzImg',
template: `
<img [attr.src]="src" [attr.width]="width" [attr.height]="height" [attr.srcset]="srcSet" [attr.sizes]="sizes" [attr.alt]="nzAlt" />
`,
preserveWhitespaces: false,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None
})
export class NzImageComponent implements OnInit, OnChanges, OnDestroy {
readonly _nzModuleName: NzConfigKey = NZ_CONFIG_MODULE_NAME;
static ngAcceptInputType_nzOptimize: BooleanInput;
static ngAcceptInputType_nzPreload: BooleanInput;

@Input() nzSrc: string = '';
@Input() nzAlt: string = '';
@Input() nzWidth: string | number = 'auto';
@Input() nzHeight: string | number = 'auto';
@Input() @WithConfig() nzLoader: NzImageLoader = defaultLoader;
@Input() @InputBoolean() @WithConfig() nzOptimize: boolean = false;
@Input() @InputBoolean() @WithConfig() nzPreload: boolean = false;
@Input() @WithConfig() nzOptimizeSizes: number[] = [16, 32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840];

get width(): number {
return typeof this.nzWidth === 'number' ? this.nzWidth : parseInt(this.nzWidth, 10);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nzWidth default value is auto will be return NaN (parseInt('auto', 10)).

}

get height(): number {
return typeof this.nzHeight === 'number' ? this.nzHeight : parseInt(this.nzHeight, 10);
}

src = '';
sizes = '';
srcSet = '';

private destroy$ = new Subject<void>();

constructor(
private render: Renderer2,
private cdr: ChangeDetectorRef,
public nzConfigService: NzConfigService,
@Inject(PLATFORM_ID) private platformId: NzSafeAny,
@Inject(DOCUMENT) private document: NzSafeAny
) {
this.nzConfigService
.getConfigChangeEventForComponent(NZ_CONFIG_MODULE_NAME)
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
this.cdr.markForCheck();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

anything forgot here?

});
}

ngOnInit(): void {
if (this.nzPreload) {
this.preload();
}
}

ngOnChanges(changes: SimpleChanges): void {
const { nzLoader, nzSrc, nzOptimized } = changes;
if (nzLoader || nzSrc || nzOptimized) {
this.genSrc();
}
}

ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}

private preload(): void {
if (isPlatformServer(this.platformId)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const linkNode = this.render.createElement('link') as HTMLLinkElement;
linkNode.rel = 'preload';
linkNode.as = 'image';
linkNode.imageSizes = this.sizes;
linkNode.imageSrcset = this.srcSet;
linkNode.href = this.src;
const headNode = this.document.getElementsByTagName('head')[0] as HTMLHeadElement;
this.render.appendChild(headNode, linkNode);
Comment on lines +109 to +110
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const headNode = this.document.getElementsByTagName('head')[0] as HTMLHeadElement;
this.render.appendChild(headNode, linkNode);
this.renderer.appendChild(this.document.head, linkNode);

}
}

private genSrc(): void {
if (!this.nzOptimize) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here need to determine the svg or data

this.src = this.nzSrc;
return;
}
const widths = this.convertWidths(this.width, this.nzOptimizeSizes);
this.src = this.nzLoader({ src: this.nzSrc, width: widths[widths.length - 1] });
this.sizes = '';
this.srcSet = widths
.map(
(w, i) =>
`${this.nzLoader({
src: this.nzSrc,
width: w
})} ${i + 1}x`
)
.join(', ');
}

private convertWidths(width: number, optimizeSizes: number[]): number[] {
const allSizes = [...optimizeSizes].sort((a, b) => a - b);
return [
...new Set(
// 2x scale is sufficient
// https://blog.twitter.com/engineering/en_us/topics/infrastructure/2019/capping-image-fidelity-on-ultra-high-resolution-devices.html
[width, width * 2].map(w => allSizes.find(p => p >= w) || allSizes[allSizes.length - 1])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[width, width * 2].map(w => allSizes.find(p => p >= w) || allSizes[allSizes.length - 1])
[width, width * 2].map(w => allSizes.find(p => p >= w) || w)

)
];
}
}
5 changes: 3 additions & 2 deletions components/image/image.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ import { NzPipesModule } from 'ng-zorro-antd/pipes';

import { NzImageGroupComponent } from './image-group.component';
import { NzImagePreviewComponent } from './image-preview.component';
import { NzImageComponent } from './image.component';
import { NzImageDirective } from './image.directive';
import { NzImageService } from './image.service';

@NgModule({
imports: [BidiModule, OverlayModule, PortalModule, DragDropModule, CommonModule, NzIconModule, NzPipesModule],
exports: [NzImageDirective, NzImagePreviewComponent, NzImageGroupComponent],
exports: [NzImageDirective, NzImagePreviewComponent, NzImageGroupComponent, NzImageComponent],
providers: [NzImageService],
entryComponents: [NzImagePreviewComponent],
declarations: [NzImageDirective, NzImagePreviewComponent, NzImageGroupComponent]
declarations: [NzImageDirective, NzImagePreviewComponent, NzImageGroupComponent, NzImageComponent]
})
export class NzImageModule {}
Loading