@react-three/drei 9.68.6 → 9.69.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -0
- package/core/SpriteAnimator.cjs.js +1 -0
- package/core/SpriteAnimator.d.ts +25 -0
- package/core/SpriteAnimator.js +358 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -105,6 +105,7 @@ The `native` route of the library **does not** export `Html` or `Loader`. The de
|
|
|
105
105
|
<li><a href="#html">Html</a></li>
|
|
106
106
|
<li><a href="#cycleraycast">CycleRaycast</a></li>
|
|
107
107
|
<li><a href="#select">Select</a></li>
|
|
108
|
+
<li><a href="#spriteanimator">Sprite Animator</a></li>
|
|
108
109
|
<li><a href="#stats">Stats</a></li>
|
|
109
110
|
<li><a href="#wireframe">Wireframe</a></li>
|
|
110
111
|
<li><a href="#usedepthbuffer">useDepthBuffer</a></li>
|
|
@@ -2077,6 +2078,69 @@ function Foo() {
|
|
|
2077
2078
|
const selected = useSelect()
|
|
2078
2079
|
```
|
|
2079
2080
|
|
|
2081
|
+
#### Sprite Animator
|
|
2082
|
+
|
|
2083
|
+

|
|
2084
|
+
|
|
2085
|
+
<p>
|
|
2086
|
+
<a href="https://codesandbox.io/s/r3f-sprite-animator-s12ijv"><img width="20%" src="https://codesandbox.io/api/v1/sandboxes/s12ijv/screenshot.png" alt="Demo"/></a>
|
|
2087
|
+
</p>
|
|
2088
|
+
|
|
2089
|
+
```tsx
|
|
2090
|
+
type Props = {
|
|
2091
|
+
/** The start frame of the animation */
|
|
2092
|
+
startFrame?: number
|
|
2093
|
+
/** The end frame of the animation */
|
|
2094
|
+
endFrame?: number
|
|
2095
|
+
/** The desired frames per second of the animaiton */
|
|
2096
|
+
fps?: number
|
|
2097
|
+
/** The frame identifier to use, has to be one of animationNames */
|
|
2098
|
+
frameName?: string
|
|
2099
|
+
/** The scale factor of the aspect ratio */
|
|
2100
|
+
scaleFactor?: number
|
|
2101
|
+
/** The URL of the texture JSON (if using JSON-Array or JSON-Hash) */
|
|
2102
|
+
textureDataURL?: string
|
|
2103
|
+
/** The URL of the texture image */
|
|
2104
|
+
textureImageURL?: string
|
|
2105
|
+
/** Whether or not the animation should loop */
|
|
2106
|
+
loop?: boolean
|
|
2107
|
+
/** The number of frames of the animation (required if using plain spritesheet without JSON) */
|
|
2108
|
+
numberOfFrames?: number
|
|
2109
|
+
/** Whether or not the animation should auto-start when all assets are loaded */
|
|
2110
|
+
autoPlay?: boolean
|
|
2111
|
+
/** The animation names of the spritesheet (if the spritesheet -with JSON- contains more animation sequences) */
|
|
2112
|
+
animationNames?: Array<string>
|
|
2113
|
+
/** Event callback when the animation starts */
|
|
2114
|
+
onStart?: Function
|
|
2115
|
+
/** Event callback when the animation ends */
|
|
2116
|
+
onEnd?: Function
|
|
2117
|
+
/** Event callback when the animation loops */
|
|
2118
|
+
onLoopEnd?: Function
|
|
2119
|
+
/** Event callback when each frame changes */
|
|
2120
|
+
onFrame?: Function
|
|
2121
|
+
/** Control when the animation runs */
|
|
2122
|
+
play?: boolean
|
|
2123
|
+
/** Control when the animation pauses */
|
|
2124
|
+
pause?: boolean
|
|
2125
|
+
}
|
|
2126
|
+
```
|
|
2127
|
+
|
|
2128
|
+
The SpriteAnimator component provided by drei is a powerful tool for animating sprites in a simple and efficient manner. It allows you to create sprite animations by cycling through a sequence of frames from a sprite sheet image or JSON data.
|
|
2129
|
+
Notes:
|
|
2130
|
+
The SpriteAnimator component internally uses the useFrame hook from react-three-fiber (r3f) for efficient frame updates and rendering.
|
|
2131
|
+
|
|
2132
|
+
```jsx
|
|
2133
|
+
<SpriteAnimator
|
|
2134
|
+
position={[-3.5, -2.0, 2.5]}
|
|
2135
|
+
startFrame={0}
|
|
2136
|
+
scaleFactor={0.125}
|
|
2137
|
+
autoPlay={true}
|
|
2138
|
+
loop={true}
|
|
2139
|
+
numberOfFrames={16}
|
|
2140
|
+
textureImageURL={'./alien.png'}
|
|
2141
|
+
/>
|
|
2142
|
+
```
|
|
2143
|
+
|
|
2080
2144
|
#### Stats
|
|
2081
2145
|
|
|
2082
2146
|
[](https://drei.vercel.app/?path=/story/misc-stats--default-story)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),r=require("@react-three/fiber"),t=require("three");function n(e){if(e&&e.__esModule)return e;var r=Object.create(null);return e&&Object.keys(e).forEach((function(t){if("default"!==t){var n=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(r,t,n.get?n:{enumerable:!0,get:function(){return e[t]}})}})),r.default=e,Object.freeze(r)}var a=n(e),u=n(t);exports.SpriteAnimator=({startFrame:e,endFrame:t,fps:n,frameName:c,scaleFactor:s,maxScale:o,textureDataURL:f,textureImageURL:i,loop:m,numberOfFrames:l,autoPlay:p,animationNames:h,onStart:w,onEnd:y,onLoopEnd:d,onFrame:S,play:g,pause:x,flipX:b,alphaTest:z,children:F,...A},E)=>{const O=r.useThree((e=>e.viewport)),R=a.useRef(null),[j,v]=a.useState(!1),L=a.useRef(),P=a.useRef(),T=a.useRef(window.performance.now()),M=a.useRef(),N=a.useRef(e||0),k=a.useRef(c||""),_=1e3/(n||30),[q,C]=a.useState(new u.Texture),D=a.useRef(0),[I,U]=a.useState([1,1,1]),W=s||.1,X=b?-1:1;const B=(e,r,t)=>{const n=r*(O.aspect>e/r?O.width/e:O.height/r),a=e*(O.aspect>e/r?O.width/e:O.height/r)*t,u=n*t,c=null!=o?o:1;let s=Math.min(c,a),f=Math.min(c,u);return a>c&&(s=c,f=u/a*c),P.current.scale.set(s,f,1),[s,f,1]};a.useEffect((()=>{if(f&&i)!function(e,r,t){const n=new u.TextureLoader,a=fetch(e).then((e=>e.json())),c=new Promise((e=>{n.load(r,e)}));Promise.all([a,c]).then((e=>{t(e[0],e[1])}))}(f,i,G);else if(i){const e=new u.TextureLoader;new Promise((r=>{e.load(i,r)})).then((e=>{G(null,e)}))}}),[]),a.useLayoutEffect((()=>{J()}),[q]),a.useEffect((()=>{}),[x]),a.useEffect((()=>{k.current!==c&&c&&(N.current=0,k.current=c)}),[c]);const G=(e,r)=>{if(null===e){if(r&&l){const e=r.image.width,t=r.image.height,n=e/l,a=t;if(M.current=r,D.current=l,R.current={frames:[],meta:{version:"1.0",size:{w:e,h:t},scale:"1"}},parseInt(n.toString(),10)===n)for(let e=0;e<l;e++)R.current.frames.push({frame:{x:e*n,y:0,w:n,h:a},rotated:!1,trimmed:!1,spriteSourceSize:{x:0,y:0,w:n,h:a},sourceSize:{w:n,h:t}})}}else if(r){R.current=e,R.current.frames=Array.isArray(e.frames)?e.frames:H(),D.current=Array.isArray(e.frames)?e.frames.length:Object.keys(e.frames).length,M.current=r;const{w:t,h:n}=K(e.frames).sourceSize,a=B(t,n,W);U(a),L.current&&(L.current.map=r)}r.premultiplyAlpha=!1,C(r)},H=()=>{const e={},r=R.current,t=h;if(t)for(let n=0;n<t.length;n++){e[t[n]]=[];for(let a in r.frames){const u=r.frames[a],c=u.frame,s=c.x,o=c.y,f=c.w,i=c.h,m=u.sourceSize.w,l=u.sourceSize.h;"string"==typeof a&&-1!==a.toLowerCase().indexOf(t[n].toLowerCase())&&e[t[n]].push({x:s,y:o,w:f,h:i,frame:c,sourceSize:{w:m,h:l}})}}return e},J=()=>{if(!R.current)return;const{meta:{size:e},frames:r}=R.current,{w:t,h:n}=Array.isArray(r)?r[0].sourceSize:c&&r[c]?r[c][0].sourceSize:{w:0,h:0};L.current.map.wrapS=L.current.map.wrapT=u.RepeatWrapping,L.current.map.center.set(0,0),L.current.map.repeat.set(1*X/(e.w/t),1/(e.h/n));const a=1/((e.h-1)/n);L.current.map.offset.x=0,L.current.map.offset.y=1-a,v(!0),w&&w({currentFrameName:c,currentFrame:N.current})};r.useFrame(((r,n)=>{var a,u;null!=(a=R.current)&&a.frames&&null!=(u=L.current)&&u.map&&(x||(p||g)&&((()=>{const r=window.performance.now(),n=r-T.current,{meta:{size:a},frames:u}=R.current,{w:s,h:o}=K(u).sourceSize,f=Array.isArray(u)?u:c?u[c]:[];let i=0,l=0;const p=t||f.length-1;if(N.current>p&&(N.current=m&&null!=e?e:0,m?null==d||d({currentFrameName:c,currentFrame:N.current}):null==y||y({currentFrameName:c,currentFrame:N.current}),!m))return;if(n<=_)return;T.current=r-n%_,B(s,o,W);const h=(a.w-1)/s,w=(a.h-1)/o,{frame:{x:S,y:g},sourceSize:{w:x,h:b}}=f[N.current],z=1/h,F=1/w;i=X>0?z*(S/x):z*(S/x)-L.current.map.repeat.x,l=Math.abs(1-F)-F*(g/b),L.current.map.offset.x=i,L.current.map.offset.y=l,N.current+=1})(),S&&S({currentFrameName:k.current,currentFrame:N.current})))}));const K=e=>{if(Array.isArray(e))return e[0];if("object"==typeof e&&null!==e){return e[Object.keys(e)[0]][0]}return{w:0,h:0}};return a.createElement("group",A,a.createElement(a.Suspense,{fallback:null},a.createElement("sprite",{ref:P,scale:I},a.createElement("spriteMaterial",{ref:L,map:q,premultipliedAlpha:!1,transparent:!0,alphaTest:null!=z?z:0}))),F)};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export declare type SpriteAnimatorProps = {
|
|
3
|
+
startFrame?: number;
|
|
4
|
+
endFrame?: number;
|
|
5
|
+
fps?: number;
|
|
6
|
+
frameName?: string;
|
|
7
|
+
scaleFactor?: number;
|
|
8
|
+
maxScale?: number;
|
|
9
|
+
textureDataURL?: string;
|
|
10
|
+
textureImageURL: string;
|
|
11
|
+
loop?: boolean;
|
|
12
|
+
numberOfFrames?: number;
|
|
13
|
+
autoPlay?: boolean;
|
|
14
|
+
animationNames?: Array<string>;
|
|
15
|
+
onStart?: Function;
|
|
16
|
+
onEnd?: Function;
|
|
17
|
+
onLoopEnd?: Function;
|
|
18
|
+
onFrame?: Function;
|
|
19
|
+
play?: boolean;
|
|
20
|
+
pause?: boolean;
|
|
21
|
+
flipX?: boolean;
|
|
22
|
+
position?: Array<number>;
|
|
23
|
+
alphaTest?: number;
|
|
24
|
+
} & JSX.IntrinsicElements['group'];
|
|
25
|
+
export declare const SpriteAnimator: React.FC<SpriteAnimatorProps>;
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useThree, useFrame } from '@react-three/fiber';
|
|
3
|
+
import * as THREE from 'three';
|
|
4
|
+
|
|
5
|
+
const SpriteAnimator = ({
|
|
6
|
+
startFrame,
|
|
7
|
+
endFrame,
|
|
8
|
+
fps,
|
|
9
|
+
frameName,
|
|
10
|
+
scaleFactor,
|
|
11
|
+
maxScale,
|
|
12
|
+
textureDataURL,
|
|
13
|
+
textureImageURL,
|
|
14
|
+
loop,
|
|
15
|
+
numberOfFrames,
|
|
16
|
+
autoPlay,
|
|
17
|
+
animationNames,
|
|
18
|
+
onStart,
|
|
19
|
+
onEnd,
|
|
20
|
+
onLoopEnd,
|
|
21
|
+
onFrame,
|
|
22
|
+
play,
|
|
23
|
+
pause,
|
|
24
|
+
flipX,
|
|
25
|
+
alphaTest,
|
|
26
|
+
children,
|
|
27
|
+
...props
|
|
28
|
+
}, fref) => {
|
|
29
|
+
const v = useThree(state => state.viewport);
|
|
30
|
+
const spriteData = React.useRef(null);
|
|
31
|
+
const [isJsonReady, setJsonReady] = React.useState(false);
|
|
32
|
+
const matRef = React.useRef();
|
|
33
|
+
const spriteRef = React.useRef();
|
|
34
|
+
const timerOffset = React.useRef(window.performance.now());
|
|
35
|
+
const textureData = React.useRef();
|
|
36
|
+
const currentFrame = React.useRef(startFrame || 0);
|
|
37
|
+
const currentFrameName = React.useRef(frameName || '');
|
|
38
|
+
const fpsInterval = 1000 / (fps || 30);
|
|
39
|
+
const [spriteTexture, setSpriteTexture] = React.useState(new THREE.Texture());
|
|
40
|
+
const totalFrames = React.useRef(0);
|
|
41
|
+
const [aspect, setAspect] = React.useState([1, 1, 1]);
|
|
42
|
+
const aspectFactor = scaleFactor || 0.1;
|
|
43
|
+
const flipOffset = flipX ? -1 : 1;
|
|
44
|
+
|
|
45
|
+
function loadJsonAndTextureAndExecuteCallback(jsonUrl, textureUrl, callback) {
|
|
46
|
+
const textureLoader = new THREE.TextureLoader();
|
|
47
|
+
const jsonPromise = fetch(jsonUrl).then(response => response.json());
|
|
48
|
+
const texturePromise = new Promise(resolve => {
|
|
49
|
+
textureLoader.load(textureUrl, resolve);
|
|
50
|
+
});
|
|
51
|
+
Promise.all([jsonPromise, texturePromise]).then(response => {
|
|
52
|
+
callback(response[0], response[1]);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const calculateAspectRatio = (width, height, factor) => {
|
|
57
|
+
const adaptedHeight = height * (v.aspect > width / height ? v.width / width : v.height / height);
|
|
58
|
+
const adaptedWidth = width * (v.aspect > width / height ? v.width / width : v.height / height);
|
|
59
|
+
const scaleX = adaptedWidth * factor;
|
|
60
|
+
const scaleY = adaptedHeight * factor;
|
|
61
|
+
const currentMaxScale = maxScale !== null && maxScale !== void 0 ? maxScale : 1; // Calculate the maximum scale based on the aspect ratio and max scale limit
|
|
62
|
+
|
|
63
|
+
let finalMaxScaleW = Math.min(currentMaxScale, scaleX);
|
|
64
|
+
let finalMaxScaleH = Math.min(currentMaxScale, scaleY); // Ensure that scaleX and scaleY do not exceed the max scale while maintaining aspect ratio
|
|
65
|
+
|
|
66
|
+
if (scaleX > currentMaxScale) {
|
|
67
|
+
finalMaxScaleW = currentMaxScale;
|
|
68
|
+
finalMaxScaleH = scaleY / scaleX * currentMaxScale;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
spriteRef.current.scale.set(finalMaxScaleW, finalMaxScaleH, 1);
|
|
72
|
+
return [finalMaxScaleW, finalMaxScaleH, 1];
|
|
73
|
+
}; // initial loads
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
React.useEffect(() => {
|
|
77
|
+
if (textureDataURL && textureImageURL) {
|
|
78
|
+
loadJsonAndTextureAndExecuteCallback(textureDataURL, textureImageURL, parseSpriteData);
|
|
79
|
+
} else if (textureImageURL) {
|
|
80
|
+
// only load the texture, this is an image sprite only
|
|
81
|
+
const textureLoader = new THREE.TextureLoader();
|
|
82
|
+
new Promise(resolve => {
|
|
83
|
+
textureLoader.load(textureImageURL, resolve);
|
|
84
|
+
}).then(texture => {
|
|
85
|
+
parseSpriteData(null, texture);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}, []);
|
|
89
|
+
React.useLayoutEffect(() => {
|
|
90
|
+
modifySpritePosition();
|
|
91
|
+
}, [spriteTexture]);
|
|
92
|
+
React.useEffect(() => {
|
|
93
|
+
}, [pause]);
|
|
94
|
+
React.useEffect(() => {
|
|
95
|
+
if (currentFrameName.current !== frameName && frameName) {
|
|
96
|
+
currentFrame.current = 0;
|
|
97
|
+
currentFrameName.current = frameName;
|
|
98
|
+
}
|
|
99
|
+
}, [frameName]);
|
|
100
|
+
|
|
101
|
+
const parseSpriteData = (json, _spriteTexture) => {
|
|
102
|
+
// sprite only case
|
|
103
|
+
if (json === null) {
|
|
104
|
+
if (_spriteTexture && numberOfFrames) {
|
|
105
|
+
//get size from texture
|
|
106
|
+
const width = _spriteTexture.image.width;
|
|
107
|
+
const height = _spriteTexture.image.height;
|
|
108
|
+
const frameWidth = width / numberOfFrames;
|
|
109
|
+
const frameHeight = height;
|
|
110
|
+
textureData.current = _spriteTexture;
|
|
111
|
+
totalFrames.current = numberOfFrames;
|
|
112
|
+
spriteData.current = {
|
|
113
|
+
frames: [],
|
|
114
|
+
meta: {
|
|
115
|
+
version: '1.0',
|
|
116
|
+
size: {
|
|
117
|
+
w: width,
|
|
118
|
+
h: height
|
|
119
|
+
},
|
|
120
|
+
scale: '1'
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
if (parseInt(frameWidth.toString(), 10) === frameWidth) {
|
|
125
|
+
// if it fits
|
|
126
|
+
for (let i = 0; i < numberOfFrames; i++) {
|
|
127
|
+
spriteData.current.frames.push({
|
|
128
|
+
frame: {
|
|
129
|
+
x: i * frameWidth,
|
|
130
|
+
y: 0,
|
|
131
|
+
w: frameWidth,
|
|
132
|
+
h: frameHeight
|
|
133
|
+
},
|
|
134
|
+
rotated: false,
|
|
135
|
+
trimmed: false,
|
|
136
|
+
spriteSourceSize: {
|
|
137
|
+
x: 0,
|
|
138
|
+
y: 0,
|
|
139
|
+
w: frameWidth,
|
|
140
|
+
h: frameHeight
|
|
141
|
+
},
|
|
142
|
+
sourceSize: {
|
|
143
|
+
w: frameWidth,
|
|
144
|
+
h: height
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
} else if (_spriteTexture) {
|
|
151
|
+
spriteData.current = json;
|
|
152
|
+
spriteData.current.frames = Array.isArray(json.frames) ? json.frames : parseFrames();
|
|
153
|
+
totalFrames.current = Array.isArray(json.frames) ? json.frames.length : Object.keys(json.frames).length;
|
|
154
|
+
textureData.current = _spriteTexture;
|
|
155
|
+
const {
|
|
156
|
+
w,
|
|
157
|
+
h
|
|
158
|
+
} = getFirstItem(json.frames).sourceSize;
|
|
159
|
+
const aspect = calculateAspectRatio(w, h, aspectFactor);
|
|
160
|
+
setAspect(aspect);
|
|
161
|
+
|
|
162
|
+
if (matRef.current) {
|
|
163
|
+
matRef.current.map = _spriteTexture;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
_spriteTexture.premultiplyAlpha = false;
|
|
168
|
+
setSpriteTexture(_spriteTexture);
|
|
169
|
+
}; // for frame based JSON Hash sprite data
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
const parseFrames = () => {
|
|
173
|
+
const sprites = {};
|
|
174
|
+
const data = spriteData.current;
|
|
175
|
+
const delimiters = animationNames;
|
|
176
|
+
|
|
177
|
+
if (delimiters) {
|
|
178
|
+
for (let i = 0; i < delimiters.length; i++) {
|
|
179
|
+
sprites[delimiters[i]] = [];
|
|
180
|
+
|
|
181
|
+
for (let innerKey in data['frames']) {
|
|
182
|
+
const value = data['frames'][innerKey];
|
|
183
|
+
const frameData = value['frame'];
|
|
184
|
+
const x = frameData['x'];
|
|
185
|
+
const y = frameData['y'];
|
|
186
|
+
const width = frameData['w'];
|
|
187
|
+
const height = frameData['h'];
|
|
188
|
+
const sourceWidth = value['sourceSize']['w'];
|
|
189
|
+
const sourceHeight = value['sourceSize']['h'];
|
|
190
|
+
|
|
191
|
+
if (typeof innerKey === 'string' && innerKey.toLowerCase().indexOf(delimiters[i].toLowerCase()) !== -1) {
|
|
192
|
+
sprites[delimiters[i]].push({
|
|
193
|
+
x: x,
|
|
194
|
+
y: y,
|
|
195
|
+
w: width,
|
|
196
|
+
h: height,
|
|
197
|
+
frame: frameData,
|
|
198
|
+
sourceSize: {
|
|
199
|
+
w: sourceWidth,
|
|
200
|
+
h: sourceHeight
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return sprites;
|
|
209
|
+
}; // modify the sprite material after json is parsed and state updated
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
const modifySpritePosition = () => {
|
|
213
|
+
if (!spriteData.current) return;
|
|
214
|
+
const {
|
|
215
|
+
meta: {
|
|
216
|
+
size: metaInfo
|
|
217
|
+
},
|
|
218
|
+
frames
|
|
219
|
+
} = spriteData.current;
|
|
220
|
+
const {
|
|
221
|
+
w: frameW,
|
|
222
|
+
h: frameH
|
|
223
|
+
} = Array.isArray(frames) ? frames[0].sourceSize : frameName ? frames[frameName] ? frames[frameName][0].sourceSize : {
|
|
224
|
+
w: 0,
|
|
225
|
+
h: 0
|
|
226
|
+
} : {
|
|
227
|
+
w: 0,
|
|
228
|
+
h: 0
|
|
229
|
+
};
|
|
230
|
+
matRef.current.map.wrapS = matRef.current.map.wrapT = THREE.RepeatWrapping;
|
|
231
|
+
matRef.current.map.center.set(0, 0);
|
|
232
|
+
matRef.current.map.repeat.set(1 * flipOffset / (metaInfo.w / frameW), 1 / (metaInfo.h / frameH)); //const framesH = (metaInfo.w - 1) / frameW
|
|
233
|
+
|
|
234
|
+
const framesV = (metaInfo.h - 1) / frameH;
|
|
235
|
+
const frameOffsetY = 1 / framesV;
|
|
236
|
+
matRef.current.map.offset.x = 0.0; //-matRef.current.map.repeat.x
|
|
237
|
+
|
|
238
|
+
matRef.current.map.offset.y = 1 - frameOffsetY;
|
|
239
|
+
setJsonReady(true);
|
|
240
|
+
if (onStart) onStart({
|
|
241
|
+
currentFrameName: frameName,
|
|
242
|
+
currentFrame: currentFrame.current
|
|
243
|
+
});
|
|
244
|
+
}; // run the animation on each frame
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
const runAnimation = () => {
|
|
248
|
+
//if (!frameName) return
|
|
249
|
+
const now = window.performance.now();
|
|
250
|
+
const diff = now - timerOffset.current;
|
|
251
|
+
const {
|
|
252
|
+
meta: {
|
|
253
|
+
size: metaInfo
|
|
254
|
+
},
|
|
255
|
+
frames
|
|
256
|
+
} = spriteData.current;
|
|
257
|
+
const {
|
|
258
|
+
w: frameW,
|
|
259
|
+
h: frameH
|
|
260
|
+
} = getFirstItem(frames).sourceSize;
|
|
261
|
+
const spriteFrames = Array.isArray(frames) ? frames : frameName ? frames[frameName] : [];
|
|
262
|
+
let finalValX = 0;
|
|
263
|
+
let finalValY = 0;
|
|
264
|
+
|
|
265
|
+
const _endFrame = endFrame || spriteFrames.length - 1;
|
|
266
|
+
|
|
267
|
+
if (currentFrame.current > _endFrame) {
|
|
268
|
+
currentFrame.current = loop ? startFrame !== null && startFrame !== void 0 ? startFrame : 0 : 0;
|
|
269
|
+
|
|
270
|
+
if (loop) {
|
|
271
|
+
onLoopEnd == null ? void 0 : onLoopEnd({
|
|
272
|
+
currentFrameName: frameName,
|
|
273
|
+
currentFrame: currentFrame.current
|
|
274
|
+
});
|
|
275
|
+
} else {
|
|
276
|
+
onEnd == null ? void 0 : onEnd({
|
|
277
|
+
currentFrameName: frameName,
|
|
278
|
+
currentFrame: currentFrame.current
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (!loop) return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (diff <= fpsInterval) return;
|
|
286
|
+
timerOffset.current = now - diff % fpsInterval;
|
|
287
|
+
calculateAspectRatio(frameW, frameH, aspectFactor);
|
|
288
|
+
const framesH = (metaInfo.w - 1) / frameW;
|
|
289
|
+
const framesV = (metaInfo.h - 1) / frameH;
|
|
290
|
+
const {
|
|
291
|
+
frame: {
|
|
292
|
+
x: frameX,
|
|
293
|
+
y: frameY
|
|
294
|
+
},
|
|
295
|
+
sourceSize: {
|
|
296
|
+
w: originalSizeX,
|
|
297
|
+
h: originalSizeY
|
|
298
|
+
}
|
|
299
|
+
} = spriteFrames[currentFrame.current];
|
|
300
|
+
const frameOffsetX = 1 / framesH;
|
|
301
|
+
const frameOffsetY = 1 / framesV;
|
|
302
|
+
finalValX = flipOffset > 0 ? frameOffsetX * (frameX / originalSizeX) : frameOffsetX * (frameX / originalSizeX) - matRef.current.map.repeat.x;
|
|
303
|
+
finalValY = Math.abs(1 - frameOffsetY) - frameOffsetY * (frameY / originalSizeY);
|
|
304
|
+
matRef.current.map.offset.x = finalValX;
|
|
305
|
+
matRef.current.map.offset.y = finalValY;
|
|
306
|
+
currentFrame.current += 1;
|
|
307
|
+
}; // *** Warning! It runs on every frame! ***
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
useFrame((state, delta) => {
|
|
311
|
+
var _spriteData$current, _matRef$current;
|
|
312
|
+
|
|
313
|
+
if (!((_spriteData$current = spriteData.current) != null && _spriteData$current.frames) || !((_matRef$current = matRef.current) != null && _matRef$current.map)) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (pause) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (autoPlay || play) {
|
|
322
|
+
runAnimation();
|
|
323
|
+
onFrame && onFrame({
|
|
324
|
+
currentFrameName: currentFrameName.current,
|
|
325
|
+
currentFrame: currentFrame.current
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}); // utils
|
|
329
|
+
|
|
330
|
+
const getFirstItem = param => {
|
|
331
|
+
if (Array.isArray(param)) {
|
|
332
|
+
return param[0];
|
|
333
|
+
} else if (typeof param === 'object' && param !== null) {
|
|
334
|
+
const keys = Object.keys(param);
|
|
335
|
+
return param[keys[0]][0];
|
|
336
|
+
} else {
|
|
337
|
+
return {
|
|
338
|
+
w: 0,
|
|
339
|
+
h: 0
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
return /*#__PURE__*/React.createElement("group", props, /*#__PURE__*/React.createElement(React.Suspense, {
|
|
345
|
+
fallback: null
|
|
346
|
+
}, /*#__PURE__*/React.createElement("sprite", {
|
|
347
|
+
ref: spriteRef,
|
|
348
|
+
scale: aspect
|
|
349
|
+
}, /*#__PURE__*/React.createElement("spriteMaterial", {
|
|
350
|
+
ref: matRef,
|
|
351
|
+
map: spriteTexture,
|
|
352
|
+
premultipliedAlpha: false,
|
|
353
|
+
transparent: true,
|
|
354
|
+
alphaTest: alphaTest !== null && alphaTest !== void 0 ? alphaTest : 0.0
|
|
355
|
+
}))), children);
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
export { SpriteAnimator };
|