-
-
Notifications
You must be signed in to change notification settings - Fork 290
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
acf74c4
commit cf23cb1
Showing
8 changed files
with
291 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
```js | ||
render( | ||
<Arwes> | ||
<h3><Words animate>A cyberpunk UI project</Words></h3> | ||
<p><Words animate> | ||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus, amet | ||
cupiditate laboriosam sunt libero aliquam, consequatur alias ducimus adipisci | ||
nesciunt odit? Odio tenetur et itaque suscipit atque officiis debitis qui. | ||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus, amet | ||
cupiditate laboriosam sunt libero aliquam, consequatur alias ducimus adipisci | ||
nesciunt odit? Odio tenetur et itaque suscipit atque officiis debitis qui. | ||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus, amet | ||
cupiditate laboriosam sunt libero aliquam, consequatur alias ducimus adipisci | ||
nesciunt odit? Odio tenetur et itaque suscipit atque officiis debitis qui. | ||
</Words></p> | ||
<p><Words animate layer='success'> | ||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus, amet | ||
cupiditate laboriosam sunt libero aliquam, consequatur alias ducimus adipisci | ||
nesciunt odit? Odio tenetur et itaque suscipit atque officiis debitis qui. | ||
</Words></p> | ||
<p><Words animate layer='alert'>With animations based on SciFi and designs from high technology</Words></p> | ||
</Arwes> | ||
); | ||
``` |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
A plain text wrapper component to apply styles and animation. | ||
|
||
```js | ||
<Words animate>Arwes is a cyberpunk UI framework</Words> | ||
``` |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
import React, { Component } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import cx from 'classnames'; | ||
|
||
export default class Words extends Component { | ||
|
||
static propTypes = { | ||
theme: PropTypes.object.isRequired, | ||
classes: PropTypes.object.isRequired, | ||
animate: PropTypes.bool, | ||
show: PropTypes.bool, | ||
layer: PropTypes.oneOf(['primary', 'secondary', 'header', 'control', 'success', 'alert', 'disabled']), | ||
blinkText: PropTypes.string, | ||
animationMaxDuration: PropTypes.number, | ||
children: PropTypes.string.isRequired, | ||
}; | ||
|
||
static defaultProps = { | ||
animate: false, | ||
show: true, | ||
layer: 'primary', | ||
blinkText: '▋', | ||
animationMaxDuration: 2000, | ||
}; | ||
|
||
constructor () { | ||
super(...arguments); | ||
|
||
this.state = { | ||
text: '', | ||
animating: false, | ||
}; | ||
} | ||
|
||
componentDidMount () { | ||
if (this.props.animate && this.props.show) { | ||
this.animateIn(); | ||
} | ||
} | ||
|
||
componentDidUpdate (prevProps) { | ||
|
||
const { animate, show, children } = this.props; | ||
|
||
const animateChanged = animate !== prevProps.animate; | ||
const showChanged = show !== prevProps.show; | ||
const childrenChanged = children !== prevProps.children; | ||
|
||
// Animation changed | ||
if (animate) { | ||
if (showChanged) { | ||
show ? this.animateIn() : this.animateOut(); | ||
} | ||
else if (childrenChanged) { | ||
this.animateIn(); | ||
} | ||
} | ||
|
||
// Not animated anymore | ||
if (!animate && animateChanged) { | ||
this.stopAnimation(); | ||
} | ||
} | ||
|
||
componentWillUnmount () { | ||
this.stopAnimation(); | ||
} | ||
|
||
render () { | ||
const { | ||
theme, | ||
classes, | ||
animate, | ||
show, | ||
layer, | ||
blinkText, | ||
animationMaxDuration, | ||
className, | ||
children, | ||
...etc | ||
} = this.props; | ||
|
||
const { animating, text } = this.state; | ||
|
||
const cls = cx(classes.root, { | ||
[classes.hide]: animate && !show && !animating, | ||
[classes.animating]: animating | ||
}, className); | ||
|
||
return ( | ||
<span className={cx(cls)} {...etc}> | ||
<span className={classes.children}> | ||
{children} | ||
{animating && ( | ||
<span | ||
className={classes.space} | ||
dangerouslySetInnerHTML={{ __html: blinkText }} | ||
/> | ||
)} | ||
</span> | ||
{animating && ( | ||
<span className={classes.text}> | ||
{text} | ||
<span | ||
className={classes.blink} | ||
dangerouslySetInnerHTML={{ __html: blinkText }} | ||
/> | ||
</span> | ||
)} | ||
</span> | ||
); | ||
} | ||
|
||
animateIn () { | ||
this.cancelNextAnimation(); | ||
this.startAnimation(true); | ||
} | ||
|
||
animateOut () { | ||
this.cancelNextAnimation(); | ||
this.startAnimation(false); | ||
} | ||
|
||
stopAnimation () { | ||
this.cancelNextAnimation(); | ||
this.setState({ animating: false }); | ||
} | ||
|
||
cancelNextAnimation () { | ||
window.cancelAnimationFrame(this.currentAnimationFrame); | ||
} | ||
|
||
startAnimation (isIn) { | ||
const { theme, children, animationMaxDuration } = this.props; | ||
|
||
if (children.length === 0) return; | ||
|
||
// 1s / frames per second (FPS) | ||
// 60 FPS are the default in requestAnimationFrame | ||
const interval = 1000 / 60; | ||
|
||
// The time it will take to add/remove a character per frame | ||
const realDuration = interval * children.length; | ||
|
||
// Duration, min is theme.animTime and max is props.animationMaxDuration | ||
const duration = isIn | ||
? Math.max(Math.min(realDuration, animationMaxDuration), theme.animTime) | ||
: theme.animTime; | ||
|
||
this.cancelNextAnimation(); | ||
|
||
this.setState({ | ||
animating: true, | ||
text: isIn ? '' : children | ||
}); | ||
|
||
const length = children.length; | ||
let start = performance.now(); | ||
let progress = null; | ||
|
||
const nextAnimation = (timestamp) => { | ||
if (!start) { | ||
start = timestamp; | ||
} | ||
|
||
progress = Math.max(timestamp - start, 0); | ||
if (!isIn) { | ||
progress = duration - progress; | ||
} | ||
|
||
// partialLength(n) = animationProgressDuration(ms) | ||
// textTotalLength(n) = totalDuration(ms) | ||
const newLength = Math.round((progress * length) / duration); | ||
const text = children.substring(0, newLength); | ||
|
||
this.setState({ text }); | ||
|
||
const continueAnimation = isIn | ||
? newLength <= length | ||
: newLength > 0; | ||
|
||
if (continueAnimation) { | ||
this.currentAnimationFrame = window.requestAnimationFrame(nextAnimation); | ||
} else { | ||
this.stopAnimation(); | ||
} | ||
}; | ||
|
||
this.currentAnimationFrame = window.requestAnimationFrame(nextAnimation); | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import React from 'react'; | ||
import testSetup from '../test-setup'; | ||
import Words from './Words'; | ||
|
||
const { mount } = testSetup(Words); | ||
|
||
describe('Words', function () { | ||
|
||
it('Should render without crashing', function () { | ||
mount({ | ||
Animation: ({ children }) => children({}) | ||
}, 'Random text'); | ||
}); | ||
|
||
}); |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import withStyles from 'react-jss/lib/injectSheet'; | ||
import Words from './Words'; | ||
import styles from './styles'; | ||
|
||
export default withStyles(styles)(Words); |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
export default (theme) => { | ||
return { | ||
root: { | ||
position: 'relative', | ||
display: 'inline-block', | ||
color: props => theme.color[props.layer].base, | ||
}, | ||
children: { | ||
display: 'inline-block', | ||
}, | ||
space: { | ||
visibility: 'hidden', | ||
opacity: 0, | ||
}, | ||
text: { | ||
position: 'absolute', | ||
left: 0, | ||
top: 0, | ||
display: 'inline-block', | ||
opacity: 0, | ||
}, | ||
blink: { | ||
display: 'inline-block', | ||
animation: `arwes-words-blink ${theme.animTime}ms step-end infinite`, | ||
}, | ||
hide: { | ||
opacity: 0, | ||
}, | ||
animating: { | ||
'& $children': { | ||
opacity: 0, | ||
}, | ||
'& $text': { | ||
opacity: 1, | ||
}, | ||
}, | ||
|
||
'@keyframes arwes-words-blink': { | ||
'0%, 100%': { | ||
color: 'rgba(0,0,0,0)', | ||
}, | ||
'50%': { | ||
color: 'inherit', | ||
}, | ||
}, | ||
}; | ||
}; |
This file contains 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