Framer Motion - Making a div randomly move across the page - reactjs

I am new to using Framer Motion but I do love the library a lot. Fantastic so far. However, I am stuck right now when trying to move a background div across the whole page.
https://codesandbox.io/s/svg-background-60ht8?file=/src/App.js
This is my codesandbox
I tried everything, read all the docs and I did get one tip to use Motionvalue and calculate the boundaries so the ball doesn't go off-screen. But I can't figure it out yet...
Does anyone know how to make the SVG/ball/div infinitely animate across the whole page? Thanks in advance!
EDIT:
It is moving randomly now, but on local machine the animation breaks and repeats when going outside the screen.

Not entirely sure if this kind of effect was what you were looking for, but I tried to add the random movement plus screen constraints in an example more akin to the DVD bouncing logo. However, I guess the motionValue should be used in all instances, since using state incurs rendering costs as well as a loss of precision.
I was not able to do the same demo using motion values, so that might be something worth exploring further.

I did something similar using MathRandom, with two state variables that represented both x and y positions on the screen, along with a setInterval() so it would change after a couple of seconds. Something like:
const Test = () => {
const { height, width } = getDimensions();
const [Position, setPosition] = useState({ x: 150, y: 150 })
useEffect(() => {
let timer = setInterval(() => {
setPosition({
...Position,
x: width * (1 - Math.random()) - 250,
y: height * (1 - Math.random()) - 250,
}, 1500)
return () => {
clearInterval(timer)
}
}, [Position])
return (
<div className="screen">
<div className='Animate' style={{
top: Position.y,
left: Position.x,
borderRadius: 100,
height: 100,
width: 100,
...
}}
/>
</div>
)
}

Related

Re-rendering a component with useSpring resets animation chain and over time makes other animations staggery

I have this spring defining a looped animation:
const style = useSpring({
from: { marginBottom: 0 },
to: [
{ marginBottom: 0 },
{ marginBottom: 80, config: { duration: 600 }, delay: 3000 },
{ marginBottom: -80, config: { duration: 0 } },
{ marginBottom: 0, config: { duration: 600, delay: 1000 } },
],
loop: true,
})
My problem is that whenever I re-render the component containing the spring, the animation 'resets' and starts moving towards the first to position (in this case { martinBottom: 0 })
It can be seen in this sandbox - click the 'reset' button just as the arrow is traveling up and it will reverse course instantly
The documentation also hints that stabilizing the references of from and to may solve the issue... and it kind of does, but it introduces a new one: if the components re-renders shortly before the animation starts, it skips the 'travel up' portion: sandbox here.
The bigger problem is that the more times I 'interrupt' the animation cycle by re-rendering, the more it affects some of my other, more demanding animations, making them more and more staggered.
Refreshing whole app instantly resets this decay and makes all animations fluid again. When I remove this useSpring from my code, this decay disappears
So what is going on there? Is there something I'm missing?
From this docs example, It makes sense to me that the animation resets on re-render ... but then why does this example instruct me to compose staged animations this way, am I missing some key step?
And what's up with this slow 'decay' of other animations' performance, is that a known cause to that?

React-Viro AR ImageTracking sub elements are positioned inside camera when target is seen

I am currently using Viro-React (React-Viro) for a AR project in which, if a certain pictures gets seen by the camera a video is played infront of it. I had it working perfectly, but somehow and a few days later, without changing the code, the video and everything inside the ViroARImageMarker is always positioned inside the camera, when the target gets seen.
This behavior only seems to happen in my own projects and not in the samples provided by Viro Media.
I have tried to:
Reinstalling node modules
Compared the package.json's and reinstalled.
Changed the position of the elements inside the ViroARImageMarker
And reorganised the elements.
But nothing seems to work.
As I said the code itself shows and hides the video, but does not position the video (every inside the ViroARImageMarker correctly, but positions them in the position of the camera when the targets gets seen and then keeps them there.
Here is the code. (Snippet at the end)
I pass this function to the ViroARSceneNavigator in another script.
There are a few animations, which just scale up/down the video depending if the target is in view or not.
(I removed the whitespaces to fit more onto one screen)
Main Code
Viro Animations and Material
"use strict";
import React, { useState } from "react";
import { ViroARScene, ViroNode, ViroARImageMarker, ViroVideo, ViroARTrackingTargets, ViroAnimations, ViroMaterials } from "react-viro";
const MainScene = (props) => {
const videoPath = require("./res/Test_Video.mp4");
const [playVideoAnimation, setPlayVideoAnimation] = useState(false);
const [videoAnimationName, setVideoAnimationString] = useState("showVideo");
const [shouldPlayVideo, setShouldPlayVideo] = useState(false);
function onAnchorFound() {
setPlayVideoAnimation(true);
setVideoAnimationString("showVideo");
setShouldPlayVideo(true);
}
function onAnchorRemoved() {
setShouldPlayVideo(false);
setVideoAnimationString("closeVideo");
setPlayVideoAnimation(true);
}
function onVideoAnimationFinish() {
setPlayVideoAnimation(false);
}
function onVideoFinish() {
setShouldPlayVideo(false);
setVideoAnimationString("closeVideo");
setPlayVideoAnimation(true);
}
return (
<ViroARScene>
<ViroARImageMarker target={"targetOne"} onAnchorFound={onAnchorFound} onAnchorRemoved={onAnchorRemoved}>
<ViroNode rotation={[-90, 0, 0]}>
<ViroVideo
position={[0, 0, 0]}
scale={[0, 0, 0]}
dragType="FixedToWorld"
animation={{ name: videoAnimationName, run: playVideoAnimation, onFinish: onVideoAnimationFinish }}
source={videoPath}
materials={["chromaKeyFilteredVideo"]}
height={0.2 * (9 / 16)}
width={0.2}
paused={!shouldPlayVideo}
onFinish={onVideoFinish}
/>
</ViroNode>
</ViroARImageMarker>
</ViroARScene>
);
};
ViroAnimations.registerAnimations({
showVideo: {
properties: { scaleX: 0.9, scaleY: 0.9, scaleZ: 0.9 },
duration: 1,
easing: "bounce",
},
closeVideo: {
properties: { scaleX: 0, scaleY: 0, scaleZ: 0 },
duration: 1,
},
});
ViroMaterials.createMaterials({
chromaKeyFilteredVideo: {
chromaKeyFilteringColor: "#00FF00",
},
});
ViroARTrackingTargets.createTargets({
targetOne: {
source: require("./res/Test_Bild.png"),
orientation: "Up",
physicalWidth: 0.01, // real world width in meters
},
});
export default MainScene;
I was able to resolve the issue by copying (downgrading) my dependencies in my package.json from the React-Viro codesamples and decreasing the width/height (inside the element) and scale (in the animation) of the video.
Note that if the sub element of the ViroARImageMarker is too big (in scale and size), the issue comes back.

Duplicate image shown - useEffect issue?

I have some code to display my board as shown below:
<Grid item xs={6} justify="center">
<Paper id="board">
</Paper>
</Grid>
But sometimes when I save my code, it displays a duplicate board on the screen on localhost:3000, although not always. Example below:
I am suspecting it it might have something to do with my useEffect:
useEffect(() => {
// componentDidMount() {
const board = new Gameboard(document.getElementById('board'), {
// position: 'r4r1k/p1P1qppp/1p6/8/5B2/2Q1P3/P4PPP/R3KB1R b KQ - 0 21',
position: game.fen(),
sprite: {
url: './gameboard-sprite.svg', // pieces and markers are stored as svg in the sprite
grid: 40, // the sprite is tiled with one piece every 40px
cache: true,
},
orientation: COLOR.white,
moveInputMode: MOVE_INPUT_MODE.dragPiece,
responsive: true,
});
board.enableMoveInput(inputHandler);
}, []);
Any idea why it could be displaying twice, but only sometimes? It always removes the duplicate board when I click the browser refresh button if that helps. But it is often there when I first save my code.
Save your Gameboard instance in useRef, this way only one instance is created:
const board = useRef();
useEffect(() => {
if(!board.current) {
board.current = new Gameboard(document.getElementById('board'), {
// position: 'r4r1k/p1P1qppp/1p6/8/5B2/2Q1P3/P4PPP/R3KB1R b KQ - 0 21',
position: game.fen(),
sprite: {
url: './gameboard-sprite.svg', // pieces and markers are stored as svg in the sprite
grid: 40, // the sprite is tiled with one piece every 40px
cache: true,
},
orientation: COLOR.white,
moveInputMode: MOVE_INPUT_MODE.dragPiece,
responsive: true,
});
board.current.enableMoveInput(inputHandler);
}
}, []);
You need to return cleanup function from effect, so when fast refresh will rerun it, there will be no duplicates. It works properly sometimes because sometimes fast refresh sometimes don't try to preserve state and renders everything as new. This depends on changes you made, so when you change one file it will render clean, but on another files it will try to preserve state and only rerun effects (this is where second board added).
Also, it is better not to interact with DOM directly and use ref instead. This is better way to work with DOM in react, since it's react main purpose to update DOM.

How to make a div shake from side to side with react spring

I have started playing around with react-spring and I am loving the concept but I am struggling to work out how to build the animation that I want.
I would like to make a div move to the right, then back to start, then to the right, but not as far, back to start, to the right, but not as far again and finally back to start. Just like a spring, that is pulled and when released goes boingoingoing back to it's resting place.
I can see from the documentation how to adjust the feel of the spring and how to trigger the animation, but I have never made an animation before so knowing which properties to change and how to make it loop properly are what I am looking for help on.
Edit: I have this animation so far, and it works, but it feels very disjointed.
const shake = useSpring({
from: { "margin-left": 0 },
to: [
{ "margin-left": 30 },
{ "margin-left": 0 },
{ "margin-left": 20 },
{ "margin-left": 0 },
{ "margin-left": 10 },
{ "margin-left": 0 }
],
config: {
mass: 1,
tension: 500,
friction: 10
}
});
Currently it is clearly three movements, can I decrease the delay between the movements so that it looks like one movement?
Is margin left the best CSS property to use?
UPDATE START
I just came across a simple solution for this problem. It I way better than the one I tried to achieve with configuration. It uses interpolation with range. It is using transform now but can easily adopted to margin.
export default function App() {
const { x } = useSpring({
from: { x: 0 },
to: { x: 1 }
});
return (
<div className="App">
<animated.div
style={{
transform: x
.interpolate({
range: [0, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 1],
output: [180, 220, 180, 220, 180, 220, 180, 200]
})
.interpolate(x => `translate3d(${x}px, 0px, 0px)`)
}}
>
Shake
</animated.div>
</div>
);
}
https://codesandbox.io/s/awesome-framework-go5oh?file=/src/App.js:103-609
UPDATE END*
You can achieve the bouncy effect. There are 3 variable controlling the spring based animation:
mass
friction
tension
You can play with the pink square here: https://www.react-spring.io/docs/hooks/api
I recommend low mass and friction and high tension. You can set these variable in every animation type. For example:
useSpring({ ..., config: {mass: 1, tension: 500, friction: 10} });

C3.js responsive x axis timeseries

I'm trying to create a graph that can look good on both mobile and desktop using c3 (http://c3js.org/).
However, I'm having trouble getting the x-axis, which is a timeseries, to display change size for mobile. The only way I've managed to do this is by destroying the graph and re-creating it from scratch. But this prevents me from adding a nice transition to the chart, which would be really nice to have.
[![desktop view][1]][1]
I tried using the tick culling option on the x axis and set a max value of 8, but this option gets ignored when using either flush() or resize() methods.
So my question is: is there any way to change the x axis values without having to destroy the graph and re-generate it?
http://imgur.com/EMECqqB
I have used the onresized: function () {...}
or you can use the onresize: function () { ... }
link
to change the culling max on resized screen:
axis: {
x: {
type: 'timeseries',
tick: {
culling: true,
format: '%m-%d',
fit:true,
culling: {
max: window.innerWidth > 500 ? 8 : 5
}
}
}
},
onresized: function () {
window.innerWidth > 500 ? chart.internal.config.axis_x_tick_culling_max = 8 : chart.internal.config.axis_x_tick_culling_max = 5;
},
Here a example: https://jsfiddle.net/07s4zx6c/2/
add mediaquery according to device size
like this.
#media screen and (max-width:810px) {
.c3 svg { font: .8em sans-serif; }
}

Resources