├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── index.js
├── package.json
└── screencasts
└── demo.gif
/.gitignore:
--------------------------------------------------------------------------------
1 | *.[aod]
2 | *.DS_Store
3 | .DS_Store
4 | *Thumbs.db
5 | *.iml
6 | .gradle
7 | .idea
8 | node_modules
9 | npm-debug.log
10 | /android/build
11 | /ios/**/*xcuserdata*
12 | /ios/**/*xcshareddata*
13 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | *.DS_Store
2 | .DS_Store
3 | *Thumbs.db
4 | .gradle
5 | .idea
6 | *.iml
7 | screencasts
8 | npm-debug.log
9 | node_modules
10 | /android/build
11 | /ios/**/*xcuserdata*
12 | /ios/**/*xcshareddata*
13 |
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | (The MIT License)
2 |
3 | Copyright (c) 2015-2016 YunJiang.Fang <42550564@qq.com>
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | 'Software'), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Native Marquee (remobile)
2 | A react-native marquee list write in js
3 |
4 | ## Installation
5 | ```sh
6 | npm install @remobile/react-native-marquee --save
7 | ```
8 |
9 | ## Usage
10 |
11 | ### Example
12 | ```js
13 | 'use strict';
14 |
15 | var React = require('react');
16 | var ReactNative = require('react-native');
17 | var {
18 | StyleSheet,
19 | View,
20 | Text,
21 | } = ReactNative;
22 |
23 | var Button = require('@remobile/react-native-simple-button');
24 | var Marquee = require('@remobile/react-native-marquee');
25 |
26 | module.exports = React.createClass({
27 | getInitialState() {
28 | return {
29 | text: '暗示健康等会拉时间段ksajdfkasdjkfasjdkfasldfjasdlf暗示健康等会拉',
30 | fontSize: 18,
31 | width: 200,
32 | lineHeight: 50,
33 | color: 'red',
34 | };
35 | },
36 | changeContent() {
37 | if (!this.contentFlag) {
38 | this.contentFlag = true;
39 | this.setState({
40 | text: '1231238123981273981273981273912873912873129837129837',
41 | });
42 | } else {
43 | this.contentFlag = false;
44 | this.setState({
45 | text: '暗示健康等会拉时间段ksajdfkasdjkfasjdkfasldfjasdlf暗示健康等会拉',
46 | });
47 | }
48 | },
49 | changeStyle() {
50 | if (!this.styleFlag) {
51 | this.styleFlag = true;
52 | this.setState({
53 | fontSize: 50,
54 | width: 100,
55 | lineHeight: 150,
56 | color: 'blue',
57 | });
58 | } else {
59 | this.styleFlag = false;
60 | this.setState({
61 | fontSize: 18,
62 | width: 200,
63 | lineHeight: 50,
64 | color: 'red',
65 | });
66 | }
67 | },
68 | render() {
69 | const {text, fontSize, color, width, lineHeight} = this.state;
70 | return (
71 |
72 |
75 |
76 |
77 |
78 | );
79 | }
80 | });
81 |
82 |
83 | var styles = StyleSheet.create({
84 | container: {
85 | flex: 1,
86 | paddingTop: 100,
87 | },
88 | label: {
89 | color: 'red',
90 | fontSize: 18,
91 | fontWeight: '800',
92 | letterSpacing: 10,
93 | fontStyle: 'italic',
94 | lineHeight: 50,
95 | backgroundColor: 'green',
96 | paddingHorizontal: 20,
97 | width: 200,
98 | left: 100,
99 | overflow: 'hidden',
100 | },
101 | });
102 | ```
103 |
104 | ## Screencasts
105 |
106 | 
107 |
108 | #### Props
109 | - `children: React.PropTypes.string.isRequired` show text
110 | - `speed: React.PropTypes.number` letter move speed, unit is ms, default is 10
111 | - `spaceRatio: React.PropTypes.number` the space ratio of container width
112 | - `style: PropTypes.style` view style and text style
113 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const React = require('react');
4 | const ReactNative = require('react-native');
5 | const {
6 | StyleSheet,
7 | View,
8 | Text,
9 | Animated,
10 | Easing,
11 | } = ReactNative;
12 | const _ = require('lodash');
13 |
14 | function until (test, iterator, callback) {
15 | if (!test()) {
16 | iterator((err) => {
17 | if (err) {
18 | return callback(err);
19 | }
20 | until(test, iterator, callback);
21 | });
22 | } else {
23 | callback();
24 | }
25 | }
26 |
27 | module.exports = React.createClass({
28 | propTypes: {
29 | children: React.PropTypes.string.isRequired,
30 | speed: React.PropTypes.number,
31 | spaceRatio: React.PropTypes.number,
32 | },
33 | getDefaultProps () {
34 | return {
35 | speed: 30,
36 | spaceRatio: 0.5,
37 | };
38 | },
39 | getInitialState () {
40 | this.alpha = {};
41 | return {
42 | left1: new Animated.Value(0),
43 | left2: new Animated.Value(0),
44 | list: this.props.children.split(''),
45 | };
46 | },
47 | componentWillReceiveProps (nextProps) {
48 | if (this.props.children != nextProps.children) {
49 | this.animateEnable = false;
50 | this.width = 0;
51 | this.state.left1.stopAnimation(() => {
52 | this.state.left2.stopAnimation(() => {
53 | Animated.timing(this.state.left1, {
54 | toValue: 0,
55 | duration: 0,
56 | }).start(() => {
57 | Animated.timing(this.state.left2, {
58 | toValue: this.width,
59 | duration: 0,
60 | }).start(() => {
61 | this.setState({ list: nextProps.children.split('') });
62 | });
63 | });
64 | });
65 | });
66 | }
67 | },
68 | onLayout (i, e) {
69 | this.alpha[i] = e.nativeEvent.layout.width;
70 | if (_.size(this.alpha) === this.state.list.length) {
71 | this.twidth = _.sum(_.values(this.alpha));
72 | this.alpha = {};
73 | if (!this.animateEnable) {
74 | this.animateEnable = true;
75 | until(
76 | () => this.width > 0,
77 | (cb) => setTimeout(cb, 100),
78 | () => this.startMoveFirstLabelHead()
79 | );
80 | }
81 | }
82 | },
83 | onLayoutContainer (e) {
84 | if (!this.width) {
85 | this.width = e.nativeEvent.layout.width;
86 | this.spaceWidth = this.props.spaceRatio * this.width;
87 | this.setState({ left1: new Animated.Value(0) });
88 | this.setState({ left2: new Animated.Value(this.width) });
89 | }
90 | },
91 | startMoveFirstLabelHead () {
92 | const { width, twidth, props } = this;
93 | const { speed } = props;
94 | Animated.timing(this.state.left1, {
95 | toValue: -twidth + this.spaceWidth,
96 | duration: (twidth - this.spaceWidth) * speed,
97 | easing: Easing.linear,
98 | delay: 500,
99 | }).start(() => {
100 | this.animateEnable && Animated.parallel(
101 | this.moveFirstLabelTail(),
102 | this.moveSecondLabelHead(),
103 | );
104 | });
105 | },
106 | moveFirstLabelHead () {
107 | const { width, twidth, props } = this;
108 | const { speed } = props;
109 | Animated.timing(this.state.left1, {
110 | toValue: -twidth + this.spaceWidth,
111 | duration: (twidth + this.spaceWidth) * speed,
112 | easing: Easing.linear,
113 | }).start(() => {
114 | this.animateEnable && Animated.parallel(
115 | this.moveFirstLabelTail(),
116 | this.moveSecondLabelHead(),
117 | );
118 | });
119 | },
120 | moveFirstLabelTail () {
121 | const { width, twidth, props } = this;
122 | const { speed } = props;
123 | Animated.timing(this.state.left1, {
124 | toValue: -twidth,
125 | duration: this.spaceWidth * speed,
126 | easing: Easing.linear,
127 | }).start(() => {
128 | this.animateEnable && this.setState({ left1: new Animated.Value(width) });
129 | });
130 | },
131 | moveSecondLabelHead () {
132 | const { width, twidth, props } = this;
133 | const { speed } = props;
134 | Animated.timing(this.state.left2, {
135 | toValue: -twidth + this.spaceWidth,
136 | duration: (twidth + this.spaceWidth) * speed,
137 | easing: Easing.linear,
138 | }).start(() => {
139 | this.animateEnable && Animated.parallel(
140 | this.moveFirstLabelHead(),
141 | this.moveSecondLabelTail(),
142 | );
143 | });
144 | },
145 | moveSecondLabelTail () {
146 | const { width, twidth, props } = this;
147 | const { speed } = props;
148 | Animated.timing(this.state.left2, {
149 | toValue: -twidth,
150 | duration: this.spaceWidth * speed,
151 | easing: Easing.linear,
152 | }).start(() => {
153 | this.animateEnable && this.setState({ left2: new Animated.Value(twidth) });
154 | });
155 | },
156 | render () {
157 | const { left1, left2, list } = this.state;
158 | const s = StyleSheet.flatten(this.props.style);
159 | const textStyleKeys = ['color', 'fontSize', 'fontWeight', 'letterSpacing', 'fontStyle', 'lineHeight', 'fontFamily', 'textDecorationLine'];
160 | const textStyle = _.pick(s, textStyleKeys);
161 | const containerStyle = _.omit(s, textStyleKeys);
162 | return (
163 |
164 |
165 | {list.map((o, i) => ({o}))}
166 |
167 |
168 | {list.map((o, i) => ({o}))}
169 |
170 |
171 | );
172 | },
173 | });
174 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@remobile/react-native-marquee",
3 | "version": "1.0.1",
4 | "description": "A react-native card list write in js",
5 | "main": "index.js",
6 | "author": {
7 | "name": "YunJiang.Fang",
8 | "email": "42550564@qq.com"
9 | },
10 | "license": "MIT",
11 | "keywords": [
12 | "react-native",
13 | "react-component",
14 | "ios",
15 | "android",
16 | "marquee",
17 | "list",
18 | "remobile",
19 | "mobile"
20 | ],
21 | "homepage": "https://github.com/remobile/react-native-marquee",
22 | "bugs": {
23 | "url": "https://github.com/remobile/react-native-marquee/issues"
24 | },
25 | "repository": {
26 | "type": "git",
27 | "url": "git://github.com/remobile/react-native-marquee.git"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/screencasts/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/remobile/react-native-marquee/41941f86784391e7a7c92f441c3ac322d3faf7a3/screencasts/demo.gif
--------------------------------------------------------------------------------