Skip to content

Instantly share code, notes, and snippets.

@thomas-jeepe
Created November 4, 2015 02:58
Show Gist options
  • Save thomas-jeepe/2e167b0b8713ec03e5cb to your computer and use it in GitHub Desktop.
Save thomas-jeepe/2e167b0b8713ec03e5cb to your computer and use it in GitHub Desktop.
Material design button with react-motion
import React, { Component } from 'react';
import ReactDom from 'react-dom'
import { TransitionMotion, spring, Motion } from 'react-motion';
class RaisedButton extends Component {
constructor() {
super()
this.state = {mouse: [], now: 't' + 0, height: 0, width: 0}
}
handleClick({pageX, pageY}) {
let calcDiag = (a, b) => Math.sqrt((a * a) + (b * b));
let style = {};
const el = ReactDom.findDOMNode(this);
const elHeight = el.offsetHeight;
const elWidth = el.offsetWidth;
let rect = el.getBoundingClientRect();
let offset = {
top: rect.top + document.body.scrollTop,
left: rect.left + document.body.scrollLeft,
};
const pointerX = pageX - offset.left;
const pointerY = pageY - offset.top;
const topLeftDiag = calcDiag(pointerX, pointerY);
const topRightDiag = calcDiag(elWidth - pointerX, pointerY);
const botRightDiag = calcDiag(elWidth - pointerX, elHeight - pointerY);
const botLeftDiag = calcDiag(pointerX, elHeight - pointerY);
const rippleRadius = Math.max(
topLeftDiag, topRightDiag, botRightDiag, botLeftDiag
);
const rippleSize = rippleRadius * 2;
const left = pointerX - rippleRadius;
const top = pointerY - rippleRadius;
this.setState({
mouse: [left, top],
now: 't' + Date.now(),
height: rippleSize,
width: rippleSize,
active: true
})
}
handleMouseUp() {
this.setState(() => {
return {
mouse: [null, null],
active: false
};
});
}
handleMouseLeave() {
this.setState({hovered:false})
}
handleMouseOver() {
this.setState({hovered:true})
}
willLeave(key, valOfKey) {
return {
...valOfKey,
opacity: spring(0, [170, 20])
};
}
willEnter(key, valOfKey) {
return {
...valOfKey,
opacity: 0,
scale: 0,
};
}
render() {
const {mouse: [mouseX, mouseY], now, height, width, active, hovered} = this.state
let {
label,
...other
} = this.props
const styles = mouseX == null ? {} : {
[now]: {
opacity: spring(.9, [200, 10]),
scale: spring(1, [140, 9]),
x: spring(mouseX),
y: spring(mouseY),
width,
height
}
}
return (
<TransitionMotion
willEnter={this.willEnter}
willLeave={this.willLeave}
styles={styles}>
{circles =>
<Motion style={{
background: spring(hovered?.1566:0, [400,40]),
firstColor: spring(active?.124:.1566, [50,40]),
firstPixel: spring(active?3:1, [50,40]),
secondPixel: spring(active?10:6, [50,40]),
secondColor: spring(active?.227:.24, [50,40]),
thirdPixel: spring(active?3:1, [50,40]),
fourthPixel: spring(active?10:4, [50,40])
}}>
{val => {
return <div
onMouseDown={::this.handleClick}
onMouseUp={::this.handleMouseUp}
onMouseLeave={::this.handleMouseLeave}
onMouseOver={::this.handleMouseOver}
{...other}
style={{
backgroundColor: val.background > 0 ? `rgba(0,0,0,${val.background})`:'#ffffff',
boxShadow: `rgba(0,0,0,${val.firstColor}) 0px ${val.firstPixel}px ${val.secondPixel}px, rgba(0,0,0,${val.secondColor}) 0px ${val.thirdPixel}px ${val.fourthPixel}px`,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
position: 'absolute',
overflow: 'hidden',
width: 'auto',
minWidth: '64px',
height: 'auto',
minHeight: '36px',
borderRadius: '3px',
}}>
{Object.keys(circles).map(key => {
const { opacity, scale, x, y, width, height } = circles[key]
return (
<div
key={key}
style={{
opacity: opacity,
scale: scale,
width: width,
height: height,
left: x,
top: y,
transform: `scale(${scale})`,
WebkitTransform: `scale(${scale})`,
borderRadius: '100%',
position: 'absolute',
backgroundColor: 'rgba(0,0,0, 0.1234314)',
}} />
)
})}
<span style={{userSelect: 'none', fontFamily: 'Roboto'}}>{this.props.label}</span>
</div>
}
}
</Motion>
}
</TransitionMotion>
);
}
}
class MainPage extends Component {
render() {
return <RaisedButton label='hi' onClick={v => console.log('called')} />
}
}
export default MainPage
/*
It is still new, made it today so it is not nearly as refined as material-ui but
in the future it will have better support for react-native and a much smaller library size.
Although this is for a personal project, I might make it a library if someone wants to contribute
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment