When you change skins and notice rendering mismatches (cropping, scaling, or positioning issues), this is typically because different skins can have different bounding boxes, which affects how the skeleton is fitted within your container.
Here are several approaches to ensure consistent rendering across skin changes:
1. Using CSS with object-fit properties
If you're using Spine Web Components (<spine-skeleton>), you can control the fitting behavior using the fit attribute:
<spine-skeleton
atlas="path/to/atlas"
skeleton="path/to/skeleton"
animation="animation-name"
skin="skin-name"
fit="contain"
style="width: 500px; height: 500px;"
></spine-skeleton>
Available fit modes:
contain (default): As large as possible while containing the skeleton entirely within the container - maintains aspect ratio
fill: Fills the container by distorting the skeleton's aspect ratio
cover: As small as possible while covering the entire container
scaleDown: Scales down to fit within the container
width: Fills container width, may overflow vertically
height: Fills container height, may overflow horizontally
none: Displays skeleton without regard to container size
2. Programmatically adjusting bounds after skin change
When changing skins programmatically, you need to update the skeleton's pose to ensure attachments are properly set:
const skeleton = yourSpineObject.skeleton;
const skeletonData = skeleton.data;
// Create and apply your custom skin
const customSkin = new spine.Skin("custom");
customSkin.addSkin(skeletonData.findSkin("skin-base"));
customSkin.addSkin(skeletonData.findSkin("new-parts"));
// ... add other skins
skeleton.setSkin(customSkin);
skeleton.setSlotsToSetupPose(); // Critical: reset slots to setup pose
// If using AnimationState, apply current animation
// to ensure attachments are correctly positioned
animationState.apply(skeleton);
3. Force recalculation of bounds
After changing skins, you may need to force the renderer to recalculate the skeleton's bounds:
skeleton.updateWorldTransform();
skeleton.getBounds(offset, size, temp); // Recalculate bounds
4. Using a fixed scale
For more control, you can set a fixed scale and use fit="none":
<spine-skeleton
atlas="path/to/atlas"
skeleton="path/to/skeleton"
animation="animation-name"
skin="skin-name"
scale="0.5"
fit="none"
style="width: 500px; height: 500px;"
></spine-skeleton>
5. CSS container approach
If you need very specific control, use CSS to create a consistent viewport:
.spine-container {
width: 500px;
height: 500px;
overflow: hidden; /* Crop if needed */
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.spine-container canvas {
max-width: 100%;
max-height: 100%;
object-fit: contain; /* Maintains aspect ratio */
}
Best Practice Recommendation
The most reliable approach is to:
- Use
fit="contain" to maintain aspect ratio
- Always call
skeleton.setSlotsToSetupPose() after changing skins
- Set fixed dimensions on your container element
- Ensure all your skins are designed with similar bounds in Spine editor
If your new skins are significantly larger, you may want to adjust their setup in the Spine editor to have more consistent bounds across all skins, or use the scale attribute to normalize their size when they differ significantly.