Last active
August 29, 2015 14:27
-
-
Save gilbox/a65b3bb23ef84627346f to your computer and use it in GitHub Desktop.
Idea for functional react scroll-based animation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const identity = x => x; | |
const topTop = containerRect => rect => | |
~~(rect.top - containerRect.top); | |
const translate3dFormatter = value => `translate3d(${value.join('px,')}px)`; | |
const translate3d = (...args) => ({ | |
value: args, | |
formatter: translate3dFormatter, | |
factory: translate3d | |
}) | |
function tweenValues(progress, a, b) { | |
if (a.value) { | |
if (!b.value) throw(Error('tweenValues mismatch: tried to tween wrapped and unwrapped values')); | |
return a.factory(...tweenValues(progress, a.value, b.value)); | |
} else { | |
if (a instanceof Array) { | |
if (!b instanceof Array) throw(Error('tweenValues expected two arrays but only found one')); | |
return a.map((value,index) => value + progress*(b[index] - value)); | |
} else { | |
return a + progress * (b-a); | |
} | |
} | |
} | |
const resolveValue = (value) => | |
x.value ? x.formatter(value) : x; | |
// currently only supports 2 keyframes | |
const tween = (position, keyframes) => { | |
const positions = Object.keys(keyframes); | |
const position0 = positions[0]; | |
const position1 = positions[1]; | |
if (position <= position0) return resolveValue(keyframes[position0]); | |
if (position >= position1) return resolveValue(keyframes[position1]); | |
const range = position1 - position0; | |
const delta = position - position0; | |
const progress = delta / range; | |
return resolveValue(tweenValues(progress, keyframes[position0], keyframes[position1])) | |
} | |
class DocumentRect extends Component { | |
static defaultProps = { formulas: [identity] } | |
constructor(props) { | |
super(props); | |
this.state = { rect: null }; | |
} | |
componentDidMount() { | |
window.addEventListener('scroll', event => { | |
this.setState({ rect: document.documentElement.getBoundingClientRect() }); | |
}); | |
} | |
render() { | |
const rect = {this.state}; | |
return this.props.children(...this.props.formulas.map(formula => formula(rect))) | |
} | |
} | |
class DivRect extends Component { | |
static defaultProps = { formulas: [identity] } | |
constructor(props) { | |
super(props); | |
this.state = {rect:null}; | |
} | |
componentWillReceiveProps() { | |
const node = React.findDOMNode(this.div); | |
const rect = node.getBoundingClientRect(); | |
this.setState({rect}); | |
} | |
render() { | |
const rect = {this.state}; | |
return <div ref={r => this.div = r} {...this.props}> | |
{this.props.children(...this.props.formulas.map(formula => formula(rect)))} | |
</div>; | |
} | |
} | |
const calculateScrollY = ({top}) => -top; | |
/// render | |
<DocumentRect formulas={[identity,calculateScrollY]}> | |
{(documentRect, scrollY) => | |
<DivRect formulas={[topTop(documentRect)]}> | |
{ (posTopTop) => | |
<h1 | |
style={{ | |
transform: tween(scrollY, { | |
[posTopTop]: translate3d(0, 150, 0), | |
[posTopTop+200]: translate3d(0, 100, 0) | |
}) | |
}}>Hello</h1> | |
}</DivRect> | |
</DocumentRect> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
a more sophisticated
tween
function could handle something like this:rgba
would work similar totranslate3d