这是我参与更文挑战的第7天,活动详情查看: 更文挑战css
用antd自带菜单导航,点击菜单项的时候并无什么特别cool的效果,那咱们就本身动手加上吧。react
大佬的实现git
在其基础上我补全了功能,使其可以解决我所遇到的问题:github
首先菜单项是多项而且单项点击切换的,大佬的实现并不能很好完成这一点,当存在多个具有该特效的节点时,一旦刷新,全部的节点都会触发刷新,那就是满屏的波纹,这一点必须解决。spring
首先作成一个高阶组件,这样就可使用ta来对其余组件进行包装,使其具备点击特效,方便使用。数组
组件命名为withMaterialHoc
,先贴出组件的实现:markdown
withMaterialHoc/index.js
antd
import React from 'react';
import { Ripple, calcEventRelativePos } from './ripple';
let withMaterialHoc = (WrappedComponent) => {
return class WithMaterial extends React.Component {
constructor(props) {
super(props)
this.isSwitchIntl = false;
this.state = {
spawnData: "",
clickCount: 0,
isSwitchIntl: false
}
}
handleClick = (event) => {
this.setState({
spawnData: calcEventRelativePos(event),
time: Date.now(),
clickCount: this.state.clickCount + 1
});
}
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.intlData != this.props.intlData) {
return true;
}
if (nextState.clickCount && (nextState.clickCount - this.state.clickCount === 1)) {
return true
} else {
return false
}
}
handleRippleEnd = () => {
let value = this.state.clickCount - 1
if (value < 0) {
value = 0
}
this.setState({
clickCount: value
})
}
render() {
const { spawnData } = this.state;
const { className, style } = this.props;
return (
<div className={`g-btn ${className || ' '}`} onClick={this.handleClick} style={style} > <WrappedComponent {...this.props} /> <Ripple handleRippleEnd={this.handleRippleEnd} spawnData={spawnData} /> </div>
);
}
};
}
export default withMaterialHoc;
复制代码
ripple.js
:app
import React, { useState, useEffect, useRef, Fragment,memo } from 'react';
import './index.css';
import { useSpring, animated } from 'react-spring';
function calcEventRelativePos(event) {
const rect = event.target.getBoundingClientRect();
return {
x: event.clientX - rect.left,
y: event.clientY - rect.top,
};
}
function Ripple(props) {
const [data, setData] = useState({ top: 0, left: 0, width: 0, height: 0 });
const isInit = useRef(true);
const rippleEl = useRef(null);
const { spawnData, handleRippleEnd} = props;
const rippleAnim = useSpring({
from: {
...props.style,
...data,
transform: 'scale(0)',
opacity: 1,
},
to: !isInit.current ? { opacity: 0, transform: 'scale(2)' } : {},
config: {
duration: props.duration || 300,
},
onRest: () => {
handleRippleEnd()
},
reset: true
});
useEffect(() => {
if (isInit.current) {
isInit.current = false;
} else {
const parentEl = rippleEl.current.parentElement;
const size = Math.max(parentEl.offsetWidth, parentEl.offsetHeight);
setData({
width: size,
height: size,
top: spawnData.y - size / 2 || 0,
left: spawnData.x - size / 2+50 || 50
});
}
}, [spawnData]);
return (
<animated.span className="g-ripple" style={rippleAnim} ref={rippleEl} ></animated.span>
);
}
Ripple = memo(Ripple)
export { Ripple, calcEventRelativePos };
复制代码
由于ripple
是一个函数组件,每次父组件刷新,子组件就会刷新,从而出发了动画(实现用的是react-spring
库,有兴趣能够深刻了解)。函数
那么解决这个问题的关键就是,我点击哪一个节点,哪一个节点触发刷新,其余的节点不刷新。
React.memo 为高阶组件。它与 React.PureComponent 很是类似,但它适用于函数组件,但不适用于 class 组件。
首先若是父组件传入子组件的props
不改变或者是引用的地址不变(浅比较),那么被memo
包装过的函数组件就不会触发刷新,so,问题就真么愉快的解决了。
效果就是宁缺毋滥的,有时恰到好处的点缀,会起到意想不到的效果,内外兼修,才是最佳的实现,赶忙加上试试。