├── .gitignore ├── LICENSE ├── README.md ├── android ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── dk │ └── madslee │ └── imageSequence │ ├── RCTImageSequenceManager.java │ ├── RCTImageSequencePackage.java │ ├── RCTImageSequenceView.java │ └── RCTResourceDrawableIdHelper.java ├── index.js ├── ios ├── RCTImageSequence.xcodeproj │ └── project.pbxproj └── RCTImageSequence │ ├── RCTImageSequenceManager.h │ ├── RCTImageSequenceManager.m │ ├── RCTImageSequenceView.h │ └── RCTImageSequenceView.m ├── package.json ├── react-native-image-sequence.podspec └── typings └── index.d.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IJ 26 | # 27 | android.iml 28 | .idea 29 | .gradle 30 | local.properties 31 | 32 | # node.js 33 | # 34 | node_modules/* 35 | npm-debug.log 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Mads Lee Jensen 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-native-image-sequence 2 | native modules for handling image sequence animations. (created because i had performance issues with a javascript only solution like: https://github.com/remobile/react-native-image-animation) 3 | 4 | its a simple wrapper around **iOS** `UIImageView.animationImages` and **Android** `AnimationDrawable` 5 | 6 | ## Installation 7 | 8 | 1. `npm i --save react-native-image-sequence` 9 | 2. `react-native link react-native-image-sequence` 10 | 11 | 12 | ## Examples 13 | 14 | ```javascript 15 | import ImageSequence from 'react-native-image-sequence'; 16 | 17 | const images = [ 18 | require('1.jpg'), 19 | require('2.jpg'), 20 | require('3.jpg'), 21 | require('4.jpg'), 22 | require('5.jpg'), 23 | ]; 24 | 25 | const centerIndex = Math.round(images.length / 2); 26 | 27 | 31 | ``` 32 | 33 | ### Change animation speed 34 | You can change the speed of the animation by setting the `framesPerSecond` property. 35 | 36 | ```javascript 37 | 41 | ``` 42 | 43 | ### Looping 44 | You can change if animation loops indefinitely by setting the `loop` property. 45 | 46 | ```javascript 47 | 52 | ``` 53 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | 6 | dependencies { 7 | classpath 'com.android.tools.build:gradle:1.3.1' 8 | } 9 | } 10 | 11 | apply plugin: 'com.android.library' 12 | 13 | android { 14 | compileSdkVersion 23 15 | buildToolsVersion "23.0.1" 16 | 17 | defaultConfig { 18 | minSdkVersion 16 19 | targetSdkVersion 22 20 | versionCode 1 21 | versionName "1.0" 22 | } 23 | lintOptions { 24 | abortOnError false 25 | } 26 | } 27 | 28 | dependencies { 29 | compile 'com.facebook.react:react-native:+' 30 | } -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madsleejensen/react-native-image-sequence/543d3b4a5fe517d59e14fb2026a417884eceea51/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 7 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/java/dk/madslee/imageSequence/RCTImageSequenceManager.java: -------------------------------------------------------------------------------- 1 | package dk.madslee.imageSequence; 2 | 3 | import com.facebook.react.bridge.ReadableArray; 4 | import com.facebook.react.bridge.ReadableMap; 5 | import com.facebook.react.uimanager.SimpleViewManager; 6 | import com.facebook.react.uimanager.ThemedReactContext; 7 | import com.facebook.react.uimanager.annotations.ReactProp; 8 | 9 | import java.util.ArrayList; 10 | 11 | 12 | public class RCTImageSequenceManager extends SimpleViewManager { 13 | @Override 14 | public String getName() { 15 | return "RCTImageSequence"; 16 | } 17 | 18 | @Override 19 | protected RCTImageSequenceView createViewInstance(ThemedReactContext reactContext) { 20 | return new RCTImageSequenceView(reactContext); 21 | } 22 | 23 | /** 24 | * sets the speed of the animation. 25 | * 26 | * @param view 27 | * @param framesPerSecond 28 | */ 29 | @ReactProp(name = "framesPerSecond") 30 | public void setFramesPerSecond(final RCTImageSequenceView view, Integer framesPerSecond) { 31 | view.setFramesPerSecond(framesPerSecond); 32 | } 33 | 34 | /** 35 | * @param view 36 | * @param images an array of ReadableMap's {uri: "http://...."} return value of the resolveAssetSource(....) 37 | */ 38 | @ReactProp(name = "images") 39 | public void setImages(final RCTImageSequenceView view, ReadableArray images) { 40 | ArrayList uris = new ArrayList<>(); 41 | for (int index = 0; index < images.size(); index++) { 42 | ReadableMap map = images.getMap(index); 43 | uris.add(map.getString("uri")); 44 | } 45 | 46 | view.setImages(uris); 47 | } 48 | 49 | /** 50 | * sets if animations is looped indefinitely. 51 | * 52 | * @param view 53 | * @param loop 54 | */ 55 | @ReactProp(name = "loop") 56 | public void setLoop(final RCTImageSequenceView view, Boolean loop) { 57 | view.setLoop(loop); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /android/src/main/java/dk/madslee/imageSequence/RCTImageSequencePackage.java: -------------------------------------------------------------------------------- 1 | package dk.madslee.imageSequence; 2 | 3 | import com.facebook.react.ReactPackage; 4 | import com.facebook.react.bridge.JavaScriptModule; 5 | import com.facebook.react.bridge.NativeModule; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.react.uimanager.ViewManager; 8 | 9 | import java.util.Arrays; 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | public class RCTImageSequencePackage implements ReactPackage { 14 | 15 | @Override 16 | public List createNativeModules(ReactApplicationContext reactContext) { 17 | return Collections.emptyList(); 18 | } 19 | 20 | // Deprecated RN 0.47 21 | public List> createJSModules() { 22 | return Collections.emptyList(); 23 | } 24 | 25 | @Override 26 | public List createViewManagers(ReactApplicationContext reactContext) { 27 | return Arrays.asList(new RCTImageSequenceManager()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /android/src/main/java/dk/madslee/imageSequence/RCTImageSequenceView.java: -------------------------------------------------------------------------------- 1 | package dk.madslee.imageSequence; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.graphics.drawable.AnimationDrawable; 7 | import android.graphics.drawable.BitmapDrawable; 8 | import android.util.Log; 9 | import android.os.AsyncTask; 10 | import android.widget.ImageView; 11 | 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.net.URL; 15 | import java.util.ArrayList; 16 | import java.util.HashMap; 17 | import java.util.concurrent.RejectedExecutionException; 18 | 19 | 20 | public class RCTImageSequenceView extends ImageView { 21 | private Integer framesPerSecond = 24; 22 | private Boolean loop = true; 23 | private ArrayList activeTasks; 24 | private HashMap bitmaps; 25 | private RCTResourceDrawableIdHelper resourceDrawableIdHelper; 26 | 27 | public RCTImageSequenceView(Context context) { 28 | super(context); 29 | 30 | resourceDrawableIdHelper = new RCTResourceDrawableIdHelper(); 31 | } 32 | 33 | private class DownloadImageTask extends AsyncTask { 34 | private final Integer index; 35 | private final String uri; 36 | private final Context context; 37 | 38 | public DownloadImageTask(Integer index, String uri, Context context) { 39 | this.index = index; 40 | this.uri = uri; 41 | this.context = context; 42 | } 43 | 44 | @Override 45 | protected Bitmap doInBackground(String... params) { 46 | if (this.uri.startsWith("http")) { 47 | return this.loadBitmapByExternalURL(this.uri); 48 | } 49 | 50 | return this.loadBitmapByLocalResource(this.uri); 51 | } 52 | 53 | 54 | private Bitmap loadBitmapByLocalResource(String uri) { 55 | return BitmapFactory.decodeResource(this.context.getResources(), resourceDrawableIdHelper.getResourceDrawableId(this.context, uri)); 56 | } 57 | 58 | private Bitmap loadBitmapByExternalURL(String uri) { 59 | Bitmap bitmap = null; 60 | 61 | try { 62 | InputStream in = new URL(uri).openStream(); 63 | bitmap = BitmapFactory.decodeStream(in); 64 | } catch (IOException e) { 65 | e.printStackTrace(); 66 | } 67 | 68 | return bitmap; 69 | } 70 | 71 | @Override 72 | protected void onPostExecute(Bitmap bitmap) { 73 | if (!isCancelled()) { 74 | onTaskCompleted(this, index, bitmap); 75 | } 76 | } 77 | } 78 | 79 | private void onTaskCompleted(DownloadImageTask downloadImageTask, Integer index, Bitmap bitmap) { 80 | if (index == 0) { 81 | // first image should be displayed as soon as possible. 82 | this.setImageBitmap(bitmap); 83 | } 84 | 85 | bitmaps.put(index, bitmap); 86 | activeTasks.remove(downloadImageTask); 87 | 88 | if (activeTasks.isEmpty()) { 89 | setupAnimationDrawable(); 90 | } 91 | } 92 | 93 | public void setImages(ArrayList uris) { 94 | if (isLoading()) { 95 | // cancel ongoing tasks (if still loading previous images) 96 | for (int index = 0; index < activeTasks.size(); index++) { 97 | activeTasks.get(index).cancel(true); 98 | } 99 | } 100 | 101 | activeTasks = new ArrayList<>(uris.size()); 102 | bitmaps = new HashMap<>(uris.size()); 103 | 104 | for (int index = 0; index < uris.size(); index++) { 105 | DownloadImageTask task = new DownloadImageTask(index, uris.get(index), getContext()); 106 | activeTasks.add(task); 107 | 108 | try { 109 | task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 110 | } catch (RejectedExecutionException e){ 111 | Log.e("react-native-image-sequence", "DownloadImageTask failed" + e.getMessage()); 112 | break; 113 | } 114 | } 115 | } 116 | 117 | public void setFramesPerSecond(Integer framesPerSecond) { 118 | this.framesPerSecond = framesPerSecond; 119 | 120 | // updating frames per second, results in building a new AnimationDrawable (because we cant alter frame duration) 121 | if (isLoaded()) { 122 | setupAnimationDrawable(); 123 | } 124 | } 125 | 126 | public void setLoop(Boolean loop) { 127 | this.loop = loop; 128 | 129 | // updating looping, results in building a new AnimationDrawable 130 | if (isLoaded()) { 131 | setupAnimationDrawable(); 132 | } 133 | } 134 | 135 | private boolean isLoaded() { 136 | return !isLoading() && bitmaps != null && !bitmaps.isEmpty(); 137 | } 138 | 139 | private boolean isLoading() { 140 | return activeTasks != null && !activeTasks.isEmpty(); 141 | } 142 | 143 | private void setupAnimationDrawable() { 144 | AnimationDrawable animationDrawable = new AnimationDrawable(); 145 | for (int index = 0; index < bitmaps.size(); index++) { 146 | BitmapDrawable drawable = new BitmapDrawable(this.getResources(), bitmaps.get(index)); 147 | animationDrawable.addFrame(drawable, 1000 / framesPerSecond); 148 | } 149 | 150 | animationDrawable.setOneShot(!this.loop); 151 | 152 | this.setImageDrawable(animationDrawable); 153 | animationDrawable.start(); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /android/src/main/java/dk/madslee/imageSequence/RCTResourceDrawableIdHelper.java: -------------------------------------------------------------------------------- 1 | package dk.madslee.imageSequence; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import android.net.Uri; 6 | import com.facebook.common.util.UriUtil; 7 | 8 | import javax.annotation.Nullable; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | 13 | public class RCTResourceDrawableIdHelper { 14 | 15 | private Map mResourceDrawableIdMap; 16 | 17 | public RCTResourceDrawableIdHelper() { 18 | mResourceDrawableIdMap = new HashMap(); 19 | } 20 | 21 | public int getResourceDrawableId(Context context, @Nullable String name) { 22 | if (name == null || name.isEmpty()) { 23 | return 0; 24 | } 25 | name = name.toLowerCase().replace("-", "_"); 26 | if (mResourceDrawableIdMap.containsKey(name)) { 27 | return mResourceDrawableIdMap.get(name); 28 | } 29 | int id = context.getResources().getIdentifier( 30 | name, 31 | "drawable", 32 | context.getPackageName()); 33 | mResourceDrawableIdMap.put(name, id); 34 | return id; 35 | } 36 | 37 | public @Nullable Drawable getResourceDrawable(Context context, @Nullable String name) { 38 | int resId = getResourceDrawableId(context, name); 39 | return resId > 0 ? context.getResources().getDrawable(resId) : null; 40 | } 41 | 42 | public Uri getResourceDrawableUri(Context context, @Nullable String name) { 43 | int resId = getResourceDrawableId(context, name); 44 | return resId > 0 ? new Uri.Builder() 45 | .scheme(UriUtil.LOCAL_RESOURCE_SCHEME) 46 | .path(String.valueOf(resId)) 47 | .build() : Uri.EMPTY; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { 3 | View, 4 | requireNativeComponent, 5 | ViewPropTypes 6 | } from 'react-native'; 7 | import { bool, string, number, array, shape, arrayOf } from 'prop-types'; 8 | import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'; 9 | 10 | class ImageSequence extends Component { 11 | render() { 12 | let normalized = this.props.images.map(resolveAssetSource); 13 | 14 | // reorder elements if start-index is different from 0 (beginning) 15 | if (this.props.startFrameIndex !== 0) { 16 | normalized = [...normalized.slice(this.props.startFrameIndex), ...normalized.slice(0, this.props.startFrameIndex)]; 17 | } 18 | 19 | return ( 20 | 23 | ); 24 | } 25 | } 26 | 27 | ImageSequence.defaultProps = { 28 | startFrameIndex: 0, 29 | framesPerSecond: 24, 30 | loop: true 31 | }; 32 | 33 | ImageSequence.propTypes = { 34 | startFrameIndex: number, 35 | images: array.isRequired, 36 | framesPerSecond: number, 37 | loop: bool 38 | }; 39 | 40 | const RCTImageSequence = requireNativeComponent('RCTImageSequence', { 41 | propTypes: { 42 | ...ViewPropTypes, 43 | images: arrayOf(shape({ 44 | uri: string.isRequired 45 | })).isRequired, 46 | framesPerSecond: number, 47 | loop: bool 48 | }, 49 | }); 50 | 51 | export default ImageSequence; 52 | -------------------------------------------------------------------------------- /ios/RCTImageSequence.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C75D3D5D1D3671B500B6F44B /* RCTImageSequenceView.m in Sources */ = {isa = PBXBuildFile; fileRef = C75D3D591D3671B500B6F44B /* RCTImageSequenceView.m */; }; 11 | C75D3D5E1D3671B500B6F44B /* RCTImageSequenceManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C75D3D5B1D3671B500B6F44B /* RCTImageSequenceManager.m */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | C75D3D4B1D36714700B6F44B /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = "include/$(PRODUCT_NAME)"; 19 | dstSubfolderSpec = 16; 20 | files = ( 21 | ); 22 | runOnlyForDeploymentPostprocessing = 0; 23 | }; 24 | /* End PBXCopyFilesBuildPhase section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | C75D3D4D1D36714700B6F44B /* libRCTImageSequence.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTImageSequence.a; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | C75D3D591D3671B500B6F44B /* RCTImageSequenceView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageSequenceView.m; sourceTree = ""; }; 29 | C75D3D5A1D3671B500B6F44B /* RCTImageSequenceView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageSequenceView.h; sourceTree = ""; }; 30 | C75D3D5B1D3671B500B6F44B /* RCTImageSequenceManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageSequenceManager.m; sourceTree = ""; }; 31 | C75D3D5C1D3671B500B6F44B /* RCTImageSequenceManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageSequenceManager.h; sourceTree = ""; }; 32 | /* End PBXFileReference section */ 33 | 34 | /* Begin PBXFrameworksBuildPhase section */ 35 | C75D3D4A1D36714700B6F44B /* Frameworks */ = { 36 | isa = PBXFrameworksBuildPhase; 37 | buildActionMask = 2147483647; 38 | files = ( 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | C75D3D441D36714700B6F44B = { 46 | isa = PBXGroup; 47 | children = ( 48 | C75D3D4F1D36714700B6F44B /* RCTImageSequence */, 49 | C75D3D4E1D36714700B6F44B /* Products */, 50 | ); 51 | sourceTree = ""; 52 | }; 53 | C75D3D4E1D36714700B6F44B /* Products */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | C75D3D4D1D36714700B6F44B /* libRCTImageSequence.a */, 57 | ); 58 | name = Products; 59 | sourceTree = ""; 60 | }; 61 | C75D3D4F1D36714700B6F44B /* RCTImageSequence */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | C75D3D591D3671B500B6F44B /* RCTImageSequenceView.m */, 65 | C75D3D5A1D3671B500B6F44B /* RCTImageSequenceView.h */, 66 | C75D3D5B1D3671B500B6F44B /* RCTImageSequenceManager.m */, 67 | C75D3D5C1D3671B500B6F44B /* RCTImageSequenceManager.h */, 68 | ); 69 | path = RCTImageSequence; 70 | sourceTree = ""; 71 | }; 72 | /* End PBXGroup section */ 73 | 74 | /* Begin PBXNativeTarget section */ 75 | C75D3D4C1D36714700B6F44B /* RCTImageSequence */ = { 76 | isa = PBXNativeTarget; 77 | buildConfigurationList = C75D3D561D36714700B6F44B /* Build configuration list for PBXNativeTarget "RCTImageSequence" */; 78 | buildPhases = ( 79 | C75D3D491D36714700B6F44B /* Sources */, 80 | C75D3D4A1D36714700B6F44B /* Frameworks */, 81 | C75D3D4B1D36714700B6F44B /* CopyFiles */, 82 | ); 83 | buildRules = ( 84 | ); 85 | dependencies = ( 86 | ); 87 | name = RCTImageSequence; 88 | productName = RCTImageSequence; 89 | productReference = C75D3D4D1D36714700B6F44B /* libRCTImageSequence.a */; 90 | productType = "com.apple.product-type.library.static"; 91 | }; 92 | /* End PBXNativeTarget section */ 93 | 94 | /* Begin PBXProject section */ 95 | C75D3D451D36714700B6F44B /* Project object */ = { 96 | isa = PBXProject; 97 | attributes = { 98 | LastUpgradeCheck = 0730; 99 | ORGANIZATIONNAME = "Mads Lee Jensen"; 100 | TargetAttributes = { 101 | C75D3D4C1D36714700B6F44B = { 102 | CreatedOnToolsVersion = 7.3.1; 103 | }; 104 | }; 105 | }; 106 | buildConfigurationList = C75D3D481D36714700B6F44B /* Build configuration list for PBXProject "RCTImageSequence" */; 107 | compatibilityVersion = "Xcode 3.2"; 108 | developmentRegion = English; 109 | hasScannedForEncodings = 0; 110 | knownRegions = ( 111 | en, 112 | ); 113 | mainGroup = C75D3D441D36714700B6F44B; 114 | productRefGroup = C75D3D4E1D36714700B6F44B /* Products */; 115 | projectDirPath = ""; 116 | projectRoot = ""; 117 | targets = ( 118 | C75D3D4C1D36714700B6F44B /* RCTImageSequence */, 119 | ); 120 | }; 121 | /* End PBXProject section */ 122 | 123 | /* Begin PBXSourcesBuildPhase section */ 124 | C75D3D491D36714700B6F44B /* Sources */ = { 125 | isa = PBXSourcesBuildPhase; 126 | buildActionMask = 2147483647; 127 | files = ( 128 | C75D3D5E1D3671B500B6F44B /* RCTImageSequenceManager.m in Sources */, 129 | C75D3D5D1D3671B500B6F44B /* RCTImageSequenceView.m in Sources */, 130 | ); 131 | runOnlyForDeploymentPostprocessing = 0; 132 | }; 133 | /* End PBXSourcesBuildPhase section */ 134 | 135 | /* Begin XCBuildConfiguration section */ 136 | C75D3D541D36714700B6F44B /* Debug */ = { 137 | isa = XCBuildConfiguration; 138 | buildSettings = { 139 | ALWAYS_SEARCH_USER_PATHS = NO; 140 | CLANG_ANALYZER_NONNULL = YES; 141 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 142 | CLANG_CXX_LIBRARY = "libc++"; 143 | CLANG_ENABLE_MODULES = YES; 144 | CLANG_ENABLE_OBJC_ARC = YES; 145 | CLANG_WARN_BOOL_CONVERSION = YES; 146 | CLANG_WARN_CONSTANT_CONVERSION = YES; 147 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 148 | CLANG_WARN_EMPTY_BODY = YES; 149 | CLANG_WARN_ENUM_CONVERSION = YES; 150 | CLANG_WARN_INT_CONVERSION = YES; 151 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 152 | CLANG_WARN_UNREACHABLE_CODE = YES; 153 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 154 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 155 | COPY_PHASE_STRIP = NO; 156 | DEBUG_INFORMATION_FORMAT = dwarf; 157 | ENABLE_STRICT_OBJC_MSGSEND = YES; 158 | ENABLE_TESTABILITY = YES; 159 | GCC_C_LANGUAGE_STANDARD = gnu99; 160 | GCC_DYNAMIC_NO_PIC = NO; 161 | GCC_NO_COMMON_BLOCKS = YES; 162 | GCC_OPTIMIZATION_LEVEL = 0; 163 | GCC_PREPROCESSOR_DEFINITIONS = ( 164 | "DEBUG=1", 165 | "$(inherited)", 166 | ); 167 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 168 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 169 | GCC_WARN_UNDECLARED_SELECTOR = YES; 170 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 171 | GCC_WARN_UNUSED_FUNCTION = YES; 172 | GCC_WARN_UNUSED_VARIABLE = YES; 173 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 174 | MTL_ENABLE_DEBUG_INFO = YES; 175 | ONLY_ACTIVE_ARCH = YES; 176 | SDKROOT = iphoneos; 177 | }; 178 | name = Debug; 179 | }; 180 | C75D3D551D36714700B6F44B /* Release */ = { 181 | isa = XCBuildConfiguration; 182 | buildSettings = { 183 | ALWAYS_SEARCH_USER_PATHS = NO; 184 | CLANG_ANALYZER_NONNULL = YES; 185 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 186 | CLANG_CXX_LIBRARY = "libc++"; 187 | CLANG_ENABLE_MODULES = YES; 188 | CLANG_ENABLE_OBJC_ARC = YES; 189 | CLANG_WARN_BOOL_CONVERSION = YES; 190 | CLANG_WARN_CONSTANT_CONVERSION = YES; 191 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 192 | CLANG_WARN_EMPTY_BODY = YES; 193 | CLANG_WARN_ENUM_CONVERSION = YES; 194 | CLANG_WARN_INT_CONVERSION = YES; 195 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 196 | CLANG_WARN_UNREACHABLE_CODE = YES; 197 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 198 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 199 | COPY_PHASE_STRIP = NO; 200 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 201 | ENABLE_NS_ASSERTIONS = NO; 202 | ENABLE_STRICT_OBJC_MSGSEND = YES; 203 | GCC_C_LANGUAGE_STANDARD = gnu99; 204 | GCC_NO_COMMON_BLOCKS = YES; 205 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 206 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 207 | GCC_WARN_UNDECLARED_SELECTOR = YES; 208 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 209 | GCC_WARN_UNUSED_FUNCTION = YES; 210 | GCC_WARN_UNUSED_VARIABLE = YES; 211 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 212 | MTL_ENABLE_DEBUG_INFO = NO; 213 | SDKROOT = iphoneos; 214 | VALIDATE_PRODUCT = YES; 215 | }; 216 | name = Release; 217 | }; 218 | C75D3D571D36714700B6F44B /* Debug */ = { 219 | isa = XCBuildConfiguration; 220 | buildSettings = { 221 | HEADER_SEARCH_PATHS = "$(SRCROOT)/../../react-native/React/**"; 222 | OTHER_LDFLAGS = "-ObjC"; 223 | PRODUCT_NAME = "$(TARGET_NAME)"; 224 | SKIP_INSTALL = YES; 225 | }; 226 | name = Debug; 227 | }; 228 | C75D3D581D36714700B6F44B /* Release */ = { 229 | isa = XCBuildConfiguration; 230 | buildSettings = { 231 | HEADER_SEARCH_PATHS = "$(SRCROOT)/../../react-native/React/**"; 232 | OTHER_LDFLAGS = "-ObjC"; 233 | PRODUCT_NAME = "$(TARGET_NAME)"; 234 | SKIP_INSTALL = YES; 235 | }; 236 | name = Release; 237 | }; 238 | /* End XCBuildConfiguration section */ 239 | 240 | /* Begin XCConfigurationList section */ 241 | C75D3D481D36714700B6F44B /* Build configuration list for PBXProject "RCTImageSequence" */ = { 242 | isa = XCConfigurationList; 243 | buildConfigurations = ( 244 | C75D3D541D36714700B6F44B /* Debug */, 245 | C75D3D551D36714700B6F44B /* Release */, 246 | ); 247 | defaultConfigurationIsVisible = 0; 248 | defaultConfigurationName = Release; 249 | }; 250 | C75D3D561D36714700B6F44B /* Build configuration list for PBXNativeTarget "RCTImageSequence" */ = { 251 | isa = XCConfigurationList; 252 | buildConfigurations = ( 253 | C75D3D571D36714700B6F44B /* Debug */, 254 | C75D3D581D36714700B6F44B /* Release */, 255 | ); 256 | defaultConfigurationIsVisible = 0; 257 | defaultConfigurationName = Release; 258 | }; 259 | /* End XCConfigurationList section */ 260 | }; 261 | rootObject = C75D3D451D36714700B6F44B /* Project object */; 262 | } 263 | -------------------------------------------------------------------------------- /ios/RCTImageSequence/RCTImageSequenceManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mads Lee Jensen on 07/07/16. 3 | // Copyright (c) 2016 Facebook. All rights reserved. 4 | // 5 | 6 | #import "RCTViewManager.h" 7 | 8 | @interface RCTImageSequenceManager : RCTViewManager 9 | 10 | 11 | @end -------------------------------------------------------------------------------- /ios/RCTImageSequence/RCTImageSequenceManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mads Lee Jensen on 07/07/16. 3 | // Copyright (c) 2016 Facebook. All rights reserved. 4 | // 5 | 6 | #import "RCTImageSequenceManager.h" 7 | #import "RCTImageSequenceView.h" 8 | 9 | @implementation RCTImageSequenceManager { 10 | } 11 | 12 | RCT_EXPORT_MODULE(); 13 | RCT_EXPORT_VIEW_PROPERTY(images, NSArray); 14 | RCT_EXPORT_VIEW_PROPERTY(framesPerSecond, NSUInteger); 15 | RCT_EXPORT_VIEW_PROPERTY(loop, BOOL); 16 | 17 | - (UIView *)view { 18 | return [RCTImageSequenceView new]; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /ios/RCTImageSequence/RCTImageSequenceView.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mads Lee Jensen on 07/07/16. 3 | // Copyright (c) 2016 Facebook. All rights reserved. 4 | // 5 | 6 | #import 7 | 8 | @interface RCTImageSequenceView : UIImageView 9 | 10 | 11 | @end -------------------------------------------------------------------------------- /ios/RCTImageSequence/RCTImageSequenceView.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mads Lee Jensen on 07/07/16. 3 | // Copyright (c) 2016 Facebook. All rights reserved. 4 | // 5 | 6 | #import "RCTImageSequenceView.h" 7 | 8 | @implementation RCTImageSequenceView { 9 | NSUInteger _framesPerSecond; 10 | NSMutableDictionary *_activeTasks; 11 | NSMutableDictionary *_imagesLoaded; 12 | BOOL _loop; 13 | } 14 | 15 | - (void)setImages:(NSArray *)images { 16 | __weak RCTImageSequenceView *weakSelf = self; 17 | 18 | self.animationImages = nil; 19 | 20 | _activeTasks = [NSMutableDictionary new]; 21 | _imagesLoaded = [NSMutableDictionary new]; 22 | 23 | for (NSUInteger index = 0; index < images.count; index++) { 24 | NSDictionary *item = images[index]; 25 | 26 | #ifdef DEBUG 27 | NSString *url = item[@"uri"]; 28 | #else 29 | NSString *url = [NSString stringWithFormat:@"file://%@", item[@"uri"]]; // when not in debug, the paths are "local paths" (because resources are bundled in app) 30 | #endif 31 | 32 | dispatch_async(dispatch_queue_create("dk.mads-lee.ImageSequence.Downloader", NULL), ^{ 33 | UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:url]]]; 34 | dispatch_async(dispatch_get_main_queue(), ^{ 35 | [weakSelf onImageLoadTaskAtIndex:index image:image]; 36 | }); 37 | }); 38 | 39 | _activeTasks[@(index)] = url; 40 | } 41 | } 42 | 43 | - (void)onImageLoadTaskAtIndex:(NSUInteger)index image:(UIImage *)image { 44 | if (index == 0) { 45 | self.image = image; 46 | } 47 | 48 | [_activeTasks removeObjectForKey:@(index)]; 49 | 50 | _imagesLoaded[@(index)] = image; 51 | 52 | if (_activeTasks.allValues.count == 0) { 53 | [self onImagesLoaded]; 54 | } 55 | } 56 | 57 | - (void)onImagesLoaded { 58 | NSMutableArray *images = [NSMutableArray new]; 59 | for (NSUInteger index = 0; index < _imagesLoaded.allValues.count; index++) { 60 | UIImage *image = _imagesLoaded[@(index)]; 61 | [images addObject:image]; 62 | } 63 | 64 | [_imagesLoaded removeAllObjects]; 65 | 66 | self.image = nil; 67 | self.animationDuration = images.count * (1.0f / _framesPerSecond); 68 | self.animationImages = images; 69 | self.animationRepeatCount = _loop ? 0 : 1; 70 | [self startAnimating]; 71 | } 72 | 73 | - (void)setFramesPerSecond:(NSUInteger)framesPerSecond { 74 | _framesPerSecond = framesPerSecond; 75 | 76 | if (self.animationImages.count > 0) { 77 | self.animationDuration = self.animationImages.count * (1.0f / _framesPerSecond); 78 | } 79 | } 80 | 81 | - (void)setLoop:(NSUInteger)loop { 82 | _loop = loop; 83 | 84 | self.animationRepeatCount = _loop ? 0 : 1; 85 | } 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-image-sequence", 3 | "repository": { 4 | "type": "git", 5 | "url": "https://github.com/madsleejensen/react-native-image-sequence.git" 6 | }, 7 | "version": "0.7.0", 8 | "description": "A component for react-native", 9 | "author": { 10 | "name": "Mads Lee Jensen", 11 | "email": "madsleejensen@gmail.com", 12 | "url": "http://mads-lee.dk" 13 | }, 14 | "peerDependencies": { 15 | "react-native": ">=0.47.0", 16 | "prop-types": ">=15.5.0" 17 | }, 18 | "license": "MIT", 19 | "homepage": "https://github.com/madsleejensen/react-native-image-sequence", 20 | "main": "index.js", 21 | "types": "typings/index.d.ts", 22 | "keywords": [ 23 | "react-native", 24 | "native", 25 | "sequence", 26 | "imagesequence", 27 | "frames", 28 | "frame-animation", 29 | "animation", 30 | "animationImages" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /react-native-image-sequence.podspec: -------------------------------------------------------------------------------- 1 | require "json" 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, "package.json"))) 4 | 5 | Pod::Spec.new do |s| 6 | s.name = package["name"] 7 | s.version = package["version"] 8 | s.summary = package["description"] 9 | s.author = "Mads Lee Jensen " 10 | s.homepage = package["homepage"] 11 | s.license = package["license"] 12 | s.platform = :ios, "9.0" 13 | s.source = {:git => "https://github.com/madsleejensen/react-native-image-sequence.git" } 14 | s.source_files = "ios/RCTImageSequence/*.{h,m}" 15 | s.dependency "React" 16 | end 17 | -------------------------------------------------------------------------------- /typings/index.d.ts: -------------------------------------------------------------------------------- 1 | import { Component } from 'react'; 2 | 3 | interface ImageSequenceProps { 4 | /** An array of source images. Each element of the array should be the result of a call to require(imagePath). */ 5 | images: any[]; 6 | /** Which index of the images array should the sequence start at. Default: 0 */ 7 | startFrameIndex?: number; 8 | /** Playback speed of the image sequence. Default: 24 */ 9 | framesPerSecond?: number; 10 | /** Should the sequence loop. Default: true */ 11 | loop?: boolean; 12 | } 13 | 14 | declare class ImageSequence extends Component { 15 | } 16 | 17 | export default ImageSequence; 18 | --------------------------------------------------------------------------------