Skip to content

Commit

Permalink
feat(message): add animation of faddeIn and fadeOut
Browse files Browse the repository at this point in the history
  • Loading branch information
Zack921 committed Mar 8, 2022
1 parent d2331dc commit c91b2b8
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 12 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@
"lodash": "^4.17.21",
"raf": "^3.4.1",
"tdesign-icons-vue": "~0.0.8",
"validator": "^13.5.1"
"validator": "^13.5.1",
"web-animations-js": "^2.3.2"
},
"peerDependencies": {
"vue": "^2.6.10"
Expand Down
88 changes: 88 additions & 0 deletions src/message/animation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { PLACEMENT_LIST } from './const';

const ANIMATION_OPTION = {
duration: 200,
easing: 'linear',
};

function fadeIn(dom: HTMLElement, placement: string) {
if (!dom) return;
const offsetWidth = dom?.offsetWidth || 0;
const offsetHeight = dom?.offsetHeight || 0;
const fadeInKeyframes: Array<Keyframe> | null = getFadeInKeyframes(placement, offsetWidth, offsetHeight);
if (!fadeInKeyframes) return;
const styleAfterFadeIn = fadeInKeyframes[fadeInKeyframes.length - 1];
setDomStyleAfterAnimation(dom, styleAfterFadeIn);
dom.animate(fadeInKeyframes, ANIMATION_OPTION);
}

function fadeOut(dom: HTMLElement, placement: string, onFinish: Function) {
if (!dom) return;
const offsetHeight = dom?.offsetHeight || 0;
const fadeOutKeyframes: Array<Keyframe> | null = getFadeOutKeyframes(placement, offsetHeight);
if (!fadeOutKeyframes) return;
const styleAfterFadeOut = fadeOutKeyframes[fadeOutKeyframes.length - 1];
setDomStyleAfterAnimation(dom, styleAfterFadeOut);

const animation = dom.animate(fadeOutKeyframes, ANIMATION_OPTION);
animation.onfinish = () => {
// eslint-disable-next-line no-param-reassign
dom.style.display = 'none';
onFinish();
};
}

function setDomStyleAfterAnimation(dom: HTMLElement, styleAfterAnimation: Keyframe) {
const keys = Object.keys(styleAfterAnimation);
for (let i = 0; i < keys.length; i += 1) {
const key = keys[i];
// eslint-disable-next-line no-param-reassign
dom.style[key] = styleAfterAnimation[key];
}
}

function getFadeInKeyframes(placement: string, offsetWidth: Number, offsetHeight: Number): Array<Keyframe> | null {
if (!PLACEMENT_LIST.includes(placement)) return null;
if (['top-left', 'left', 'bottom-left'].includes(placement)) {
return [
{ opacity: 0, marginLeft: `-${offsetWidth}px` },
{ opacity: 1, marginLeft: '0' },
];
}
if (['top-right', 'right', 'bottom-right'].includes(placement)) {
return [
{ opacity: 0, marginRight: `-${offsetWidth}px` },
{ opacity: 1, marginRight: '0' },
];
}
if (['top'].includes(placement)) {
return [
{ opacity: 0, marginTop: `-${offsetHeight}px` },
{ opacity: 1, marginTop: '0' },
];
}
if (['center', 'bottom'].includes(placement)) {
return [
{ opacity: 0, transform: `translate3d(0, ${offsetHeight}px, 0)` },
{ opacity: 1, transform: 'translate3d(0, 0, 0)' },
];
}
}

function getFadeOutKeyframes(placement: string, offsetHeight: Number): Array<Keyframe> | null {
if (!PLACEMENT_LIST.includes(placement)) return null;
if (['bottom-left', 'bottom', 'bottom-right'].includes(placement)) {
const marginOffset = `${offsetHeight}px`;
return [
{ opacity: 1, marginTop: '0px' },
{ opacity: 0, marginTop: marginOffset },
];
}
const marginOffset = `-${offsetHeight}px`;
return [
{ opacity: 1, marginTop: '0px' },
{ opacity: 0, marginTop: marginOffset },
];
}

export { fadeIn, fadeOut };
18 changes: 10 additions & 8 deletions src/message/const.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
export const THEME_LIST: string[] = [
'info',
'success',
'warning',
'error',
'question',
'loading',
];
export const THEME_LIST: string[] = ['info', 'success', 'warning', 'error', 'question', 'loading'];

const DISTANCE = '32px';

Expand Down Expand Up @@ -34,6 +27,9 @@ export const PLACEMENT_OFFSET = {
right: DISTANCE,
top: '50%',
transform: 'translateY(-50%)',
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-end',
},
'top-left': {
left: DISTANCE,
Expand All @@ -42,10 +38,16 @@ export const PLACEMENT_OFFSET = {
'top-right': {
right: DISTANCE,
top: DISTANCE,
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-end',
},
'bottom-right': {
right: DISTANCE,
bottom: DISTANCE,
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-end',
},
'bottom-left': {
left: DISTANCE,
Expand Down
1 change: 1 addition & 0 deletions src/message/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'web-animations-js'; // web animations polyfill
import _Message from './message';
import withInstall from '../utils/withInstall';
import { TdMessageProps } from './type';
Expand Down
18 changes: 15 additions & 3 deletions src/message/message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { THEME_LIST } from './const';
import { renderTNodeJSX, renderContent } from '../utils/render-tnode';
import props from './props';
import { ClassName } from '../common';
import { fadeIn, fadeOut } from './animation';

const name = `${prefix}-message`;

Expand All @@ -27,7 +28,10 @@ export default Vue.extend({
Loading,
},

props: { ...props },
props: {
...props,
placement: String, // just for animation
},

data() {
return {
Expand Down Expand Up @@ -55,6 +59,11 @@ export default Vue.extend({
this.duration && this.setTimer();
},

mounted() {
const msgDom = this.$refs.msg as HTMLElement;
fadeIn(msgDom, this.$props.placement);
},

methods: {
setTimer() {
if (!this.duration) {
Expand All @@ -63,7 +72,10 @@ export default Vue.extend({
this.timer = Number(
setTimeout(() => {
this.clearTimer();
this.$emit('duration-end');
const msgDom = this.$refs.msg as HTMLElement;
fadeOut(msgDom, this.$props.placement, () => {
this.$emit('duration-end');
});
if (this.onDurationEnd) {
this.onDurationEnd();
}
Expand Down Expand Up @@ -107,7 +119,7 @@ export default Vue.extend({

render() {
return (
<div class={this.classes} onMouseenter={this.clearTimer} onMouseleave={this.setTimer}>
<div ref="msg" class={this.classes} onMouseenter={this.clearTimer} onMouseleave={this.setTimer}>
{this.renderIcon()}
{renderContent(this, 'default', 'content')}
{this.renderClose()}
Expand Down
1 change: 1 addition & 0 deletions src/message/messageList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const MessageList = Vue.extend({
const mg = {
...msg,
key: getUniqueId(),
placement: this.placement,
};
this.list.push(mg);
return this.list.length - 1;
Expand Down

0 comments on commit c91b2b8

Please sign in to comment.