jojo
It's still not entirely clear to me why you can't use the CSS padding approach. Perhaps you could help us better understand your problem by providing a sketch with a couple of rectangles showing the two different widgets (zoomed in and not zoomed in) and the issue with sizing.
In any case, as I mentioned above, you can already achieve this by defining your personalized fit mode.
First, let's understand the easiest way to focus on a specific part of your skeleton in the div: by using the bounds object.
For example, if you want to zoom in on Spineboy's head, you can do it like this:
<spine-widget
identifier="boi"
atlas="assets/spineboy-pma.atlas"
skeleton="assets/spineboy-pro.skel"
animation="walk"
debug
clip
></spine-widget>
<script>
(async () => {
const boi = spine.getSpineWidget("boi");
await boi.loadingPromise;
const myBound = {
x: -110,
y: 350,
width: 310,
height: 350,
};
boi.setBounds(myBound);
})();
</script>
However, this won't add any padding since the skeleton is automatically scaled to fit using the default method, which is contain
.
There's no direct way to define your personalized fit mode, but you can set the fit mode to none
and utilize the beforeUpdateWorldTransforms
callback. The none
fit mode doesn't automatically scale the skeleton.beforeUpdateWorldTransforms
callback. The fit mode none
does not autoscale the skeleton.
In the beforeUpdateWorldTransforms
callback, we want to change the scale of the skeleton based on the div size.
We need to:
- get the div size
- determine the target width/height (1/2 of the div width/height) and transform it to the skeleton space
- determine the smallest scale ratio
- scale the skeleton accordingly
<spine-widget
identifier="boi"
atlas="assets/spineboy-pma.atlas"
skeleton="assets/spineboy-pro.skel"
animation="walk"
fit="none"
debug
clip
></spine-widget>
<script>
(async () => {
const boi = spine.getSpineWidget("boi");
const { skeleton } = await boi.loadingPromise;
const myBound = {
x: -110,
y: 350,
width: 310,
height: 350,
};
boi.setBounds(myBound);
boi.beforeUpdateWorldTransforms = (skeleton, state) => {
// get the div size
const containerBounds = boi.getHTMLElementReference().getBoundingClientRect();
// determine the target width/height (1/2 of the div width/height) and transform it to the skeleton space
const targetWidth = containerBounds.width / 2 * window.devicePixelRatio;
const targetHeight = containerBounds.height / 2 * window.devicePixelRatio;
// determine the smallest scale ratio
const bounds = boi.bounds;
const scaleW = targetWidth / bounds.width;
const scaleH = targetHeight / bounds.height;
const scale = Math.min(scaleW, scaleH);
// scale the skeleton accordingly
skeleton.scaleX = scale;
skeleton.scaleY = scale;
}
})();
</script>
Clearly, this isn't the most straightforward approach, but it works.
We might consider adding a padding option or an easier way to create a custom fit mode in the future.