From c91b2b8c212a5983aea95602c6b3425c7c51cddb Mon Sep 17 00:00:00 2001 From: Zack921 <407473490@qq.com> Date: Fri, 11 Feb 2022 22:27:50 +0800 Subject: [PATCH 1/2] feat(message): add animation of faddeIn and fadeOut --- package.json | 3 +- src/message/animation.ts | 88 +++++++++++++++++++++++++++++++++++++ src/message/const.ts | 18 ++++---- src/message/index.ts | 1 + src/message/message.tsx | 18 ++++++-- src/message/messageList.tsx | 1 + 6 files changed, 117 insertions(+), 12 deletions(-) create mode 100644 src/message/animation.ts diff --git a/package.json b/package.json index 6cfa96970..38c367c0f 100755 --- a/package.json +++ b/package.json @@ -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" diff --git a/src/message/animation.ts b/src/message/animation.ts new file mode 100644 index 000000000..3847ca3aa --- /dev/null +++ b/src/message/animation.ts @@ -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 | 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 | 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 | 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 | 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 }; diff --git a/src/message/const.ts b/src/message/const.ts index 9d54758ad..3f655477f 100644 --- a/src/message/const.ts +++ b/src/message/const.ts @@ -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'; @@ -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, @@ -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, diff --git a/src/message/index.ts b/src/message/index.ts index 34d7206da..d01124b25 100644 --- a/src/message/index.ts +++ b/src/message/index.ts @@ -1,3 +1,4 @@ +import 'web-animations-js'; // web animations polyfill import _Message from './message'; import withInstall from '../utils/withInstall'; import { TdMessageProps } from './type'; diff --git a/src/message/message.tsx b/src/message/message.tsx index 2b044f922..f2dd0df92 100644 --- a/src/message/message.tsx +++ b/src/message/message.tsx @@ -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`; @@ -27,7 +28,10 @@ export default Vue.extend({ Loading, }, - props: { ...props }, + props: { + ...props, + placement: String, // just for animation + }, data() { return { @@ -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) { @@ -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(); } @@ -107,7 +119,7 @@ export default Vue.extend({ render() { return ( -
+
{this.renderIcon()} {renderContent(this, 'default', 'content')} {this.renderClose()} diff --git a/src/message/messageList.tsx b/src/message/messageList.tsx index 47121b9b4..d70ccb91c 100644 --- a/src/message/messageList.tsx +++ b/src/message/messageList.tsx @@ -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; From ece64d5daf3b21457baa33aa48d937b6a7afae54 Mon Sep 17 00:00:00 2001 From: Zack921 <407473490@qq.com> Date: Tue, 8 Mar 2022 20:26:47 +0800 Subject: [PATCH 2/2] test: handle unit test error of message --- package.json | 3 +-- src/message/animation.ts | 2 +- src/message/index.ts | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 38c367c0f..6cfa96970 100755 --- a/package.json +++ b/package.json @@ -79,8 +79,7 @@ "lodash": "^4.17.21", "raf": "^3.4.1", "tdesign-icons-vue": "~0.0.8", - "validator": "^13.5.1", - "web-animations-js": "^2.3.2" + "validator": "^13.5.1" }, "peerDependencies": { "vue": "^2.6.10" diff --git a/src/message/animation.ts b/src/message/animation.ts index 3847ca3aa..46f7bdcfb 100644 --- a/src/message/animation.ts +++ b/src/message/animation.ts @@ -20,7 +20,7 @@ function fadeOut(dom: HTMLElement, placement: string, onFinish: Function) { if (!dom) return; const offsetHeight = dom?.offsetHeight || 0; const fadeOutKeyframes: Array | null = getFadeOutKeyframes(placement, offsetHeight); - if (!fadeOutKeyframes) return; + if (!fadeOutKeyframes) return onFinish(); const styleAfterFadeOut = fadeOutKeyframes[fadeOutKeyframes.length - 1]; setDomStyleAfterAnimation(dom, styleAfterFadeOut); diff --git a/src/message/index.ts b/src/message/index.ts index d01124b25..34d7206da 100644 --- a/src/message/index.ts +++ b/src/message/index.ts @@ -1,4 +1,3 @@ -import 'web-animations-js'; // web animations polyfill import _Message from './message'; import withInstall from '../utils/withInstall'; import { TdMessageProps } from './type';