principles-ui-components
Version:
Supporting UI controller for Tizen TV web application, which developed base on React Framework.
246 lines (223 loc) • 8.88 kB
JavaScript
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { is, Map } from 'immutable';
import CommonAPI from './CommonAPI';
export default class ScrollText extends Component {
constructor(props) {
super(props);
this.state = {
textAniName: '',
textAniDuration: '',
textAniDelay: '',
};
this.animationDone = this.animationDone.bind(this);
}
componentWillMount() {
const { children, fontSize, fontFamily, width, textGap } = this.props;
this.textWidth = CommonAPI.getTextWidth(children, fontSize, fontFamily);
this.aniName = `Text${this.textWidth}${textGap}`;
if (this.textWidth > width) {
const movePx = this.textWidth + textGap;
this.style = document.createElement('style');
this.style.type = 'text/css';
const keyFrames = ` ${this.aniName}1
{
0% {transform: translateX(0px) translateZ(100px);}
100% {transform: translateX(-${movePx}px) translateZ(100px);}
}
${this.aniName}2
{
0% {transform: translateX(0px) translateZ(100px);}
100% {transform: translateX(-${movePx}px) translateZ(100px);}
}
`;
this.style.innerHTML = keyFrames;
document.getElementsByTagName('head')[0].appendChild(this.style);
}
}
componentDidMount() {
this.text.addEventListener('animationend', this.animationDone, false);
if (this.state.textAniName === '') {
if (this.props.scroll) {
const { width } = this.props;
if (this.textWidth > width) {
this.onMount();
}
}
}
}
componentWillReceiveProps(nextProps) {
const { children, fontSize, fontFamily } = nextProps;
if (nextProps.children !== this.props.children ||
nextProps.fontSize !== this.props.fontSize ||
nextProps.textGap !== this.props.textGap ||
nextProps.width !== this.props.width ||
nextProps.fontFamily !== this.props.fontFamily) {
this.textWidth = CommonAPI.getTextWidth(children, fontSize, fontFamily);
this.aniName = `Text${this.textWidth}${nextProps.textGap}`;
if (this.textWidth > nextProps.width) {
const movePx = this.textWidth + nextProps.textGap;
this.removeStyleFromHeader();
this.style = document.createElement('style');
this.style.type = 'text/css';
const keyFrames = ` ${this.aniName}1
{
0% {transform: translateX(0px) translateZ(100px);}
100% {transform: translateX(-${movePx}px) translateZ(100px);}
}
${this.aniName}2
{
0% {transform: translateX(0px) translateZ(100px);}
100% {transform: translateX(-${movePx}px) translateZ(100px);}
}
`;
this.style.innerHTML = keyFrames;
document.getElementsByTagName('head')[0].appendChild(this.style);
}
}
if (!nextProps.scroll) {
this.setState({
textAniName: '',
textAniDuration: '',
textAniDelay: '',
});
}
}
shouldComponentUpdate(nextProps, nextState) {
return (JSON.stringify(nextProps) !== JSON.stringify(this.props)) || (JSON.stringify(nextState) !== JSON.stringify(this.state));
}
componentDidUpdate() {
if (this.state.textAniName === '') {
if (this.props.scroll) {
const { width } = this.props;
if (this.textWidth > width) {
this.onMount();
}
}
}
}
componentWillUnmount() {
this.removeStyleFromHeader();
this.text.removeEventListener('animationend', this.animationDone, false);
}
onMount() {
this.setState({
textAniName: `${this.aniName}1`,
textAniDuration: `${(this.textWidth + this.props.textGap) / 50}s`,
textAniDelay: '1s',
});
}
removeStyleFromHeader() {
if (this.style) {
document.getElementsByTagName('head')[0].removeChild(this.style);
this.style = null;
}
}
animationDone(e) {
e.preventDefault();
const target = e.target;
if (target === e.currentTarget) {
if (e.type === 'animationend') {
let textAni = this.state.textAniName;
if (textAni === `${this.aniName}1`) {
textAni = `${this.aniName}2`;
} else {
textAni = `${this.aniName}1`;
}
this.setState({ textAniName: textAni });
}
}
}
render() {
const { scroll, children, left, top, width, height, lineHeight, fontSize, fontFamily, color, textAlign, verticalAlign } = this.props;
const { textAniName, textAniDuration, textAniDelay } = this.state;
const style = {
position: 'absolute',
overflow: 'hidden',
left,
top,
width,
height,
lineHeight,
textAlign,
verticalAlign,
fontSize,
fontFamily,
color,
};
if (scroll && this.textWidth > width) {
style.textOverflow = '';
const aniStyle = {
position: 'absolute',
left: 0,
top: 0,
width: (2 * this.textWidth) + this.props.textGap,
};
aniStyle.animationName = textAniName;
aniStyle.animationDuration = textAniDuration;
aniStyle.animationDelay = textAniDelay;
aniStyle.animationTimingFunction = 'linear';
const text1Style = {
position: 'absolute',
left: 0,
top: 0,
animationTimingFunction: 'linear',
animationFillMode: 'both',
};
const text2Style = {
position: 'absolute',
left: this.textWidth + this.props.textGap,
top: 0,
animationTimingFunction: 'linear',
animationFillMode: 'both',
};
return (<div style={style}>
<div style={aniStyle} ref={(text) => { this.text = text; }}>
<div style={text1Style} ref='text1'>{children}</div>
<div style={text2Style} ref='text2' aria-hidden='true'>{children}</div>
</div>
</div>);
}
const text3Style = {
position: 'absolute',
left: 0,
top: 0,
width,
height,
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
};
return (<div style={style} ref={(text) => { this.text = text; }}>
<div style={text3Style} ref='text3'>{children}</div>
</div>);
}
}
ScrollText.defaultProps = {
left: 0,
top: 0,
width: 195,
height: 40,
lineHeight: '40px',
textAlign: 'left',
verticalAlign: 'middle',
scroll: true,
fontSize: 26,
fontFamily: 'SamsungOneGui_600',
textGap: 60,
color: '#000000',
};
ScrollText.propTypes = {
left: PropTypes.number,
top: PropTypes.number,
width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
lineHeight: PropTypes.string,
textAlign: PropTypes.oneOf(['left', 'center', 'right']),
verticalAlign: PropTypes.oneOf(['top', 'middle', 'bottom']),
scroll: PropTypes.bool.isRequired,
fontSize: PropTypes.number.isRequired,
fontFamily: PropTypes.string.isRequired,
textGap: PropTypes.number.isRequired,
color: PropTypes.string,
};