From b4d95f7e204b4d11c0568954c2616dab83a67297 Mon Sep 17 00:00:00 2001 From: afc163 Date: Wed, 8 Aug 2018 16:01:07 +0800 Subject: [PATCH] Refactor button click effect implementation for better perfermance --- components/button/button.tsx | 23 +++++------ components/button/clickAnimation.tsx | 20 +++++++++ components/button/style/index.less | 41 ++++++++++--------- .../__snapshots__/DrawerEvent.test.js.snap | 2 +- typings/custom-typings.d.ts | 2 +- 5 files changed, 53 insertions(+), 35 deletions(-) create mode 100644 components/button/clickAnimation.tsx diff --git a/components/button/button.tsx b/components/button/button.tsx index cbe2831173..b610f22961 100644 --- a/components/button/button.tsx +++ b/components/button/button.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { findDOMNode } from 'react-dom'; import * as PropTypes from 'prop-types'; import classNames from 'classnames'; +import clickAnimation from './clickAnimation'; import Icon from '../icon'; import Group from './button-group'; @@ -86,20 +87,22 @@ export default class Button extends React.Component { block: PropTypes.bool, }; - timeout: number; delayTimeout: number; + clickAnimation: { + cancel: () => void; + }; constructor(props: ButtonProps) { super(props); this.state = { loading: props.loading, - clicked: false, hasTwoCNChar: false, }; } componentDidMount() { this.fixTwoCNChar(); + this.clickAnimation = clickAnimation(findDOMNode(this) as HTMLElement); } componentWillReceiveProps(nextProps: ButtonProps) { @@ -122,12 +125,12 @@ export default class Button extends React.Component { } componentWillUnmount() { - if (this.timeout) { - clearTimeout(this.timeout); - } if (this.delayTimeout) { clearTimeout(this.delayTimeout); } + if (this.clickAnimation) { + this.clickAnimation.cancel(); + } } fixTwoCNChar() { @@ -148,12 +151,7 @@ export default class Button extends React.Component { } handleClick: React.MouseEventHandler = e => { - // Add click effect - this.setState({ clicked: true }); - clearTimeout(this.timeout); - this.timeout = window.setTimeout(() => this.setState({ clicked: false }), 500); - - const onClick = this.props.onClick; + const { onClick } = this.props; if (onClick) { (onClick as React.MouseEventHandler)(e); } @@ -169,7 +167,7 @@ export default class Button extends React.Component { type, shape, size, className, children, icon, prefixCls, ghost, loading: _loadingProp, block, ...rest } = this.props; - const { loading, clicked, hasTwoCNChar } = this.state; + const { loading, hasTwoCNChar } = this.state; // large => lg // small => sm @@ -190,7 +188,6 @@ export default class Button extends React.Component { [`${prefixCls}-${sizeCls}`]: sizeCls, [`${prefixCls}-icon-only`]: !children && icon, [`${prefixCls}-loading`]: loading, - [`${prefixCls}-clicked`]: clicked, [`${prefixCls}-background-ghost`]: ghost, [`${prefixCls}-two-chinese-chars`]: hasTwoCNChar, [`${prefixCls}-block`]: block, diff --git a/components/button/clickAnimation.tsx b/components/button/clickAnimation.tsx new file mode 100644 index 0000000000..e9c90e19d2 --- /dev/null +++ b/components/button/clickAnimation.tsx @@ -0,0 +1,20 @@ +import TransitionEvents from 'css-animation/lib/Event'; + +const clickAnimation = (node: HTMLElement) => { + function handler() { + node.removeAttribute('ant-click-animating'); + node.setAttribute('ant-click-animating', 'true'); + TransitionEvents.addEndEventListener(node, () => { + node.removeAttribute('ant-click-animating'); + TransitionEvents.removeEndEventListener(node); + }); + } + node.addEventListener('click', handler, false); + return { + cancel: () => { + node.removeEventListener('click', handler, false); + }, + }; +}; + +export default clickAnimation; diff --git a/components/button/style/index.less b/components/button/style/index.less index eec2b2afa4..c73813d16b 100644 --- a/components/button/style/index.less +++ b/components/button/style/index.less @@ -8,6 +8,7 @@ @btn-ghost-color: @text-color; @btn-ghost-bg: transparent; @btn-ghost-border: @border-color-base; +@btn-animation-width: 6px; // Button styles // ----------------------------- @@ -131,21 +132,7 @@ margin-left: 8px; } - &-clicked:after { - content: ''; - position: absolute; - top: -1px; - left: -1px; - bottom: -1px; - right: -1px; - border-radius: inherit; - border: 0 solid @primary-color; - opacity: 0.4; - animation: buttonEffect .4s; - display: block; - } - - &-danger&-clicked:after { + &-danger[ant-click-animating]:after { border-color: @btn-danger-color; } @@ -180,11 +167,11 @@ @keyframes buttonEffect { to { opacity: 0; - top: -6px; - left: -6px; - bottom: -6px; - right: -6px; - border-width: 6px; + top: -@btn-animation-width; + left: -@btn-animation-width; + bottom: -@btn-animation-width; + right: -@btn-animation-width; + border-width: @btn-animation-width; } } @@ -197,3 +184,17 @@ a.@{btn-prefix-cls} { line-height: @btn-height-sm - 2px; } } + +[ant-click-animating]:after { + content: ''; + position: absolute; + top: -1px; + left: -1px; + bottom: -1px; + right: -1px; + border-radius: inherit; + border: 0 solid @primary-color; + opacity: 0.4; + animation: buttonEffect .4s cubic-bezier(.25, .8, .25, 1); + display: block; +} diff --git a/components/drawer/__tests__/__snapshots__/DrawerEvent.test.js.snap b/components/drawer/__tests__/__snapshots__/DrawerEvent.test.js.snap index 875ab4c752..fdf8f3e5d7 100644 --- a/components/drawer/__tests__/__snapshots__/DrawerEvent.test.js.snap +++ b/components/drawer/__tests__/__snapshots__/DrawerEvent.test.js.snap @@ -3,7 +3,7 @@ exports[`Drawer render correctly 1`] = `