Skip to content
Open
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
28 changes: 26 additions & 2 deletions components/image/demo/preview-src.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,44 @@ title:

## zh-CN

可以设置不同的预览图片。
可以设置不同的预览图片。可以自定义预览图的加载占位符(placeholder)。

## en-US

You can set different preview image.
You can set different preview image. You can also customize the loading placeholder of preview image with VNode.

</docs>

<template>
<a-image
src="https://aliyuncdn.antdv.com/logo.png"
:width="200"
:preview="{
src: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
placeholder: () => CustomLoadingComp,
}"
/>
<a-image
:width="200"
src="https://aliyuncdn.antdv.com/logo.png"
:preview="{
src: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
placeholder: true,
}"
/>
</template>

<script lang="ts" setup>
import { h } from 'vue';
import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined';
import { theme } from 'ant-design-vue';
const { token } = theme.useToken();

const CustomLoadingComp = h(LoadingOutlined, {
style: {
fontSize: '256px',
color: token.value.colorPrimary,
},
spin: true,
});
</script>
15 changes: 8 additions & 7 deletions components/image/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ Previewable image.
| --- | --- | --- | --- | --- |
| alt | Image description | string | - | 2.0.0 |
| fallback | Load failure fault-tolerant src | string | - | 2.0.0 |
| height | Image height | string \| number | - | 2.0.0 |
| placeholder | Load placeholder, use default placeholder when set `true` | boolean \| slot | - | 2.0.0 |
| preview | preview config, disabled when `false` | boolean \| [previewType](#previewtype) | true | 2.0.0 |
| height | Image height | string\| number | - | 2.0.0 |
| placeholder | Load placeholder, use default placeholder when set `true` | boolean\| slot | - | 2.0.0 |
| preview | preview config, disabled when `false` | boolean\| [previewType](#previewtype) | true | 2.0.0 |
| src | Image path | string | - | 2.0.0 |
| previewMask | custom mask | false \| function \| slot | - | 3.2.0 |
| width | Image width | string \| number | - | 2.0.0 |
| previewMask | custom mask | false\| function \| slot | - | 3.2.0 |
| width | Image width | string\| number | - | 2.0.0 |

### events

Expand All @@ -38,11 +38,12 @@ Previewable image.
{
visible?: boolean;
onVisibleChange?: (visible, prevVisible) => void;
getContainer?: string | HTMLElement | (() => HTMLElement);
getContainer: string | HTMLElement | (() => HTMLElement);
src?: string;
maskClassName?: string;
current?: number;
placeholder?: (() => vNode) | boolean;
}
```

Other attributes [&lt;img>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#Attributes)
Other attributes [&lt;img&gt;](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#Attributes)
13 changes: 7 additions & 6 deletions components/image/index.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*LVQ3R5JjjJEAAA
| --- | --- | --- | --- | --- |
| alt | 图像描述 | string | - | 2.0.0 |
| fallback | 加载失败容错地址 | string | - | 2.0.0 |
| height | 图像高度 | string \| number | - | 2.0.0 |
| placeholder | 加载占位, 为 `true` 时使用默认占位 | boolean \| slot | - | 2.0.0 |
| preview | 预览参数,为 `false` 时禁用 | boolean \| [previewType](#previewtype) | true | 2.0.0 |
| height | 图像高度 | string\| number | - | 2.0.0 |
| placeholder | 加载占位, 为 `true` 时使用默认占位 | boolean\| slot | - | 2.0.0 |
| preview | 预览参数,为 `false` 时禁用 | boolean\| [previewType](#previewtype) | true | 2.0.0 |
| src | 图片地址 | string | - | 2.0.0 |
| previewMask | 自定义 mask | false \| function \| slot | - | 3.2.0 |
| width | 图像宽度 | string \| number | - | 2.0.0 |
| previewMask | 自定义 mask | false\| function \| slot | - | 3.2.0 |
| width | 图像宽度 | string\| number | - | 2.0.0 |

### 事件

Expand All @@ -43,7 +43,8 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*LVQ3R5JjjJEAAA
src?: string;
maskClassName?: string;
current?: number;
placeholder?: (() => vNode) | boolean;
}
```

其他属性见 [&lt;img>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#Attributes)
其他属性见 [&lt;img&gt;](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#Attributes)
4 changes: 3 additions & 1 deletion components/image/style/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ export const genImagePreviewStyle: GenerateStyle<ImageToken> = (token: ImageToke
transition: `transform ${motionDurationSlow} ${motionEaseOut} 0s`,
userSelect: 'none',
pointerEvents: 'auto',
'&-placeholder': {
position: 'absolute',
},

'&-wrapper': {
...genBoxStyle(),
Expand All @@ -214,7 +217,6 @@ export const genImagePreviewStyle: GenerateStyle<ImageToken> = (token: ImageToke
},
},
},

[`${previewCls}-moving`]: {
[`${previewCls}-preview-img`]: {
cursor: 'grabbing',
Expand Down
6 changes: 5 additions & 1 deletion components/vc-image/src/Image.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { CSSProperties, PropType } from 'vue';
import type { CSSProperties, PropType, VNode } from 'vue';
import { ref, watch, defineComponent, computed, onMounted, onUnmounted } from 'vue';
import isNumber from 'lodash-es/isNumber';
import cn from '../../_util/classNames';
Expand All @@ -19,6 +19,8 @@ export type ImagePreviewType = Omit<
> & {
src?: string;
visible?: boolean;
fallback?: string;
placeholder?: boolean | (() => VNode);
onVisibleChange?: (value: boolean, prevValue: boolean) => void;
getContainer?: GetContainer | false;
maskClassName?: string;
Expand Down Expand Up @@ -79,6 +81,7 @@ const ImageInternal = defineComponent({
onVisibleChange: () => {},
getContainer: undefined,
};

return typeof props.preview === 'object'
? mergeDefaultValue(props.preview, defaultValues)
: defaultValues;
Expand Down Expand Up @@ -288,6 +291,7 @@ const ImageInternal = defineComponent({
onClose={onPreviewClose}
mousePosition={mousePosition.value}
src={mergedSrc}
placeholder={preview.value.placeholder}
alt={alt}
getContainer={getPreviewContainer.value}
icons={icons}
Expand Down
42 changes: 40 additions & 2 deletions components/vc-image/src/Preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import {
shallowRef,
watch,
cloneVNode,
ref,
nextTick,
} from 'vue';
import type { VNode, PropType } from 'vue';

import type { VNode, Ref, PropType } from 'vue';
import type { ImageStatus } from './Image';
import classnames from '../../_util/classNames';
import Dialog from '../../vc-dialog';
import Spin from '../../spin';
import { type IDialogChildProps, dialogPropTypes } from '../../vc-dialog/IDialogPropTypes';
import { getOffset } from '../../vc-util/Dom/css';
import addEventListener from '../../vc-util/Dom/addEventListener';
Expand All @@ -28,6 +31,7 @@ export interface PreviewProps extends Omit<IDialogChildProps, 'onClose' | 'mask'
src?: string;
alt?: string;
rootClassName?: string;
placeholder?: boolean | (() => VNode);
icons?: {
rotateLeft?: VNode;
rotateRight?: VNode;
Expand All @@ -49,6 +53,11 @@ export const previewProps = {
...dialogPropTypes(),
src: String,
alt: String,
fallback: String,
placeholder: {
type: [Boolean, Function] as PropType<boolean | (() => VNode)>,
default: () => true,
},
rootClassName: String,
icons: {
type: Object as PropType<PreviewProps['icons']>,
Expand All @@ -65,7 +74,25 @@ const Preview = defineComponent({
const { rotateLeft, rotateRight, zoomIn, zoomOut, close, left, right, flipX, flipY } = reactive(
props.icons,
);
// 判断是否是自定义placeholder
const isCustomPlaceholder = computed(() => typeof props.placeholder === 'function');

const isDefaultPlaceholder = computed(() => {
return props.placeholder === true;
});
const hasPlaceholder = computed(() => isCustomPlaceholder.value || isDefaultPlaceholder.value);
const status: Ref<ImageStatus> = ref(hasPlaceholder.value ? 'loading' : 'normal');

watch(
() => props.src,
() => {
status.value = 'loading';
},
);
const renderPlaceholder = () => {
const { placeholder } = props;
return typeof placeholder === 'function' ? placeholder() : placeholder;
};
const scale = shallowRef(1);
const rotate = shallowRef(0);
const flip = reactive({ x: 1, y: 1 });
Expand Down Expand Up @@ -386,6 +413,11 @@ const Preview = defineComponent({
<img
onMousedown={onMouseDown}
onDblclick={onDoubleClick}
onLoad={() => {
nextTick(() => {
status.value = 'normal';
});
}}
ref={imgRef}
class={`${props.prefixCls}-img`}
src={combinationSrc.value}
Expand All @@ -396,6 +428,12 @@ const Preview = defineComponent({
}deg)`,
}}
/>
{isDefaultPlaceholder.value && status.value === 'loading' && (
<Spin size="large" class={`${props.prefixCls}-img-placeholder`}></Spin>
)}
{isCustomPlaceholder.value && status.value === 'loading' && (
<div class={`${props.prefixCls}-img-placeholder`}>{renderPlaceholder()}</div>
)}
</div>
{showLeftOrRightSwitches.value && (
<div
Expand Down