Saturday, 1 December 2018

React JS animations based on JSON data

I am using React/Redux and am storing animation data in JSON and trying to get it to display on a React page.

I am using setTimeOut (for pauses) and setInterval (for animation movement). However, I seem to be having trouble understanding how to implement the animations correctly and think I'm going about things totally the wrong way.

My JSON code:

"objects": [

    {
        "title": "puppy",
        "image_set": [
            {
                "image": "images/puppy_sitting.png",
                "startx": 520,
                "starty": 28,
                "pause": 1000

            },
            {
                "image": "images/puppy_walking.png",
                "startx": 520,
                "starty": 28,
                "endx": 1,
                "endy": 1,
                "time": 1000
            },
            {
                "image": "images/puppy_crouching.png",
                "startx": 1,
                "starty": 1,
                "endx": 500,
                "endy": 400,
                "time": 2000
            }

        ]
    },
    {
        "title": "scorpion",
        "image_set": [
            {
                "image": "images/scorping_sleeping.png",
                "startx": 100,
                "starty": 400,
                "pause": 5000

            },
            {
                "image": "images/scorpion_walking.png",
                "startx": 100,
                "starty": 400,
                "endx": 500,
                "endy": 400,
                "time": 7000
            },
            {
                "image": "images/scorpion_walking.png",
                "startx": 500,
                "starty": 400,
                "endx": 100,
                "endy": 400,
                "time": 2000
            },
            {
                "image": "images/scorpion_walking.png",
                "startx": 100,
                "starty": 400,
                "endx": 200,
                "endy": 400,
                "time": 7000
            },
            {
                "image": "images/scorpion_walking.png",
                "startx": 200,
                "starty": 400,
                "endx": 100,
                "endy": 400,
                "time": 1000
            }
        ]
    }
]

Each object can have several images related to them. The animations will continue to repeat non-stop. Each object should move concurrently with each of the other objects so that I can create a scene of various animals and objects moving around it.

My animation code:

I'm pretty sure I'm barking up the wrong tree here, but my code looks something like this:

  //image_set is the list of images for a specific object
  //object_num is the array index corresponding to the JSON objects array
  //selected is the array index corresponding to which image in the image_set will be displayed
  runAnimation(image_set, object_num, selected){

        //Uses prevState so that we keep state immutable
        this.setState(prevState => {
            let images = [...prevState.images];

            if (!images[object_num]){
                images.push({image: null, x: 0, y: 0})

            }

            images[object_num].image = image_set[selected].image;
            images[object_num].x = this.getFactoredX(image_set[selected].startx);
            images[object_num].y = this.getFactoredY(image_set[selected].starty);
            return {images: images};
        })

        if (image_set[selected].endx && image_set[selected].endy && image_set[selected].time){
                let x = this.getFactoredX(image_set[selected].startx)
                let y = this.getFactoredY(image_set[selected].starty)
                let startx = x
                let starty = y
                let endx = this.getFactoredX(image_set[selected].endx)
                let endy = this.getFactoredY(image_set[selected].endy)
                let time = image_set[selected].time

                let x_increment = (endx - x) / (time / 50)
                let y_increment = (endy - y) / (time / 50)



                let int = setInterval(function(){

                        x += x_increment
                        y += y_increment


                        if (x > endx || y > endy){
                                clearInterval(int)
                        }

                        this.setState(prevState => {
                                let images = [...prevState.images]

                                if (images[object_num]){
                                        images[object_num].x = x
                                        images[object_num].y = y
                                }


                                return {images: images};


                        })


                }.bind(this),
                 50
                )

        }

        if (image_set[selected].pause && image_set[selected].pause > 0){
                selected++

                if (selected == image_set.length){
                        selected = 0
                }

                setTimeout(function() {
                        this.runAnimation(image_set, object_num, selected)

                }.bind(this),
                  image_set[selected].pause
                )

        }
        else {
                selected++

                if (selected == image_set.length){
                        selected = 0
                }
                setTimeout(function() {
                        this.runAnimation(image_set, object_num, selected)

                }.bind(this),
                        50
                )
        }


  }

Redux and this.props.data

Redux brings in the data as props. So, I have a function called from my componentDidMount and componentWillReceiveProps functions that passes the original image set into the loadAnimationFunction.

My render()

In my render() function I have something like this:

if (this.state.images.length > 1){
    animated = this.state.images.map((image, i) => {
            let x_coord = image.x
            let y_coord = image.y
            return (
                     <div key={i} style=>
                            <img src={`/api/get_image.php?image=${image.image}`} />
                    </div>
            )

    })
}

x_factor / y_factor

Throughout my code there is also reference to x and y factor. This is because the background that the animations appear in may be scaled smaller or larger. Therefore I also scale the position of the starting and ending x/y coordinates for each animation as well as scale the animated images themselves.

time and pause time

Time indicates the time in ms that the animation should take. Pause time indicates how long in ms to pause before moving to the next animation.

The problem

The code does not move the animations smoothly and they seem to jump around sporadically.

Also, when I click the mouse anywhere on the page it causes the animations to jump to another position. Why would clicking the mouse affect the animation?

One thing I've noticed is that if I have the console open for debugging purposes, this really slows down the animations.

What can I do to my code so that the animations work as expected?



from React JS animations based on JSON data

1 comment: