[new feature] Image: add loading slot

This commit is contained in:
陈嘉涵 2019-05-17 14:51:06 +08:00
parent 1321ba6902
commit d7b69d2fe4
8 changed files with 185 additions and 34 deletions

View File

@ -19,6 +19,7 @@
>
<van-image
:fit="fit"
width="100%"
height="27vw"
:src="image"
/>
@ -26,6 +27,33 @@
</van-col>
</van-row>
</demo-block>
<demo-block :title="$t('loading')">
<van-row gutter="20">
<van-col span="8">
<van-image
width="100%"
height="27vw"
/>
<div class="text">{{ $t('defaultTip') }}</div>
</van-col>
<van-col span="8">
<van-image
width="100%"
height="27vw"
>
<template v-slot:loading>
<van-loading
type="spinner"
size="20"
/>
</template>
</van-image>
<div class="text">{{ $t('customTip') }}</div>
</van-col>
</van-row>
</demo-block>
</demo-section>
</template>
@ -33,10 +61,18 @@
export default {
i18n: {
'zh-CN': {
fitMode: '缩放模式'
fitMode: '填充模式',
loading: '加载中提示',
error: '加载失败提示',
defaultTip: '默认提示',
customTip: '自定义提示'
},
'en-US': {
fitMode: 'Fit Mode'
fitMode: 'Fit Mode',
loading: 'Loading',
error: 'Error',
defaultTip: 'Default Tip',
customTip: 'Custom Tip'
}
},

View File

@ -78,3 +78,9 @@ Vue.use(Lazyload);
| click | Triggered when click image | event: Event |
| load | Triggered when image loaded | - |
| error | Triggered when image load failed | - |
### Slots
| Name | Description |
|------|------|
| loading | Custom loading placeholder |

View File

@ -1,4 +1,5 @@
import { use, isDef, suffixPx } from '../utils';
import Icon from '../icon';
const [sfc, bem] = use('image');
@ -19,6 +20,13 @@ export default sfc({
};
},
watch: {
src() {
this.loading = true;
this.error = false;
}
},
computed: {
style() {
const style = {};
@ -48,33 +56,46 @@ export default sfc({
onClick(event) {
this.$emit('click', event);
},
renderContent() {
if (this.loading) {
return (
<div class={bem('loading')}>
{this.slots('loading') || <Icon name="photo-o" size="20" />}
</div>
);
}
},
renderImage() {
const imgData = {
class: bem('img'),
attrs: {
alt: this.alt
},
style: {
objectFit: this.fit
},
on: {
load: this.onLoad,
error: this.onError
}
};
if (this.lazyLoad) {
return <img vLazy={this.src} {...imgData} />;
}
return <img src={this.src} {...imgData} />;
}
},
render(h) {
const imgData = {
class: bem('img'),
attrs: {
alt: this.alt
},
style: {
objectFit: this.fit
},
on: {
load: this.onLoad,
error: this.onError
}
};
const Image = this.lazyLoad ? (
<img vLazy={this.src} {...imgData} />
) : (
<img src={this.src} {...imgData} />
);
return (
<div class={bem()} style={this.style} onClick={this.onClick}>
{Image}
{this.renderImage()}
{this.renderContent()}
</div>
);
}

View File

@ -1,11 +1,26 @@
@import '../style/var';
.van-image {
position: relative;
display: inline-block;
&__img {
&__img,
&__error,
&__loading {
display: block;
width: 100%;
height: 100%;
}
&__error,
&__loading {
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
color: @gray-dark;
background-color: @background-color;
}
}

View File

@ -4,32 +4,69 @@ exports[`renders demo correctly 1`] = `
<div>
<div>
<div class="van-row">
<div class="van-image" style="width: 100px; height: 100px;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img"></div>
<div class="van-image" style="width: 100px; height: 100px;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o" style="font-size: 20px;">
<!----></i></div>
</div>
</div>
</div>
<div>
<div class="van-row" style="margin-left: -10px; margin-right: -10px;">
<div class="van-col van-col--8" style="padding-left: 10px; padding-right: 10px;">
<div class="van-image" style="height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: contain;"></div>
<div class="van-image" style="width: 100%; height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: contain;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o" style="font-size: 20px;">
<!----></i></div>
</div>
<div class="text">contain</div>
</div>
<div class="van-col van-col--8" style="padding-left: 10px; padding-right: 10px;">
<div class="van-image" style="height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: cover;"></div>
<div class="van-image" style="width: 100%; height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: cover;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o" style="font-size: 20px;">
<!----></i></div>
</div>
<div class="text">cover</div>
</div>
<div class="van-col van-col--8" style="padding-left: 10px; padding-right: 10px;">
<div class="van-image" style="height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: fill;"></div>
<div class="van-image" style="width: 100%; height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: fill;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o" style="font-size: 20px;">
<!----></i></div>
</div>
<div class="text">fill</div>
</div>
<div class="van-col van-col--8" style="padding-left: 10px; padding-right: 10px;">
<div class="van-image" style="height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: none;"></div>
<div class="van-image" style="width: 100%; height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: none;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o" style="font-size: 20px;">
<!----></i></div>
</div>
<div class="text">none</div>
</div>
<div class="van-col van-col--8" style="padding-left: 10px; padding-right: 10px;">
<div class="van-image" style="height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: scale-down;"></div>
<div class="van-image" style="width: 100%; height: 27vw;"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: scale-down;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o" style="font-size: 20px;">
<!----></i></div>
</div>
<div class="text">scale-down</div>
</div>
</div>
</div>
<div>
<div class="van-row" style="margin-left: -10px; margin-right: -10px;">
<div class="van-col van-col--8" style="padding-left: 10px; padding-right: 10px;">
<div class="van-image" style="width: 100%; height: 27vw;"><img class="van-image__img">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o" style="font-size: 20px;">
<!----></i></div>
</div>
<div class="text">默认提示</div>
</div>
<div class="van-col van-col--8" style="padding-left: 10px; padding-right: 10px;">
<div class="van-image" style="width: 100%; height: 27vw;"><img class="van-image__img">
<div class="van-image__loading">
<div class="van-loading van-loading--spinner"><span class="van-loading__spinner van-loading__spinner--spinner" style="color: rgb(201, 201, 201); width: 20px; height: 20px;"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></span></div>
</div>
</div>
<div class="text">自定义提示</div>
</div>
</div>
</div>
</div>
`;

View File

@ -1,3 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`lazy load 1`] = `<div class="van-image"><img class="van-image__img"></div>`;
exports[`lazy load 1`] = `
<div class="van-image"><img class="van-image__img">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o" style="font-size: 20px;">
<!----></i></div>
</div>
`;
exports[`load event 1`] = `<div class="van-image"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img"></div>`;
exports[`load event 2`] = `
<div class="van-image"><img src="" class="van-image__img">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o" style="font-size: 20px;">
<!----></i></div>
</div>
`;

View File

@ -19,6 +19,10 @@ test('load event', () => {
wrapper.find('img').trigger('load');
expect(wrapper.emitted('load')[0][0]).toBeTruthy();
expect(wrapper).toMatchSnapshot();
wrapper.setProps({ src: '' });
expect(wrapper).toMatchSnapshot();
});
test('error event', () => {

View File

@ -20,7 +20,7 @@ Vue.use(Image);
/>
```
### 缩放模式
### 填充模式
```html
<van-image
@ -48,6 +48,18 @@ import { Lazyload } from 'vant';
Vue.use(Lazyload);
```
### 加载中提示
`Image`组件默认提供了图片加载中的提示,可以通过插槽自定义加载中提示
```html
<van-image src="https://img.yzcdn.cn/vant/cat.jpeg">
<template v-slot:loading>
<van-loading type="spinner" size="20" />
</template>
</van-image>
```
## API
### Props
@ -55,13 +67,13 @@ Vue.use(Lazyload);
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|------|------|------|------|------|
| src | 图片链接 | `String` | - | - |
| fit | 图片裁剪、缩放的模式 | `String` | `fill` | - |
| fit | 图片填充模式 | `String` | `fill` | - |
| alt | 替代文本 | `String` | - | - |
| width | 宽度,默认单位为 px | `String | Number` | - | - |
| height | 高度,默认单位为 px | `String | Number` | - | - |
| lazy-load | 是否开启图片懒加载,须配合 [Lazyload](#/zh-CN/lazyload) 组件使用 | `Boolean` | `false` | - |
### fit 可选值
### 图片填充模式
| 名称 | 含义 |
|------|------|
@ -78,3 +90,9 @@ Vue.use(Lazyload);
| click | 点击图片时触发 | event: Event |
| load | 图片加载完毕时触发 | - |
| error | 图片加载失败时触发 | - |
### Slots
| 名称 | 说明 |
|------|------|
| loading | 自定义加载状态显示内容 |