As seen below, I tried to create a bounce effect at the end of another animation, in Popmotion.
I was not sure how to go about it, so I tried to reverse the velocity once it hit a certain threshold.
The results are sporadic and does not always work.
Any ideas on how to best create a bounce effect with Popmotion?
Clarification 1
The ball bounces most of the times, but how long it bounces varies greatly. Sometimes it stops abruptly after just one bounce. I am not sure why that is, because I do not fully understand how the solution actually works. Why does it slow down, if we simply reverse the velocity. Looking at the code, my guess would have been that the ball would oscillate indefinitely, but it does not.
Clarification 2
In Firefox 65.0.1, the animation seems consistent. In Chrome 72.x, it acts irrationally. I.e. the animation and bounce length changes each time.
const {
tween,
styler,
value,
easing,
physics,
transform
} = popmotion;
const {
clamp,
pipe,
conditional
} = transform;
const ball = document.querySelector('#ball');
const ballStyler = styler(ball);
const ballY = value(0, ballStyler.set('y'));
const BOTTOM = 50;
const pipedPhysics = physics({
acceleration: 2000,
// friction: 0.5,
// restSpeed: 0,
// springStrength: 300,
// to: 50
}).pipe(clamp(0, BOTTOM));
const anim = pipedPhysics.start(ballY);
ballY.subscribe(v => {
if (v >= BOTTOM) {
anim.setVelocity(-ballY.getVelocity());
};
// console.log('v, vel: ', v, ballY.getVelocity());
});
#ball {
background: #ff2420;
border-radius: 50%;
position: absolute;
left: 50%;
width: 50px;
height: 50px;
margin: 0px;
transform-origin: 50%;
}
<script src="https://unpkg.com/popmotion/dist/popmotion.global.min.js"></script>
<div id="ball"></div>
Related
I am trying to do an animation example but there is a problem about accumulation. At first click on the button, the translate animation is done and the position of the element changes permanently. However, on second click it does the translate animation again but this time does not keep the last position. How to overcome this? I went through MDN document and applied the optional section but failed to complete this challenge. Regards,
https://jsfiddle.net/ja218pbr/19/
document.getElementsByTagName("button")[0].addEventListener("click", function() {
document.querySelectorAll("div")[0].animate([
{transform: 'translate(100%, 0)'}
], {
duration: 250,
composite: "accumulate",
iterationComposite: "accumulate",
fill: "forwards"
});
});
If I'm understanding this correctly, you want to be able to let the object slide again from the position it ended in earlier. To do this we can get the boundingClientRect each time the button is clicked and calculate the new translation distance by basically taking the width of the object and adding the left distance of the client rect, this will effectively allow the rectangle to keep moving from the distance it ended in before. Also I removed the composite property because it caused the rectangle to jump over the correct position.
document.getElementsByTagName("button")[0].addEventListener("click", function() {
const clientRect = document.querySelectorAll("div")[0].getBoundingClientRect();
const translationX = clientRect.width + clientRect.left;
document.querySelectorAll("div")[0].animate([{
transform: `translate(${translationX}px, 0)`
}], {
duration: 250,
iterationComposite: "accumulate",
fill: "forwards"
});
});
body {
margin: 0;
}
div {
position: relative;
height: 150px;
width: 150px;
background-color: yellow;
text-align: center;
line-height: 150px;
}
<div>Moving Object</div>
<button>Press</button>
I know that CSS transitions do not work with auto keyword, so if I want to apply transition: left 2s, right 2s; have to vary both right and left from (let's say) 0 to 100.
I have a script which might change an object's position either towards left or right, but I cannot set right: 100 if left is set to 0 (it has to be auto).
So I came out with this solution.
function moveObject(direction) {
direction ? myobject.style.right = 0 : myobject.style.left = 0; //same position as auto but can be transitioned
direction ? myobject.style.right = "50vw" : myobject.style.left = "50vw"; //actual transition
}
function resetObject() {
myObject.style.left = myObject.style.right = "auto"; //now I can move to either right or left again
//I don't need an animation on reset
}
#myobject {
position: absolute;
left: auto;
right: auto;
/*width, height...*/
}
<div id="myObject"></div>
This didn't work (the assign to 0 was completely ignored).
So I came out with this solution instead (same as above but using setTimeout(..., 0);
function moveObject(direction) {
direction ? myobject.style.right = 0 : myobject.style.left = 0;
setTimeout(function() {
direction ? myobject.style.right = "50vw" : myobject.style.left = "50vw"; //this is executed async
}, 0);
}
function resetObject() {
myObject.style.left = myObject.style.right = "auto";
}
#myobject {
position: absolute;
left: auto;
right: auto;
/*width, height...*/
}
<div id="myObject"></div>
This improved the result, but sometimes (it seems to be completely random, but more frequently on small screens), the transition still does not occur.
I supposed that this was because of the fact that sometimes the asynchronous function is executed too soon, so I tried to increase the delay to 10ms, but the problem still occurs sometimes (less frequently).
Now, I could increase the value furthermore, but:
I can never be sure that the error will not occur anyway sooner or later, maybe on a faster device (unless I set the timeout to a very large number)
I cannot set the timeout to a very large number, since I want it to be imperceptible
So, is there a minimum number that can assure a successful output?
If not, how can accomplish the same result?
As #Akxe suggested, the solution was reflowing the page. In other words, browsers flush multiple changes of the DOM all together. This speeds up the page by a bit but leads to some problems like the one I described (further information here)
As suggested here, the solution was a function like this one:
function reflow() { //this will be called between the two elements
void(document.documentElement.offsetWidth);
}
function moveObject(direction) {
direction ? myobject.style.right = 0 : myobject.style.left = 0;
reflow(); //the line above is now flushed
direction ? myobject.style.right = "50vw" : myobject.style.left = "50vw";
}
function resetObject() {
myObject.style.left = myObject.style.right = "auto";
}
#myobject {
position: absolute;
left: auto;
right: auto;
/*width, height...*/
}
<div id="myObject"></div>
The function actually worked for me even without using void, but I preferred keeping it since in the referenced question someone pointed out that it didn't work for him, and also for more clarity.
This could also be useful: it is an (unofficial) list of everything that causes page reflow.
Problem:
So, made a digital story with illustrations. I pieced it all together by using simple waapi css animations, that gives a parallax effect. Problem with my code is that all my content/images is placed on a timeline in relation to a offset value between 0 and 1. This makes the whole story blast trough in one fast scroll motion on the trackpad/scrollwheel :(
Tried various css and js methods to smooth and slow down the scroll without luck.
How can I best write this so the scroll is slowed down?
Demo (Best in 1440px900px window): https://andyradall.github.io/andylax/
Source code git: https://github.com/Andyradall/andylax
Source code in Jsfiddle: https://jsfiddle.net/Andyradall/yLvboa6t/8/
Example of javascript code for one of the images:
// Settings for animations length
const animasjonSettings = {
duration: 12000,
fill: "both"
}
// Settings for one of the images
const tekstboks3 = document.querySelector("#tekstboks3");
const tekstboks3Keyframes = [
{top: "2.5rem", left: "-22rem", opacity: 1.0},
{top: "2.5rem", left: "-22rem", opacity: 1.0, offset: 0.41},
{top: "2.5rem", left: "3rem", opacity: 1.0, offset: 0.46},
{top: "2.5rem", left: "3rem", opacity: .01, offset: 0.7},
{top: "2.5rem", left: "3rem", opacity: .01,}
];
const tekstboks3Animasjon = tekstboks3.animate(tekstboks3Keyframes, animasjonSettings);
// Start all animations in array on scroll
function animerAlle() {
const y = scrollY;
for(const animasjon of animasjoner) {
animasjon.currentTime = y * 3;
}
}
document.addEventListener("scroll", animerAlle);
I'm not 100% sure since i don't have a tactile device, but I could make the scroll longer (and thus, slower) only by changing these two properties of your code (multiplying by 2 but you can try other operations...)
/* index.css */
body {
height: 1530vh; /* 765 * 2 = 1530 */
background: #7DD3E2;
}
// index.js
// Innstilling for animasjonslengde
const animasjonSettings = {
duration: 24000, // 12000 * 2 = 24000
fill: "both"
}
don't know if it's what you want...
Besides, really good job your site is amazing!
[edit]
for the smooth part, you can look into scroll-snap property but I fear it would make more harm than good
I'm making a very basic space invaders game, but having some trouble with the collisionDetection. enemy are the ones I need to hit, they are inside "fiende". "missile" is the div for the missiles.
I haven't tried much, since I'm very new to js, hence I don't have enough knowledge to improve on it.
EDIT: After some testing, I've found out that the collisionDetection breaks when the animations is running.
function collisionDetection() {
for (var enemy = 0; enemy < fiende.length; enemy++) {
for (var missile = 0; missile < missiles.length; missile++) {
if (
missiles[missile].left >= fiende[enemy].left &&
missiles[missile].left <= (fiende[enemy].left + 50) &&
missiles[missile].top <= (fiende[enemy].top + 50) &&
missiles[missile].top >= fiende[enemy].top
) {
fiende.splice(enemy, 1);
missiles.splice(missile, 1);
}
}
}
}
HTML
<div id="background">
<div id="missiles"></div>
<div id="fiende"></div>
</div>
CSS
div.missile1 {
width: 10px;
height: 28px;
background-image: url('missile1.png');
position: absolute;
}
div.enemy {
width: 50px;
height: 50px;
background-image: url('enemy.png');
position: absolute;
}
Some of the animation, it's basically the same after that.
#keyframes bevegelse {
0% {
left: -230px;
top: 0%;
}
5% {
left: 250px;
top: 0%;
}
10% {
left: 250px;
top: 40px;
}
15% {
left: -230px;
top: 40px;
}
When a missile hits the enemy, it destroys some other enemies, which the missile didn't hit. Sometimes it doesn't even destroy the randoms ones.
You have a problem in your loop. You are stepping through the indices into the array, but also modifying the array as you go, so you may get confusing results. This may or may not be the cause of your other issues (clearly this is not all of your code) however fixing this may help.
There are a number of ways to fix this. My usual method is to count backwards through the array so that removing elements does not impact the locations of indices the loop is yet to visit.
function collisionDetection() {
//because this loop is continued once the array is modified, count from the end
for (var enemy = fiende.length-1; enemy >=0 ; enemy--) {
//this loop exits when a clash is found, so count as normal
for (var missile = 0; missile < missiles.length; missile++) {
if (
missiles[missile].left >= fiende[enemy].left &&
missiles[missile].left <= (fiende[enemy].left + 50) &&
missiles[missile].top <= (fiende[enemy].top + 50) &&
missiles[missile].top >= fiende[enemy].top
) {
fiende.splice(enemy, 1);
missiles.splice(missile, 1);
//the enemy has already been hit, exit and dont consider other missiles
break;
}
}
}
}
I've made the assumption here that it is one missile gone per enemy. If you want more than one missile to be destroyed, then remove the break and also count backwards in the inner loop.
Thank you for supplying a reference to your git code (see the comments below).
The above is an error in the code, however not one which is typically causing any problem. The issue seems to be that when the enemies are animated the bullets don't hit them. The problem here is that you are detecting collisions with your js code's view of the enemy location, but you are enhancing the animation with css animation. If you wish to detect the collisions then you need to animate entirely in JS.
Try removing the css animation to verify you can hit the enemies. Then if that works you need to do all the movement in js.
I'm trying to make some DOM element rotate smoothly around a fixed point. I'm writing this from scratch using jQuery and no matter what update speed I choose for the setInterval or how small I go with the amount of degrees the orbit advances on each loop, I get this janky staircase animation effect. I've tried using jquery's .animate instead of the .css hoping it would smooth things out but I cant seem to get it to work. Any help is appreciated.
In other words, it's not as smooth as rotating an image in HTML5 canvas. I want to make it smoother.
Here is a jsFiddle demonstrating the issue.
Notice how the animation is not quite smooth?
For reference, here is the code:
HTML
<div id="div"></div>
<div class="dot"></div>
<button class="stop">STOP</button>
<button class="start">START</button>
CSS
#div{
position:absolute;
height: 20px;
width: 20px;
background-color: #000;
}
.dot{
position:absolute;
width: 5px;
height: 5px;
background-color: #000;
}
button{
position:absolute;
}
.stop{
top:200px;
}
.start{
top:225px;
}
THE ALL IMPORTANT JAVASCRIPT
$(document).ready(function(){
$('#div').data('angle', 90);
var interval;
$('.stop').on('click', function(){
if(interval){
clearInterval(interval);
interval = undefined;
}
});
$('.start').on('click', function(){
if(!interval){
interval = setBoxInterval();
}
});
interval = setBoxInterval();
});
function drawOrbitingBox(degrees){
var centerX = 100,
centerY = 100,
div = $('#div'),
orbitRadius = 50;
//dot might not be perfectly centered
$('.dot').css({left:centerX, top:centerY});
//given degrees (in degrees, not radians), return the next x and y coords
function coords(degrees){
return {left:centerX + (orbitRadius * Math.cos((degrees*Math.PI)/180)),
top :centerY - (orbitRadius * Math.sin((degrees*Math.PI)/180))};
}
//increment the angle of the object and return new coords through coords()
function addDegrees(jqObj, degreeIncrement){
var newAngle = jqObj.data('angle') + degreeIncrement;
jqObj.data('angle', newAngle);
return coords(newAngle);
}
//change the left and top css property to simulate movement
// I've tried changing this to .animate() and using the difference
// between current and last position to no avail
div.css(addDegrees(div, degrees), 1);
}
function setBoxInterval(){
var interval = window.setInterval(function(){
drawOrbitingBox(-0.2); //This is the degree increment
}, 10); //This is the amount of time it takes to increment position by the degree increment
return interval;
}
I'd rather not resort to external libraries/plugins but I will if that's the accepted way of doing this kind of stuff. Thank you for your time.
That's because the value you set for top and left properties is rounded up. You should try using CSS Transforms.
Combining CSS Animations/Transitions and CSS Transforms you should also be able to get the animation without JavaScript.
Oh, I run into that myself!
There is actually nothing you can do, the stuttering you see is the pixel size. The pixel is the minimal step for css based animations, you can't do "half pixels" or "0.2 pixels". You will see that the same keeps happening with css3 animations.
The only solution is to speed up your animation, i'm afraid.
Also, cosndsider using rquestAnimationFrame instead of interval: https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame