At first, my animation seems to work fine. However, after a few seconds, the animations become very jerky and laggy, I'm not sure why.
At first I thought it was due to the animation button I had which allows the user to start and stop the animation at will. However, even after I commented out the button, the animation continued to be laggy.
let camera, scene, renderer;
const loader = new GLTFLoader();
let mixer = null;
let controls;
const clock = new THREE.Clock();
let previousTime = 0;
//start and stop button
let runAnim = false;
let isPlay = true;
//animation
function animation() {
if (!isPlay) return;
const elapsedTime = clock.getElapsedTime();
const deltaTime = elapsedTime - previousTime;
previousTime = elapsedTime;
//Update mixer
if (mixer !== null) {
mixer.update(deltaTime);
}
// Update controls
controls.update();
window.requestAnimationFrame(animation);
render();
}
function render() {
renderer.render(scene, camera);
}
module.exports = function getImage() {
const mountRef = useRef(null);
useEffect(() => {
//Model
loader.load(`/gltf/1.gltf`);
mixer = new THREE.AnimationMixer(gltf.scene);
const action = mixer.clipAction(gltf.animations[0]);
action.play();
animation();
//Camera
camera = new THREE.PerspectiveCamera(
70,
window.innerWidth / window.innerHeight,
0.1,
100
);
camera.position.set(4, 0, 5);
scene = new THREE.Scene();
// Controls
controls = new OrbitControls(camera, renderer.domElement);
controls.update();
controls.enableDamping = true;
// Animation button
const animateButton = document.getElementById('animate-button');
const stopAnimation = (e) => {
if (runAnim) {
runAnim = false;
isPlay = true;
animation();
console.log('animation starts');
} else {
runAnim = true;
isPlay = false;
console.log('animation stops');
}
};
animateButton.addEventListener('click', stopAnimation);
return () => mountRef.removeChild(renderer.domElement);
}, []);
return (
<div>
<div ref={mountRef}>
<AnimationButton />
</div>
</div>
);
};
Related
the problem with the script I have is it starts out playing and then pauses when button is pressed I need the opposite. Plus I need the sound to be in sync with the length of the images in the array and both the image sequence and the sound file should play in a loop and start out as paused and play when the button is pressed.
HTML
<div>
<button id="bt1" onclick="togglePlay();toggle()"></button>
</div>
<script>
var myAudio = document.getElementById("myAudio");
var isPlaying = false;
let
loop,
index = 0,
images = [
"cat/1.svg", "cat/2.svg", "cat/3.svg", "cat/4.svg", "cat/5.svg", "cat/6.svg", "cat/7.svg", "cat/8.svg",
"cat/9.svg", "cat/10.svg", "cat/11.svg", "cat/12.svg", "cat/13.svg", "cat/14.svg", "cat/15.svg", "cat/16.svg",
"cat/17.svg", "cat/18.svg"
];
function startLoop() {
loop = setInterval(() => {
document.querySelector('img').src = images[index];
index++;
// Loop images array
if (index % images.length === 0) {
index = 0;
}
}, 100);
}
function toggle() {
if (loop) {
clearInterval(loop);
loop = null;
} else {
startLoop();
}
}
startLoop();
function togglePlay() {
isPlaying ? myAudio.pause() : myAudio.play();
};
myAudio.onplaying = function() {
isPlaying = true;
};
myAudio.onpause = function() {
isPlaying = false;
};
</script>
I'm trying to make my tabbar dissappear while scrolling down with animation. onScroll sends boolean value if last y coord is bigger than current y coord its scrolling up and otherwise scrolling down. If I continue to scroll down onScroll still sends true value to my function and animaton works more than once. How can i disable position so only toValue is gonna work and my function will not trigger again and again while returning boolean value is same from onScroll.
function runTiming(value, dest) {
const clock = new Clock();
const state = {
finished: new Value(0),
position: new Value(0),
time: new Value(0),
frameTime: new Value(0),
};
const config = {
duration: 200,
toValue: new Value(0),
easing: Easing.inOut(Easing.cubic),
};
return block([
cond(clockRunning(clock), 0, [
set(state.finished, 0),
set(state.time, 0),
set(state.position, value),
set(state.frameTime, 0),
set(config.toValue, dest),
startClock(clock),
]),
timing(clock, state, config),
cond(state.finished, debug('stop clock', stopClock(clock))),
state.position,
]);
}
This might be due to the onScroll event being fired more than once.
I just coded this yesterday and will give you the code that is working and has been tested:
export const throttle = (func, limit) => {
let inThrottle
return function() {
const args = arguments
const context = this
if (!inThrottle) {
func.apply(context, args)
inThrottle = true
setTimeout(() => inThrottle = false, limit)
}
}
}
class MainComponent extends PureComponent {
constructor(props) {
super(props);
this.offset = 0;
this.isAnimatingMenu = false;
this.onScroll = this.onScroll.bind(this)
};
onScroll = throttle(event => {
const currentOffset = event.nativeEvent.contentOffset.y;
const direction = currentOffset > this.offset ? 'down' : 'up';
const diff = currentOffset - this.offset;
if (direction === 'down' && diff > 9 && !this.isAnimatingMenu) {
this.isAnimatingMenu = true
this.onHideMenu(() => this.isAnimatingMenu = false)
}
if (direction === 'up' && diff < -9 && !this.isAnimatingMenu) {
this.isAnimatingMenu = true
this.onShowMenu(() => this.isAnimatingMenu = false)
}
this.offset = currentOffset;
}, 75)
render() {
<FlatList
windowSize={1}
bounces={false}
style={styles.flex1}
scrollEventThrottle={75}
onScroll={this.onScroll}
renderItem={this.renderItem}
data={this.deriveHomeWidgetsDataFromProps()}
/>
}
}
In the functions onHideMenu and onShowMenu call your animation function to show/hide the menu. onScroll can be implemented on ScrollView or SectionList aswell. If you need more help let me know.
I am trying to implement orbitControls in my react application. I have loaded 3d model GraphicsView
of expo-graphics. Model loaded perfectly, now i need to rotate the 3D model with screen drag. For
this i have added orbitControls, which is not working properly. Model did'nt rotate with screen
drag.Please help what i need to do to do to rotate 3D model in my react app.
Here is my model class.
```
import React from 'react';
import ExpoTHREE, { THREE } from 'expo-three';
import { GraphicsView } from 'expo-graphics';
import OrbitControls from 'three-orbitcontrols';
class Model extends React.Component {
componentDidMount() {
THREE.suppressExpoWarnings();
}
// When our context is built we can start coding 3D things.
onContextCreate = async ({ gl, pixelRatio, width, height }) => {
// Create a 3D renderer
this.renderer = new ExpoTHREE.Renderer({
gl,
pixelRatio,
width,
height,
});
// We will add all of our meshes to this scene.
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0x3d392f);
//this.scene.fog = new THREE.Fog( 0xa0a0a0, 10, 50 );
this.camera = new THREE.PerspectiveCamera(12, width/height, 1, 1000);
this.camera.position.set(3, 3, 3);
this.camera.lookAt(0, 0, 0);
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.controls.rotateSpeed = 2.0;
this.controls.zoomSpeed = 1.2;
this.controls.panSpeed = 0.8;
this.controls.noPan = false;
this.controls.staticMoving = false;
this.controls.dynamicDampingFactor = 0.3;
this.controls.keys = [ 65, 83, 68 ];
this.controls.addEventListener( 'change', this.onRender );
//clock = new THREE.Clock();
//this.scene.add(new THREE.AmbientLight(0xffffff));
var hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444 );
hemiLight.position.set( 0, 20, 0 );
this.scene.add( hemiLight );
var dirLight = new THREE.DirectionalLight( 0xffffff );
dirLight.position.set( - 3, 10, - 10 );
dirLight.castShadow = true;
dirLight.shadow.camera.top = 2;
dirLight.shadow.camera.bottom = - 2;
dirLight.shadow.camera.left = - 2;
dirLight.shadow.camera.right = 2;
dirLight.shadow.camera.near = 0.1;
dirLight.shadow.camera.far = 40;
this.scene.add( dirLight );
await this.loadModel();
};
loadModel = async () => {
const obj = {
"f.obj": require('../assets/models/f.obj')
}
const model = await ExpoTHREE.loadAsync(
obj['f.obj'],
null,
obj
);
// this ensures the model will be small enough to be viewed properly
ExpoTHREE.utils.scaleLongestSideToSize(model, 1);
this.scene.add(model)
};
// When the phone rotates, or the view changes size, this method will be called.
onResize = ({ x, y, scale, width, height }) => {
// Let's stop the function if we haven't setup our scene yet
if (!this.renderer) {
return;
}
this.camera.aspect = width / height;
this.camera.updateProjectionMatrix();
this.renderer.setPixelRatio(scale);
this.renderer.setSize(width, height);
};
// Called every frame.
onRender = delta => {
// Finally render the scene with the Camera
this.renderer.render(this.scene, this.camera);
};
render() {
return (
<GraphicsView
onContextCreate={this.onContextCreate}
onRender={this.onRender}
onResize={this.onResize}
/>
);
}
}
export default Model;
```
I'm trying another solution to my problem:
The thing is: im rendering a Sketch component with a background image and sketching over it
onReady = async () => {
const { layoutWidth, layoutHeight, points } = this.state;
this.sketch.graphics = new PIXI.Graphics();
const linesStored = this.props.screenProps.getSketchLines();
if (this.sketch.stage) {
if (layoutWidth && layoutHeight) {
const background = await PIXI.Sprite.fromExpoAsync(this.props.image);
background.width = layoutWidth * scaleR;
background.height = layoutHeight * scaleR;
this.sketch.stage.addChild(background);
this.sketch.renderer._update();
}
if (linesStored) {
for(let i = 0; i < linesStored.length; i++) {
this.sketch.stage.addChild(linesStored[i])
this.sketch.renderer._update();
}
}
}
};
this lineStored variable is returning a data i've saved onChange:
onChangeAsync = async (param) => {
const { uri } = await this.sketch.takeSnapshotAsync();
this.setState({
image: { uri },
showSketch: false,
});
if (this.sketch.stage.children.length > 0) {
this.props.screenProps.storeSketchOnState(this.sketch.stage.children);
}
this.props.callBackOnChange({
image: uri,
changeAction: this.state.onChangeAction,
startSketch: this.startSketch,
undoSketch: this.undoSketch,
});
};
storeSketchOnState saves this.sketch.stage.children; so when i change screen and back to the screen my Sketch component in being rendered, i can retrieve the sketch.stage.children from App.js state and apply to Sketch component to persist the sketching i was doing before
i'm trying to apply the retrieved data like this
if (linesStored) {
for(let i = 0; i < linesStored.length; i++) {
this.sketch.stage.addChild(linesStored[i])
this.sketch.renderer._update();
}
}
```
but it is not working =(
I am trying to implement a drag and drop solution in react-native and I want to make my flatlist scroll if I drag an item in the upper 10 percent and bottom 10 percent of the view. So far the only way i can get it to happen is by calling a function that recursively dispatches a this._flatList.scrollToOffset({ offset, animated: false });. The recursive call stops when a certain condition is met but the scrolling effect is choppy. Any advice on making that smoother?
// on move pan responder
onPanResponderMove: Animated.event([null, { [props.horizontal ? 'moveX' : 'moveY']: this._moveAnim }], {
listener: (evt, gestureState) => {
const { moveX, moveY } = gestureState
const { horizontal } = this.props
this._move = horizontal ? moveX : moveY;
const { pageY } = evt.nativeEvent;
const tappedPixel = pageY;
const topIndex = Math.floor(((tappedPixel - this._distanceFromTop + this._scrollOffset) - this._containerOffset) / 85);
const bottomIndex = Math.floor(((tappedPixel + (85 - this._distanceFromTop) + this._scrollOffset) - this._containerOffset) / 85);
this.setTopAndBottom(topIndex, bottomIndex);
this.scrolling = true;
this.scrollRec();
}
}),
// recursive scroll function
scrollRec = () => {
const { activeRow } = this.state;
const { scrollPercent, data, } = this.props;
const scrollRatio = scrollPercent / 100;
const isLastItem = activeRow === data.length - 1;
const fingerPosition = Math.max(0, this._move - this._containerOffset);
const shouldScrollUp = fingerPosition < (this._containerSize * scrollRatio); // finger is in first 10 percent
const shouldScrollDown = !isLastItem && fingerPosition > (this._containerSize * (1 - scrollRatio)) // finger is in last 10
const nextSpacerIndex = this.getSpacerIndex(this._move, activeRow);
if (nextSpacerIndex >= this.props.data.length) { this.scrolling = false; return this._flatList.scrollToEnd(); }
if (nextSpacerIndex === -1) { this.scrolling = false; return; }
if (shouldScrollUp) this.scroll(-20);
else if (shouldScrollDown) this.scroll(20);
else { this.scrolling = false; return; };
setTimeout(this.scrollRec, 50);
}
/
Yeah, pass an options object to your Animated.event with your listener with useNativeDriver set to true
Animated.event([
null,
{ [props.horizontal ? "moveX" : "moveY"]: this._moveAnim },
{
listener: ...,
useNativeDriver: true
}
]);
Should make things significantly smoother.
edit: I feel I should add that there is probably a saner way to accomplish this, are you only using a FlatList because of it's "infinite" dimensions?