You have full access to the AnimationState the player uses. Use that instead of play/pause. Don't set an animation, then since you assign the player to playerRef.current you can play an animation at any time like this:

playerRef.current.animationState.setAnimation(0, "animation-name", false);

The animation will continue to be played, so you can use an empty animation to fade it in/out, if you like:

playerRef.current.animationState.setEmptyAnimation(0, 0);
var entry = playerRef.current.animationState.addAnimation(0, "animation-name", false, 0);
entry.setMixDuration(0.5); // Fade in time (from setup pose).
playerRef.current.animationState.setEmptyAnimation(0, 0.5); // Fade out time (to setup pose).

See the API reference:
http://esotericsoftware.com/spine-api-reference
And:
https://esotericsoftware.com/spine-runtimes-guide

Related Discussions
...

This is very good suggestion Nate, thanks

I am failing to make that section work though, when I add these lines skeleton just wont load, dont think there are errors.. but still.. something is wrong

full code

<script src="https://unpkg.com/@esotericsoftware/spine-player@4.2.*/dist/iife/spine-player.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/@esotericsoftware/spine-player@4.2.*/dist/spine-player.min.css">
<div id="player-container" style="width: 100%; height: 100vh; margin: 0; border: none!important; overflow: hidden;"></div>
<style>
   body {
   overflow: hidden; /* Hide scrollbars */
   display: block";
   }
</style>
<script>
   new spine.SpinePlayer("player-container", {
   	jsonUrl: "https://warmanw.com/spine/lunar-beast-annie-alistar.json",
   	atlasUrl: "https://warmanw.com/spine/lunar-beast-annie-alistar.atlas",
   	//animation: "combined",
   	premultipliedAlpha: true,
   	showControls: false,
   	alpha: false,
   	backgroundColor: "#202020ff",
   	fullScreenBackgroundColor: "#202020ff",
   	viewport: {
         x: -2000,
         y: 800,
         width: 4000,
         height: 4000,
         padLeft: "0%",
         padRight: "0%",
         padTop: "0%",
         padBottom: "0%",
         //debugRender: true
   	},
   	success: (player) => {
         player.animationState.setAnimation(0, "combined", true);
   	}
   });
</script>

p.s. I was also followign this example
EsotericSoftware/spine-runtimesblob/4.1/spine-ts/spine-player/example/example.html

Great! Thanks now i can play the animation once, reset it and play it again every time user clicks a button with this useEffect!.

  useEffect(() => {
    if(playerRef.current && animationState === 'play') {
      playerRef.current.animationState?.setEmptyAnimation(0, 0)
      playerRef.current.animationState?.addAnimation(0, 'default', false, 0)
      playerRef.current.play()
    }
  },[animationState])

Happy to see your progress @GTomasel

entry.setMixDuration(0.5); // Fade in time (from setup pose).

Nate this line throws error, I checked entry is not null ("Entry value logs as [object Object]")

Error: Unable to render skeleton.
entry.setMixDuration is not a function

Also I still struggle to play animation in success callback instead of setting in the player as default animation. the thing is there is no error, after I click on the player skeleton snaps into place and animation starts playing, I tried:

  • On firefox
  • With spineboy assets
  • To log all and Success callback actually logs and all objects are not null
  • Delaying calling animation in success callback for .5 sec
  • Setup a different viewport for that animation.

p.s Nate, I am very grateful for any of your posts. However I am not expecting you to jump in and help me everytime. I will continue my struggles and maybe tomorrow I will figure it out, I feel it is something simple and stupid.

@warmanw The JavaScript runtime doesn't use setters. Use eg entry.setMixDuration = 0.25;. Show your code for success. You may need player.play(); as GTomasel showed above.

Here's some example code (though is is using the 4.0 player):
http://esotericsoftware.com/files/blog/4.0-released/blog.js

It is the code used on this page:
http://esotericsoftware.com/blog/Spine-4.0-is-here

It shows some cool things, like the Downloader. That enables the same assets (namely the texture atlas) to be used by multiple players, without downloading and parsing them multiple times.

Don't worry about asking for assistance, we are here to help! That said, we do appreciate that you are putting in sufficient effort to help yourself. When we see that it just makes us want to help more. 🙂

This piece of code is gold, I searched all google have no idea why could not find it. was scrubbing all korean sites already 😃

Thanks Nate!

So Before your post I already figured out how to make animation play in success,

This works

  success: function () {
    	 alistar.addAnimation("blackscreen",false);
         alistar.addAnimation("combined",true ).mixDuration = 6;
         alistar.play();
      }

This does not work, it seems I am failing to make state to work,still no errors but ir is not responding

 success: function () {
    	 alistar.animationState.addAnimation(0,"blackscreen",false);
         alistar.animationState.addAnimation(0,"combined",true ).mixDuration = 6;
         alistar.play();
      }

So my current questions are:

  • Why I cant use animationState
  • Why when I hide the loading showLoading: false, I see instead black background
  • How to make it load like a feather. I am looking forward to change my reel page as well so I need as fast as possible. Exported files are huge I know, but still not 150mb as was the video I previously used.

alistar.addAnimation calls this:
EsotericSoftware/spine-runtimesblob/4.1/spine-ts/spine-player/src/Player.ts#L700
That changes the viewport, sets animation to an Animation object, and then calls AnimationState addAnimationWith, which takes an Animation object.

That differs from AnimationState addAnimation which takes a string that is the name of the animation, not an Animation object. See the code for that, it just looks up the Animation object and calls addAnimationWith:
EsotericSoftware/spine-runtimesblob/4.1/spine-ts/spine-core/src/AnimationState.ts#L549

My guess is your animationState.addAnimation code works, but doesn't set the viewport, so you don't see what you expect. You might try:

 success: function () {
    	 alistar.setViewport("combined");
    	 alistar.animationState.addAnimation(0,"blackscreen",false);
         alistar.animationState.addAnimation(0,"combined",true ).mixDuration = 6;
         alistar.play();
      }

Re: showLoading: false what do you expect to see?

Never use JSON unless you need to look at or manipulate the data. Use binary. It's smaller and loads faster.

You can use gzip and/or brotli to compress your atlas and binary files. This requires your web server to do the right things though.

Your atlas is massive. Export with a lower scale unless it's really needed. You might try exporting smaller and see if it still looks good enough. The player doesn't have a way to set the scale of the SkeletonJson or SkeletonBinary loader. I'll add that in 4.2-beta.

You can make an atlas that multiple skeletons share, then use Downloader so the atlas is only parsed once. See my post above.

    Nate Your code works Nate 😃 it was indeed viewport issue.

    Re: showLoading: false what do you expect to see?
    Background color I set in player

    Thanks for explanation I really like one big atlas option.

    I'm not sure on the background! I'll have to try it.

    8 months later
    6 months later

    I dont know why but I end up coming to this thread every year.

    ChatGPT didn’t provide the help I needed. I’ve gone through multiple example codes to learn more, but I’m still stuck on this simple issue and can’t seem to get past it
    Please clarify for me: The code below works fine, except it does not play the animation, no errors it looks like it plays the first frame but does not update/apply every frame so animation is still, if I add player.play(); animation plays but I want to have more control over the state, and not just play and pause the player. eventually I want to play multiple animations on different tracks once, not in a loop

    <script>  
        new spine.SpinePlayer("player-container", {
    	jsonUrl: "gifts.json",
    	atlasUrl: "gifts.atlas",
    	showControls: true,
    	skin: "bluff",
    	backgroundColor:  "000000",
    	defaultMix: 0,
    	premultipliedAlpha: true,
    	success: function (player) {
    		player.setViewport("base");
    		player.animationState.setAnimation(0, "base", false);		
            }	
       }); 
      </script>

    if you visit here you will see the player is playing but animation is not.

      See the player code here, where it calls success and after:
      EsotericSoftware/spine-runtimesblob/4.2/spine-ts/spine-player/src/Player.ts#L550-L567

      if (!entry) { will be false, because you set an entry. Next we go to } else if (!this.currentViewport) { which is false because you set a viewport. The result is this code doesn't call play(), which is what makes the player advance the skeleton's animation state.

      It makes sense the player automatically plays if the animation config property is set. Otherwise I'm not sure it makes sense to only play if a viewport hasn't been set. I'll bring it up with our dev team. Also, when this code doesn't call play(), the play/pause button shows pause, which is strange.

      In the meantime, try calling player.play();.

      warmanw

      Thanks for reporting this! That’s a regression.
      You should be able to get your desired result by simply calling:

      player.animationState.setAnimation(0, "base", false);

      in your success callback.
      Just wait until the end of the day. A fixed version (4.2.73) of the player will be available. 🙂


      Released! Let us know if now it works as you expected.

      My eyes are in tears—thank you so much! It works like a charm.

      I have one more question: When I don't set an animation immediately on success and just add a keydown listener, the keys won't respond unless I click on the player, which then plays the animation. Is there a way to prevent the animation from playing or pausing when clicked? Also, why don’t the key presses work unless I first click on the player?

        warmanw

        why don’t the key presses work unless I first click on the player?

        You are probably adding the event listener to the html element containing the player. That event listener will listen events only when the element is focused.
        You probably want to add the event listener to the document.

        Is there a way to prevent the animation from playing or pausing when clicked?

        Right now, the only way is setting showControls: false. That has the side effect to hide the player UI.

        You are probably adding the event listener to the html element containing the player

        Not really, I add it to document, even tried on window, still it only catches after player is clicked

        here is the code

         <script>
                    var player = new spine.SpinePlayer("player-container", {
                        jsonUrl: "gifts.json",
                        atlasUrl: "gifts.atlas",
                        showControls: true,
                        skin: "bluff",
                        backgroundColor: "000000",
                        defaultMix: 0,
                        premultipliedAlpha: true,
                        success: function (player) {
                            player.setViewport("base");
        					
                        },
                    });
        
                    document.addEventListener("keydown", function (event) {
                        if (!player) return; // Ensure player is loaded
        
                        let currentAnimation = player.animationState.getCurrent(0)?.animation?.name;
                        if (currentAnimation !== "base") {
                            player.animationState.setAnimation(0, "base", false);
                        } else {
                            let randomIndex = Math.floor(Math.random() * 6) + 1;
                            player.animationState.setAnimation(randomIndex, "meta_" + randomIndex, false);
                        }
                    });
                </script>

          warmanw

          If you don't set any animation at the beginning, the player won't play anything.
          Set a default animation, or invoke player.play() in the success callback.

          Thanks for your help Davide you already saved my day, however I want the player to start without playing anything and only when I press a key it should play an animation once. but pressing key wont respond unless I click on player which starts animation which I dotn want.

          if (!player) return; This isn't doing anything. You set the player before even adding the listener, so it's always set.

          As Davide said, you aren't setting an animation in success, so the player isn't playing. Call play(). Also don't indent your code inside a script like a psychopath.

          <script>
          var player = new spine.SpinePlayer("player-container", {
          	jsonUrl: "gifts.json",
          	atlasUrl: "gifts.atlas",
          	showControls: true,
          	skin: "bluff",
          	backgroundColor: "000000",
          	defaultMix: 0,
          	premultipliedAlpha: true,
          	success: function (player) {
          		player.setViewport("base");
          		player.play();
          	},
          });
          
          document.addEventListener("keydown", function (event) {
          	let currentAnimation = player.animationState.getCurrent(0)?.animation?.name;
          	if (currentAnimation !== "base") {
          		 player.animationState.setAnimation(0, "base", false);
          	} else {
          		 let randomIndex = Math.floor(Math.random() * 6) + 1;
          		 player.animationState.setAnimation(randomIndex, "meta_" + randomIndex, false);
          	}
          });
          </script>