├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.yml
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── ava-jsdom-setup.js
├── example
└── index.html
├── package.json
└── src
├── AnimatedSpriteSheet.js
├── AnimatedSpriteSheet.test.js
├── Sprite.js
├── Sprite.test.js
├── SpriteSheet.js
├── SpriteSheet.test.js
└── index.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015-loose", "react"],
3 | "plugins": ["transform-class-properties", "transform-object-rest-spread"]
4 | }
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # top-most EditorConfig file
2 | root = true
3 |
4 | # Unix-style newlines with a newline ending every file
5 | [*]
6 | end_of_line = lf
7 | insert_final_newline = true
8 |
9 | [*.js]
10 | indent_style = space
11 | indent_size = 2
12 |
13 | [*.json]
14 | indent_style = space
15 | indent_size = 2
16 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | coverage
2 | dist
3 |
--------------------------------------------------------------------------------
/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | extends: airbnb
2 | parser: babel-eslint
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .nyc_output
2 | coverage
3 | dist
4 |
5 | node_modules
6 | npm-debug.log
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 5
4 | - 6
5 |
6 | script:
7 | - npm run lint
8 | - npm test
9 |
10 | after_success:
11 | - npm run coverage | codecov
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Johannes Stein
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-spritesheet
2 | Spritesheets for React
3 |
4 | ## Installation
5 |
6 | ```
7 | npm install react-spritesheet
8 | ```
9 |
10 | ## Usage
11 | This module exports three components:
12 |
13 | ### Sprite
14 | This component is for displaying a part of a bigger image - a rectangular cutout as you will.
15 |
16 | ```jsx
17 |
18 | ```
19 |
20 | #### Props
21 |
22 | ##### filename
23 | ###### Type: `String`
24 |
25 | The filename of the image to be displayed
26 |
27 | ##### x
28 | ###### Type: `Number`
29 |
30 | The x offset of the image
31 |
32 | ##### y
33 | ###### Type: `Number`
34 |
35 | The y offset of the image
36 |
37 | ##### width
38 | ###### Type: `Number`
39 |
40 | The width of the image that will be displayed
41 |
42 | ##### height
43 | ###### Type: `Number`
44 |
45 | The height of the image that will be displayed
46 |
47 | ### SpriteSheet
48 | Providing an image and a spritesheet object, this will display an image from the pre-defined object.
49 |
50 | ```jsx
51 | const spriteSheet = {
52 | image1: {
53 | x: 0,
54 | y: 0,
55 | width: 100,
56 | height: 100,
57 | },
58 | image2: {
59 | x: 100,
60 | y: 0,
61 | width: 100,
62 | height: 100,
63 | },
64 | };
65 |
66 |
67 | ```
68 |
69 | For generating a spritesheet object, take a look at [TexturePacker](https://www.codeandweb.com/texturepacker) or [spritesmith](https://github.com/Ensighten/spritesmith).
70 |
71 | #### Props
72 |
73 | ##### filename
74 | ###### Type: `String`
75 |
76 | The filename of the image to be displayed
77 |
78 | ##### data
79 | ###### Type: `Object`
80 |
81 | The spritesheet object, to be in the form of:
82 | ```json
83 | {
84 | "": {
85 | "x": 0,
86 | "y": 0,
87 | "width": 0,
88 | "height": 0
89 | }
90 | }
91 | ```
92 |
93 | ##### sprite
94 | ###### Type: `String`
95 |
96 | The name of the sprite that should be displayed
97 |
98 | ### AnimatedSpriteSheet
99 | Similar to `Sprite`, but displays one sprite after another at a particular speed.
100 |
101 | ```jsx
102 |
111 | ```
112 |
113 | #### Props
114 |
115 | ##### filename
116 | ###### Type: `String`
117 |
118 | The filename of the image to be displayed
119 |
120 | ##### initialFrame
121 | ###### Type: `Number`
122 | ###### Default: `0`
123 |
124 | The initial frame of the image
125 |
126 | ##### frame
127 | ###### Type: `Object`
128 |
129 | The dimensions of a single frame
130 |
131 | ##### bounds
132 | ###### Type: `Object`
133 |
134 | The bounds of the image that will be used for displaying one frame after another.
135 |
136 | ##### isPlaying
137 | ###### Type: `Boolean`
138 | ###### Default: `true`
139 |
140 | Determines if the spritesheet is actually playing.
141 |
142 | ##### loop
143 | ###### Type: `Boolean`
144 | ###### Default: `true`
145 |
146 | Loops through the animation
147 |
148 | ##### speed
149 | ###### Type: `Number`
150 | ###### Default: `300`
151 |
152 | The speed (in milliseconds) of displaying one frame after another
153 |
154 | ## Example
155 |
156 | See `example/index.html`.
157 |
158 | ## License
159 |
160 | MIT
161 |
--------------------------------------------------------------------------------
/ava-jsdom-setup.js:
--------------------------------------------------------------------------------
1 | require('babel-register');
2 | require('babel-polyfill');
3 |
4 | global.document = require('jsdom').jsdom('');
5 | global.window = document.defaultView;
6 | global.navigator = window.navigator;
7 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | SpriteSheet example
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-spritesheet",
3 | "version": "1.0.1",
4 | "description": " component for React",
5 | "main": "dist/react-spritesheet.js",
6 | "jsnext:main": "dist/react-spritesheet.es2015.js",
7 | "scripts": {
8 | "prebuild": "rimraf dist",
9 | "build": "rollup-babel-lib-bundler src/index.js",
10 | "prepublish": "in-publish && npm run build || not-in-publish",
11 | "lint": "eslint .",
12 | "test": "ava --verbose 'src/**/*.test.js'",
13 | "precommit": "npm run lint",
14 | "coverage": "nyc --reporter lcov --reporter html ava 'src/**/*.test.js'"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/frostney/react-spritesheet.git"
19 | },
20 | "keywords": [
21 | "react",
22 | "animated",
23 | "animation",
24 | "component",
25 | "sprite",
26 | "sprites",
27 | "spritesheet"
28 | ],
29 | "files": [
30 | "dist",
31 | "src"
32 | ],
33 | "ava": {
34 | "babel": "inherit",
35 | "require": "./ava-jsdom-setup"
36 | },
37 | "rollupBabelLibBundler": {
38 | "moduleName": "SpriteSheet",
39 | "babel": {
40 | "presets": [
41 | "es2015-loose-rollup",
42 | "react"
43 | ],
44 | "plugins": [
45 | "transform-class-properties",
46 | "transform-export-extensions",
47 | "transform-object-rest-spread"
48 | ]
49 | }
50 | },
51 | "author": "Johannes Stein",
52 | "license": "MIT",
53 | "bugs": {
54 | "url": "https://github.com/frostney/react-spritesheet/issues"
55 | },
56 | "homepage": "https://github.com/frostney/react-spritesheet#readme",
57 | "dependencies": {
58 | "react": "^15.0.1",
59 | "shallowequal": "^0.2.2"
60 | },
61 | "bundleDependencies": [
62 | "shallowequal"
63 | ],
64 | "devDependencies": {
65 | "ava": "^0.14.0",
66 | "babel-core": "^6.7.7",
67 | "babel-eslint": "^6.0.4",
68 | "babel-plugin-transform-class-properties": "^6.5.2",
69 | "babel-plugin-transform-export-extensions": "^6.5.0",
70 | "babel-plugin-transform-object-rest-spread": "^6.6.5",
71 | "babel-polyfill": "^6.7.4",
72 | "babel-preset-es2015-loose": "^7.0.0",
73 | "babel-preset-es2015-loose-rollup": "^7.0.0",
74 | "babel-preset-react": "^6.5.0",
75 | "babel-register": "^6.7.2",
76 | "buffer": "^4.6.0",
77 | "chai": "^3.4.1",
78 | "codecov": "^1.0.1",
79 | "enzyme": "^2.0.0",
80 | "eslint": "^2.8.0",
81 | "eslint-config-airbnb": "^8.0.0",
82 | "eslint-plugin-import": "^1.6.0",
83 | "eslint-plugin-jsx-a11y": "^1.0.3",
84 | "eslint-plugin-react": "^5.0.1",
85 | "husky": "^0.11.1",
86 | "in-publish": "^2.0.0",
87 | "nyc": "^6.4.0",
88 | "react-addons-test-utils": "^15.0.1",
89 | "react-dom": "^15.0.1",
90 | "rimraf": "^2.5.2",
91 | "rollup-babel-lib-bundler": "^3.0.1"
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/AnimatedSpriteSheet.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes, Component } from 'react';
2 | import shallowEqual from 'shallowequal';
3 |
4 | import Sprite from './Sprite';
5 |
6 | class AnimatedSpriteSheet extends Component {
7 | static propTypes = {
8 | filename: PropTypes.string,
9 | initialFrame: PropTypes.number,
10 | frame: PropTypes.shape({
11 | height: PropTypes.number,
12 | width: PropTypes.number,
13 | }),
14 | bounds: PropTypes.shape({
15 | x: PropTypes.number,
16 | y: PropTypes.number,
17 | width: PropTypes.number,
18 | height: PropTypes.number,
19 | }),
20 | isPlaying: PropTypes.bool,
21 | loop: PropTypes.bool,
22 | speed: PropTypes.number,
23 | };
24 |
25 | static defaultProps = {
26 | initialFrame: 0,
27 | frame: {
28 | width: 0,
29 | height: 0,
30 | },
31 | bounds: {
32 | x: 0,
33 | y: 0,
34 | width: 0,
35 | height: 0,
36 | },
37 | isPlaying: true,
38 | loop: true,
39 | speed: 300,
40 | };
41 |
42 | constructor(props) {
43 | super(props);
44 |
45 | const maxFramesWidth = ((this.props.bounds.width - this.props.bounds.x) /
46 | this.props.frame.width);
47 | const maxFramesHeight = ((this.props.bounds.height - this.props.bounds.y) /
48 | this.props.frame.height);
49 |
50 | const maxFrames = maxFramesWidth * maxFramesHeight;
51 |
52 | this.state = {
53 | frame: props.initialFrame,
54 | maxFrames,
55 | };
56 | }
57 |
58 | componentDidMount() {
59 | this.timerId = setInterval(() => {
60 | if (this.props.loop && this.state.frame === this.state.maxFrames) {
61 | return this.setState({
62 | frame: 0,
63 | });
64 | }
65 |
66 | return this.setState({
67 | frame: this.state.frame + 1,
68 | });
69 | }, this.props.speed);
70 | }
71 |
72 | shouldComponentUpdate(nextProps, nextState) {
73 | if (!shallowEqual(this.props.frame, nextProps.frame)) {
74 | return true;
75 | }
76 |
77 | if (!shallowEqual(this.props.bounds, nextProps.bounds)) {
78 | return true;
79 | }
80 |
81 | if (!shallowEqual({
82 | filename: this.props.filename,
83 | initialFrame: this.props.initialFrame,
84 | isPlaying: this.props.isPlaying,
85 | loop: this.props.loop,
86 | speed: this.props.speed,
87 | }, {
88 | filename: nextProps.filename,
89 | initialFrame: nextProps.initialFrame,
90 | isPlaying: nextProps.isPlaying,
91 | loop: nextProps.loop,
92 | speed: nextProps.speed,
93 | })) {
94 | return true;
95 | }
96 |
97 | if (!shallowEqual(this.state, nextState)) {
98 | return true;
99 | }
100 |
101 | return false;
102 | }
103 |
104 | componentWillUnmount() {
105 | clearInterval(this.timerId);
106 | }
107 |
108 | render() {
109 | const spriteData = {
110 | filename: this.props.filename,
111 | width: this.props.frame.width,
112 | height: this.props.frame.height,
113 | x: this.props.frame.width * this.state.frame,
114 | };
115 |
116 | return ;
117 | }
118 | }
119 |
120 | export default AnimatedSpriteSheet;
121 |
--------------------------------------------------------------------------------
/src/AnimatedSpriteSheet.test.js:
--------------------------------------------------------------------------------
1 | import test from 'ava';
2 |
3 | import React from 'react';
4 | import AnimatedSpriteSheet from './AnimatedSpriteSheet';
5 |
6 | import { render } from 'enzyme';
7 |
8 | const FILENAME = 'https://cdn.codeandweb.com/blog/2014/11/05/animate-sprites-in-css-with-texturepacker/capguy-walk.png';
9 |
10 | test('is instantiable', t => {
11 | const wrapper = render();
12 |
13 | t.truthy(wrapper);
14 | });
15 |
--------------------------------------------------------------------------------
/src/Sprite.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 |
3 | const Sprite = ({ filename, x, y, width, height }) => {
4 | if (!filename) {
5 | return null;
6 | }
7 |
8 | const style = {
9 | backgroundImage: `url(${filename})`,
10 | backgroundPosition: `${x * (-1)}px ${y * (-1)}px`,
11 | backgroundRepeat: 'no-repeat',
12 | width,
13 | height,
14 | };
15 |
16 | return ;
17 | };
18 |
19 | Sprite.defaultProps = {
20 | x: 0,
21 | y: 0,
22 | width: 0,
23 | height: 0,
24 | };
25 |
26 | Sprite.propTypes = {
27 | filename: PropTypes.string,
28 | x: PropTypes.number,
29 | y: PropTypes.number,
30 | width: PropTypes.number,
31 | height: PropTypes.number,
32 | };
33 |
34 | export default Sprite;
35 |
--------------------------------------------------------------------------------
/src/Sprite.test.js:
--------------------------------------------------------------------------------
1 | import test from 'ava';
2 |
3 | import React from 'react';
4 | import Sprite from './Sprite';
5 |
6 | import { render } from 'enzyme';
7 |
8 | const FILENAME = 'https://cdn.codeandweb.com/blog/2014/11/05/animate-sprites-in-css-with-texturepacker/capguy-walk.png';
9 |
10 | test('is instantiable', t => {
11 | const wrapper = render();
12 |
13 | t.truthy(wrapper);
14 | });
15 |
--------------------------------------------------------------------------------
/src/SpriteSheet.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 |
3 | import Sprite from './Sprite';
4 |
5 | const SpriteSheet = ({ filename, data, sprite }) => {
6 | if (!filename || !data || !sprite) {
7 | return null;
8 | }
9 |
10 | const currentSprite = data[sprite];
11 |
12 | const spriteData = { ...currentSprite, filename };
13 |
14 | return ;
15 | };
16 |
17 | SpriteSheet.propTypes = {
18 | filename: PropTypes.string,
19 | sprite: PropTypes.string,
20 | data: PropTypes.object,
21 | };
22 |
23 | export default SpriteSheet;
24 |
--------------------------------------------------------------------------------
/src/SpriteSheet.test.js:
--------------------------------------------------------------------------------
1 | import test from 'ava';
2 |
3 | import React from 'react';
4 | import SpriteSheet from './SpriteSheet';
5 |
6 | import { render } from 'enzyme';
7 |
8 | const FILENAME = 'https://cdn.codeandweb.com/blog/2014/11/05/animate-sprites-in-css-with-texturepacker/capguy-walk.png';
9 |
10 | test('is instantiable', t => {
11 | const wrapper = render();
16 |
17 | t.truthy(wrapper);
18 | });
19 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export Sprite from './Sprite';
2 | export SpriteSheet from './SpriteSheet';
3 | export AnimatedSpriteSheet from './AnimatedSpriteSheet';
4 |
--------------------------------------------------------------------------------