-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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. |
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; | ||
} |
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` 可以在服务端渲染下预先加载该图片。 | ||||||
|
||||||
## en-US | ||||||
|
||||||
Adding `nzPreload` will preload the image under server-side rendering. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
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'; | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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 | ||||||
|
@@ -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` | ✅ | | ||||||
|
@@ -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` | ✅ | | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|nzOptimizeSizes | Optimized loading size | `number[]` | `[16, 32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840]` | ✅ | | ||||||
|
||||||
### NzImageService | ||||||
|
||||||
|
@@ -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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||||||
}; | ||||||
``` |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -23,7 +23,7 @@ import { NzImageModule } from 'ng-zorro-antd/image'; | |||||
|
||||||
| 参数 | 说明 | 类型 | 默认值 | 支持全局配置 | | ||||||
| ----------- | ---------------------------------- | ---------------- | ------ | ----- | | ||||||
| nzSrc | 图片地址 | `string` | - | - | | ||||||
| nzSrc | url | `string` | - | | | ||||||
| nzFallback | 加载失败容错地址 | `string` | - | ✅ | | ||||||
| nzPlaceholder | 加载占位地址 | `string` | - | ✅ | | ||||||
| nzDisablePreview | 是否禁止预览 | `boolean` | `false` | ✅ | | ||||||
|
@@ -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` | ✅ | | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|nzOptimizeSizes | 优化加载尺寸 | `number[]` | `[16, 32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840]` | ✅ | | ||||||
|
||||||
### NzImageService | ||||||
|
||||||
| 参数 | 说明 | 类型 | 默认值 | | ||||||
|
@@ -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); | ||||||
}; | ||||||
``` |
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); | ||
}; |
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); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||||||||||
} | ||||||||||
|
||||||||||
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(); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)) { | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
private genSrc(): void { | ||||||||||
if (!this.nzOptimize) { | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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]) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
) | ||||||||||
]; | ||||||||||
} | ||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.