├── .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 | --------------------------------------------------------------------------------