feat: update avatar

pull/1790/head
tangjinzhou 2020-02-08 16:29:55 +08:00
parent 7a8bef0ef3
commit 2394d9f844
7 changed files with 76 additions and 41 deletions

View File

@ -1,5 +1,5 @@
module.exports = { module.exports = {
dev: { dev: {
componentName: 'auto-complete', // dev components componentName: 'avatar', // dev components
}, },
}; };

View File

@ -1,6 +1,7 @@
import { ConfigConsumerProps } from '../config-provider'; import { ConfigConsumerProps } from '../config-provider';
import Icon from '../icon'; import Icon from '../icon';
import { getListeners } from '../_util/props-util'; import { getListeners, getComponentFromProp } from '../_util/props-util';
import PropTypes from '../_util/vue-types';
export default { export default {
name: 'AAvatar', name: 'AAvatar',
@ -22,7 +23,7 @@ export default {
src: String, src: String,
/** Srcset of image avatar */ /** Srcset of image avatar */
srcSet: String, srcSet: String,
icon: String, icon: PropTypes.any,
alt: String, alt: String,
loadError: Function, loadError: Function,
}, },
@ -32,6 +33,7 @@ export default {
data() { data() {
return { return {
isImgExist: true, isImgExist: true,
isMounted: false,
scale: 1, scale: 1,
}; };
}, },
@ -46,38 +48,35 @@ export default {
}, },
}, },
mounted() { mounted() {
this.prevChildren = this.$slots.default; this.$nextTick(() => {
this.prevState = { ...this.$data }; this.setScale();
this.isMounted = true;
});
},
updated() {
this.$nextTick(() => { this.$nextTick(() => {
this.setScale(); this.setScale();
}); });
}, },
updated() {
if (
this.preChildren !== this.$slots.default ||
(this.prevState.scale !== this.$data.scale && this.$data.scale === 1) ||
this.prevState.isImgExist !== this.$data.isImgExist
) {
this.$nextTick(() => {
this.setScale();
});
}
this.preChildren = this.$slots.default;
this.prevState = { ...this.$data };
},
methods: { methods: {
setScale() { setScale() {
const childrenNode = this.$refs.avatarChildren; if (!this.$refs.avatarChildren || !this.$refs.avatarNode) {
if (childrenNode) { return;
const childrenWidth = childrenNode.offsetWidth;
const avatarWidth = this.$el.getBoundingClientRect().width;
if (avatarWidth - 8 < childrenWidth) {
this.scale = (avatarWidth - 8) / childrenWidth;
} else {
this.scale = 1;
}
this.$forceUpdate();
} }
const childrenWidth = this.$refs.avatarChildren.offsetWidth; // offsetWidth avoid affecting be transform scale
const nodeWidth = this.$refs.avatarNode.offsetWidth;
// denominator is 0 is no meaning
if (
childrenWidth === 0 ||
nodeWidth === 0 ||
(this.lastChildrenWidth === childrenWidth && this.lastNodeWidth === nodeWidth)
) {
return;
}
this.lastChildrenWidth = childrenWidth;
this.lastNodeWidth = nodeWidth;
// add 4px gap for each side to get better performance
this.scale = nodeWidth - 8 < childrenWidth ? (nodeWidth - 8) / childrenWidth : 1;
}, },
handleImgLoadError() { handleImgLoadError() {
const { loadError } = this.$props; const { loadError } = this.$props;
@ -88,12 +87,12 @@ export default {
}, },
}, },
render() { render() {
const { prefixCls: customizePrefixCls, shape, size, src, icon, alt, srcSet } = this.$props; const { prefixCls: customizePrefixCls, shape, size, src, alt, srcSet } = this.$props;
const icon = getComponentFromProp(this, 'icon');
const getPrefixCls = this.configProvider.getPrefixCls; const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('avatar', customizePrefixCls); const prefixCls = getPrefixCls('avatar', customizePrefixCls);
const { isImgExist, scale } = this.$data; const { isImgExist, scale, isMounted } = this.$data;
const sizeCls = { const sizeCls = {
[`${prefixCls}-lg`]: size === 'large', [`${prefixCls}-lg`]: size === 'large',
@ -122,10 +121,14 @@ export default {
if (src && isImgExist) { if (src && isImgExist) {
children = <img src={src} srcSet={srcSet} onError={this.handleImgLoadError} alt={alt} />; children = <img src={src} srcSet={srcSet} onError={this.handleImgLoadError} alt={alt} />;
} else if (icon) { } else if (icon) {
children = <Icon type={icon} />; if (typeof icon === 'string') {
children = <Icon type={icon} />;
} else {
children = icon;
}
} else { } else {
const childrenNode = this.$refs.avatarChildren; const childrenNode = this.$refs.avatarChildren;
if (childrenNode || (scale !== 1 && childrenNode)) { if (childrenNode || scale !== 1) {
const transformString = `scale(${scale}) translateX(-50%)`; const transformString = `scale(${scale}) translateX(-50%)`;
const childrenStyle = { const childrenStyle = {
msTransform: transformString, msTransform: transformString,
@ -148,15 +151,21 @@ export default {
</span> </span>
); );
} else { } else {
const childrenStyle = {};
if (!isMounted) {
childrenStyle.opacity = 0;
}
children = ( children = (
<span class={`${prefixCls}-string`} ref="avatarChildren"> <span class={`${prefixCls}-string`} ref="avatarChildren" style={{ opacity: 0 }}>
{children} {children}
</span> </span>
); );
} }
} }
return ( return (
<span {...{ on: getListeners(this), class: classString, style: sizeStyle }}>{children}</span> <span ref="avatarNode" {...{ on: getListeners(this), class: classString, style: sizeStyle }}>
{children}
</span>
); );
}, },
}; };

View File

@ -3,6 +3,26 @@ import { asyncExpect } from '@/tests/utils';
import Avatar from '..'; import Avatar from '..';
describe('Avatar Render', () => { describe('Avatar Render', () => {
let originOffsetWidth;
beforeAll(() => {
// Mock offsetHeight
originOffsetWidth = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetWidth').get;
Object.defineProperty(HTMLElement.prototype, 'offsetWidth', {
get() {
if (this.className === 'ant-avatar-string') {
return 100;
}
return 80;
},
});
});
afterAll(() => {
// Restore Mock offsetHeight
Object.defineProperty(HTMLElement.prototype, 'offsetWidth', {
get: originOffsetWidth,
});
});
it('Render long string correctly', () => { it('Render long string correctly', () => {
const wrapper = mount(Avatar, { const wrapper = mount(Avatar, {
slots: { slots: {
@ -25,6 +45,9 @@ describe('Avatar Render', () => {
attachToDocument: true, attachToDocument: true,
}); });
wrapper.vm.setScale = jest.fn(() => { wrapper.vm.setScale = jest.fn(() => {
if (wrapper.vm.scale === 0.5) {
return;
}
wrapper.setData({ scale: 0.5 }); wrapper.setData({ scale: 0.5 });
wrapper.vm.$forceUpdate(); wrapper.vm.$forceUpdate();
}); });
@ -35,14 +58,14 @@ describe('Avatar Render', () => {
const children = wrapper.findAll('.ant-avatar-string'); const children = wrapper.findAll('.ant-avatar-string');
expect(children.length).toBe(1); expect(children.length).toBe(1);
expect(children.at(0).text()).toBe('Fallback'); expect(children.at(0).text()).toBe('Fallback');
expect(wrapper.vm.setScale).toBeCalled(); expect(wrapper.vm.setScale).toHaveBeenCalled();
}); });
await asyncExpect(() => { await asyncExpect(() => {
expect(global.document.body.querySelector('.ant-avatar-string').style.transform).toContain( expect(global.document.body.querySelector('.ant-avatar-string').style.transform).toContain(
'scale(0.5)', 'scale(0.5)',
); );
global.document.body.innerHTML = ''; global.document.body.innerHTML = '';
}, 0); }, 1000);
}); });
it('should handle onError correctly', async () => { it('should handle onError correctly', async () => {
global.document.body.innerHTML = ''; global.document.body.innerHTML = '';

View File

@ -9,6 +9,6 @@ exports[`renders ./components/avatar/demo/basic.md correctly 1`] = `
</div> </div>
`; `;
exports[`renders ./components/avatar/demo/dynamic.md correctly 1`] = `<div><span class="ant-avatar ant-avatar-lg ant-avatar-square" style="background-color: rgb(245, 106, 0); vertical-align: middle;"><span class="ant-avatar-string">U</span></span> <button type="button" class="ant-btn ant-btn-sm" style="vertical-align: middle;"><span>改 变</span></button></div>`; exports[`renders ./components/avatar/demo/dynamic.md correctly 1`] = `<div><span class="ant-avatar ant-avatar-lg ant-avatar-square" style="background-color: rgb(245, 106, 0); vertical-align: middle;"><span class="ant-avatar-string" style="opacity: 0;">U</span></span> <button type="button" class="ant-btn ant-btn-sm" style="vertical-align: middle;"><span>改 变</span></button></div>`;
exports[`renders ./components/avatar/demo/type.md correctly 1`] = `<div><span class="ant-avatar ant-avatar-circle ant-avatar-icon"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span> <span class="ant-avatar ant-avatar-circle"><span class="ant-avatar-string">U</span></span> <span class="ant-avatar ant-avatar-circle"><span class="ant-avatar-string">USER</span></span> <span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"></span> <span class="ant-avatar ant-avatar-circle" style="color: rgb(245, 106, 0); background-color: rgb(253, 227, 207);"><span class="ant-avatar-string">U</span></span> <span class="ant-avatar ant-avatar-circle ant-avatar-icon" style="background-color: rgb(135, 208, 104);"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span></div>`; exports[`renders ./components/avatar/demo/type.md correctly 1`] = `<div><span class="ant-avatar ant-avatar-circle ant-avatar-icon"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span> <span class="ant-avatar ant-avatar-circle ant-avatar-icon"><i slot="icon" aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span> <span class="ant-avatar ant-avatar-circle"><span class="ant-avatar-string" style="opacity: 0;">U</span></span> <span class="ant-avatar ant-avatar-circle"><span class="ant-avatar-string" style="opacity: 0;">USER</span></span> <span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"></span> <span class="ant-avatar ant-avatar-circle" style="color: rgb(245, 106, 0); background-color: rgb(253, 227, 207);"><span class="ant-avatar-string" style="opacity: 0;">U</span></span> <span class="ant-avatar ant-avatar-circle ant-avatar-icon" style="background-color: rgb(135, 208, 104);"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span></div>`;

View File

@ -5,7 +5,7 @@
<us> <us>
#### With Badge #### With Badge
Usually used for messages remind. Usually used for reminders and notifications.
</us> </us>
```tpl ```tpl

View File

@ -12,6 +12,9 @@ Image, Icon and letter are supported, and the latter two kinds avatar can have c
<template> <template>
<div> <div>
<a-avatar icon="user" /> <a-avatar icon="user" />
<a-avatar>
<a-icon type="user" slot="icon" />
</a-avatar>
<a-avatar>U</a-avatar> <a-avatar>U</a-avatar>
<a-avatar>USER</a-avatar> <a-avatar>USER</a-avatar>
<a-avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" /> <a-avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />

View File

@ -2,7 +2,7 @@
| 参数 | 说明 | 类型 | 默认值 | | 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| icon | 设置头像的图标类型,参考 `Icon` 组件 | string | - | | icon | 设置头像的图标类型,可设为 Icon 的 `type` 或 VNode | string \| VNode \| slot | - |
| shape | 指定头像的形状 | Enum{ 'circle', 'square' } | `circle` | | shape | 指定头像的形状 | Enum{ 'circle', 'square' } | `circle` |
| size | 设置头像的大小 | number \| Enum{ 'large', 'small', 'default' } | `default` | | size | 设置头像的大小 | number \| Enum{ 'large', 'small', 'default' } | `default` |
| src | 图片类头像的资源地址 | string | - | | src | 图片类头像的资源地址 | string | - |