CSS continuous rotation without setTimeout - javascript

So I've managed to create a rotation that only rotates in one direction by using a setTimeout. I was wondering if I could still achieve this without using it. The main issue I have is that if I click fast enough, it will still spin the other way.
So overall, is there a way to make it spin in one direction regardless of how fast I click.
function clicked()
{
element = $("#spin");
if ($(element).hasClass("rotate2"))
{
$(element).removeClass("rotate rotate2");
setTimeout( () => $(element).addClass("rotate"), 1);
}
else if($(element).hasClass("rotate"))
{
$(element).addClass("rotate2");
}
else
{
$(element).addClass("rotate");
}
}
div.horizontal {
position: absolute;
width: 100%;
height: 5px;
background-color: red;
top: 50%;
transform: translateY(-50%);
}
div.horizontal.rotate {
transform: translateY(-50%) rotate(-180deg);
transition: transform 0.5s;
}
div.horizontal.rotate2 {
transform: translateY(-50%) rotate(-360deg);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="width: 50px; height: 50px; position: relative;">
<div id="spin" class="horizontal">
</div>
</div>
<button onclick="clicked()">Rotate</button>

You can just use a single class for the transition, and remove the class on transitionend
var $button = $('#button'),
$spin = $('#spin');
$(document).on('click', $button, function() {
$spin.addClass('rotate').on('transitionend',function() {
$(this).removeClass('rotate');
});
})
div.horizontal {
position: absolute;
width: 100%;
height: 5px;
background-color: red;
top: 50%;
}
div.horizontal.rotate {
transform: rotate(-180deg);
transition: transform .5s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="width: 50px; height: 50px; position: relative;">
<div id="spin" class="horizontal">
</div>
</div>
<button id="button">Rotate</button>

Related

Clicking a button to initiate an animation, then clicking another button to revers that animation

I created a button where you can click it. It then initiates a CSS animation where some buttons and text flow down the screen saying are you sure you want to do this? They can then click the yes or no button and I want it to reverse the animation back up. I have completed the animation going down but I can't manage to repeat that animation going back up when they click a button.
Here is the HTML:
<button onclick="confirm()">Delete</button>
<div id="wholeshow" class="confirm-whole">
<h1 class="confirm">Delete</h1>
<p class="confirm">Are you sure you want to delete ____?</p>
<button class="confirm" onclick="reset()">Delete</button>
<button class="confirm" onclick="cancel()">Cancel</button>
</div>
Here is the JS:
function confirm() {
document.getElementById("wholeshow").classList.add("add");
}
and here is the CSS:
.confirm-whole {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: grey;
animation: slideIn 0.5s forwards;
}
#keyframes slideIn {
0% {
top: -250px;
height: 0%;
}
99% {
height: 0%;
}
100% {
top: 0px;
height: 100%;
}
}
.add {
display: block;
}
Thanks so much for the help!
The JavaScript isn't pretty but it works, perhaps a better solution would be to use CSS transitions if they are able to support your animation requirements.
function confirm() {
document.getElementById("wholeshow").classList.add("add");
}
// added
function reset() {
const wholeshow = document.getElementById("wholeshow");
const resetDir = () => { // need to reset the animation direction and remove the `add` class
wholeshow.removeEventListener("animationend", resetDir, true);
wholeshow.style.animationDirection = "normal";
wholeshow.classList.remove("add");
};
wholeshow.addEventListener("animationend", resetDir, true);
wholeshow.style.animationDirection = "reverse";
wholeshow.classList.remove("add");
void wholeshow.offsetWidth; // force rerender
wholeshow.classList.add("add");
}
.confirm-whole {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: grey;
animation: slideIn 0.5s;
}
#keyframes slideIn {
0% {
top: -250px;
height: 0%;
}
99% {
height: 0%;
}
100% {
top: 0px;
height: 100%;
}
}
.add {
display: block;
}
<button onclick="confirm()">Delete</button>
<div id="wholeshow" class="confirm-whole">
<h1 class="confirm">Delete</h1>
<p class="confirm">Are you sure you want to delete ____?</p>
<button class="confirm" onclick="reset()">Delete</button>
<button class="confirm" onclick="cancel()">Cancel</button>
</div>

Play new css animation seamlessly

I created a simple HTML game, which disappears under the screen when I click on a moving box.
However, the animation that disappears starts at the original location, not where it was clicked.
I think 0% of the remove #keyframes should have the location of the click, but I couldn't find a way
How shall I do it?
(function () {
const charactersGroup = document.querySelectorAll('.character');
const stage = document.querySelector('.stage')
const clickHandler = (e) => {
const target = e.target;
if (target.classList.contains('character')) {
target.classList.remove(`f${target.dataset.id}`);
target.classList.add('f0');
target.classList.add('remove');
setTimeout(() => { stage.removeChild(target) }, 2000);
}
}
stage.addEventListener('click', clickHandler);
}());
.stage {
overflow: hidden;
position: relative;
background: #eeeeaa;
width: 40vw;
height: 20vw;
}
#keyframes moving {
0% {
transform: translateX(0);
}
100% {
transform: translateX(30vw);
}
}
#keyframes remove {
0% {
transform: translate(0);
}
100% {
transform: translateY(60vw);
}
}
.character {
position: absolute;
width: 50px;
height: 50px;
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: contain;
animation: moving infinite alternate;
}
.remove {
animation: remove 0.2s cubic-bezier(.68,-0.55,.27,1.55) forwards;
}
.f0 {
background-color: black;
animation-duration: 2s;
}
.f1 {
left: 5%;
bottom: 5%;
animation-duration: 2s;
background-color: red;
}
<div class="stage">
<div class="character f1" data-id="1"></div>
</div>
Change the first animation to consider left instead of translate then append both of them to the element initially and you simply toggle the animation-play-state when adding the remove class
(function() {
const charactersGroup = document.querySelectorAll('.character');
const stage = document.querySelector('.stage')
const clickHandler = (e) => {
const target = e.target;
if (target.classList.contains('character')) {
target.classList.add('remove');
setTimeout(() => {
stage.removeChild(target)
}, 2000);
}
}
stage.addEventListener('click', clickHandler);
}());
.stage {
overflow: hidden;
position: relative;
background: #eeeeaa;
width: 40vw;
height: 20vw;
}
#keyframes moving {
100% {
left:calc(95% - 50px);
}
}
#keyframes remove {
50% {
transform: translateY(-30vh);
}
100% {
transform: translateY(60vw);
}
}
.character {
position: absolute;
width: 50px;
height: 50px;
background:red;
left: 5%;
bottom: 5%;
animation:
moving 2s infinite alternate,
remove 1s cubic-bezier(.68, -0.55, .27, 1.55) forwards paused;
}
.remove {
animation-play-state:paused,running;
background: black;
}
<div class="stage">
<div class="character f1" data-id="1"></div>
</div>
If your use case is to deal with a lot of such boxes and complexity, it's better to go with handling everything with pure JS but I tried to make this work with minimal changes in JS and CSS.
I have added comments to the new JS lines.
Also taken the liberty to have a separate class with name moving for animation moving so that we can remove it on click.
(function () {
const charactersGroup = document.querySelectorAll('.character');
const stage = document.querySelector('.stage')
const clickHandler = (e) => {
const target = e.target;
if (target.classList.contains('character')) {
target.classList.remove(`f${target.dataset.id}`);
target.classList.add('f0');
// remove the moving animation
target.classList.remove('moving');
// Get offsetWidth which is the half of width to substract later while calculating left for the target i.e our box.
const offsetWidth = parseInt(getComputedStyle(target).width)/2;
// e.clientX gives us the x coordinate of the mouse pointer
// target.getBoundingClientRect().left gives us left position of the bounding rectangle and acts as a good offset to get the accurate left for our box.
target.style.left = `${e.clientX -target.getBoundingClientRect().left - offsetWidth}px`;
target.classList.add('remove');
setTimeout(() => { stage.removeChild(target) }, 2000);
}
}
stage.addEventListener('click', clickHandler);
}());
.stage {
overflow: hidden;
position: relative;
background: #eeeeaa;
width: 40vw;
height: 20vw;
}
#keyframes moving {
0% {
transform: translateX(0);
}
100% {
transform: translateX(30vw);
}
}
#keyframes remove {
0% {
transform: translate(0vh);
}
100% {
transform: translateY(60vw);
}
}
.character {
position: absolute;
width: 50px;
height: 50px;
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: contain;
}
.moving{
animation: moving infinite alternate;
}
.remove {
animation: remove 0.2s cubic-bezier(.68,-0.55,.27,1.55) forwards;
}
.f0 {
background-color: black;
animation-duration: 2s;
}
.f1 {
left: 5%;
bottom: 5%;
animation-duration: 2s;
background-color: red;
}
<div class="stage">
<div class="character moving f1" data-id="1"></div>
</div>

Adding and removing class on scroll

I am adding a class to a div after a user scrolls. This works fine, but for some reason it won't remove this class when the user scrolls back again. There are no errors in the console. Where am I going wrong?
var scrolled = $('body').offset().top - 800;
$(window).on('resize scroll', function() {
if ($(window).scrollTop() > scrolled) {
$('#one').addClass('in');
} else {
$('#one').removeClass('in');
}
});
section.bg-red {
background: red;
}
section.bg-blue {
background: blue;
}
section {
min-height: 100vh;
}
section p {
color: red;
text-align: center;
position: absolute;
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
section.in p {
color: #fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="bg-red" id="one">
<p>Well done you scrolled</p>
</section>
<section class="bg-blue">
ddd
</section>
View on Codepen
The problem is that you are subtracting 800 from the body's offset top, which will produce a negative number. The window's scroll top will never be a negative number, so the class will never be removed.
section.bg-red {
background: red;
}
section.bg-blue {
background: blue;
}
section {
min-height: 100vh;
}
section p {
color: red;
text-align: center;
position: absolute;
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
section.in p {
color: #fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="bg-red" id="one">
<p>Well done you scrolled</p>
</section>
<section class="bg-blue">
ddd
</section>
<script>
var scrolled = $('body').offset().top;
$(window).on('resize scroll', function() {
if ($(window).scrollTop() > scrolled) {
$('#one').addClass('in');
} else {
$('#one').removeClass('in');
}
});
</script>
I found a solution using Waypoints.js which does as required. All that is necessary is to include waypoints.js to the project and write the following Javascript below.
var $elone = $('.element-one');
$elone.waypoint(function(direction) {
if (direction == 'down') {
$elone.addClass('in');
}
else {
$elone.removeClass('in');
}
}, {offset: '50%'});
This allows you to use percentages instead of pixels which works better for a responsive website.

Trigger CSS Animations in JavaScript

I don't know how to use JQuery, so I need a method which could trigger an animation using JavaScript only.
I need to call/trigger CSS Animation when the user scrolls the page.
function start() {
document.getElementById('logo').style.animation = "anim 2s 2s forward";
document.getElementById('earthlogo').style.animation = "anim2 2s 2s forward";
}
* {
margin: 0px;
}
#logo {
position: fixed;
top: 200px;
height: 200px;
width: 1000px;
left: 5%;
z-index: 4;
opacity: 0.8;
}
#earthlogo {
position: fixed;
top: 200px;
height: 120px;
align-self: center;
left: 5%;
margin-left: 870px;
margin-top: 60px;
z-index: 4;
opacity: 0.9;
}
#keyframes anim {
50% {
filter: blur(10px);
transform: rotate(-15deg);
box-shadow: 0px 0px 10px 3px;
}
100% {
height: 100px;
width: 500px;
left: 10px;
top: 10px;
box-shadow: 0px 0px 15px 5px rgba(0, 0, 0, 0.7);
background-color: rgba(0, 0, 1, 0.3);
opacity: 0.7;
}
}
#keyframes anim2 {
50% {
filter: blur(40px);
transform: rotate(-15deg);
}
100% {
height: 60px;
width: 60px;
left: 10px;
top: 10px;
margin-left: 435px;
margin-top: 30px;
opacity: 0.8;
}
}
#backstar {
position: fixed;
width: 100%;
z-index: 1;
}
#earth {
position: absolute;
width: 100%;
z-index: 2;
top: 300px;
}
<img src="logo.png" id="logo" onclick="start();">
<img src="earthlogo.gif" id="earthlogo" onscroll="start();">
<img src="earth.png" id="earth">
<img src="stars.jpg" id="backstar">
The simplest method to trigger CSS animations is by adding or removing a class - how to do this with pure Javascript you can read here:
How do I add a class to a given element?
If you DO use jQuery (which should really be easy to learn in basic usage) you do it simply with addClass / removeClass.
All you have to do then is set a transition to a given element like this:
.el {
width:10px;
transition: all 2s;
}
And then change its state if the element has a class:
.el.addedclass {
width:20px;
}
Note: This example was with transition. But for animations its the same: Just add the animation on the element which has a class on it.
There is a similar question here: Trigger a CSS keyframe animation via scroll
This is how you can use vanilla JavaScript to change/trigger an animation associated with an HTML element.
First, you define your animations in CSS.
#keyframes spin1 { 100% { transform:rotate(360deg); } }
#keyframes spin2 { 100% { transform:rotate(-360deg); } }
#keyframes idle { 100% {} }
Then you use javascript to switch between animations.
document.getElementById('yourElement').style.animation="spin2 4s linear infinite";
Note: 'yourElement' is the target HTML element that you wish to
animate.
For example: <div id="yourElement"> ... </div>
Adding and removing the animation class does not work in a function. The delay is simply too little. As suggested by this article you can request the browser to reflow and then add the class. The delay isn't an issue in that case. Hence, you can use this code:
element.classList.remove("animation")
element.offsetWidth
element.classList.add("animation")
The best thing is, this works everywhere. All credit goes to the article.
A more idiomatic solution is to use the Web Animations API.
Here is the example from MDN:
document.getElementById("alice").animate(
[
{ transform: 'rotate(0) translate3D(-50%, -50%, 0)', color: '#000' },
{ color: '#431236', offset: 0.3 },
{ transform: 'rotate(360deg) translate3D(-50%, -50%, 0)', color: '#000' }
], {
duration: 3000,
iterations: Infinity
}
);
OP's example:
document.getElementById('logo').animate(
[
{},
{
filter: 'blur(10px)',
transform: 'rotate(-15deg)',
box-shadow: '0px 0px 10px 3px',
},
{
height: '100px',
width: '500px',
left: '10px',
top: '10px',
box-shadow: '0px 0px 15px 5px rgba(0, 0, 0, 0.7)',
background-color: 'rgba(0, 0, 1, 0.3)',
opacity: '0.7',
},
],
{
duration: 2000,
delay: 2000,
fill: 'forwards',
},
)
At the time of writing, it's supported in all major browsers except IE.
Supported browsers
I have a similar problem.
The best answer didn’t work for me, but when I added the delay it worked.
The following is my solution.
CSS
.circle_ani1,
.circle_ani2 {
animation-duration: 1s;
animation-iteration-count: 1;
}
.circle_ani1 {
animation-name: circle1;
}
.circle_ani2 {
animation-name: circle2;
}
JS
let temp_circle1 = $('.TimeCountdown_circle1').removeClass('circle_ani1');
let temp_circle2 = $('.TimeCountdown_circle2').removeClass('circle_ani2');
window.setTimeout(function() {
temp_circle1.addClass('circle_ani1');
temp_circle2.addClass('circle_ani2');
}, 50);
Vanilla JS version
document.getElementById('logo').classList.add("anim");
document.getElementById('earthlogo').classList.add("anim2");
You could use CSS to hide the image / animation and show when the user scrolls. This would work like this:
CSS:
div {
border: 1px solid black;
width: 200px;
height: 100px;
overflow: scroll;
}
#demo{
display: none;
}
HTML:
<div id="myDIV"> </div>
<div id="demo">
<img src="earthlogo.gif" id="earthlogo" alt="Thanks for scrolling. Now you see me">
</div>
Your javascript just needs to include an eventListener to call the function which triggers the display of your animation.
JS:
document.getElementById("myDIV").addEventListener("scroll", start);
function start() {
document.getElementById('demo').style.display='block';
}
You could use animation-play-state (Mdn docs) like this
element.style.animationPlayState = "paused/running"
Code snippet:
function play() {
document.getElementById("div").style.animationPlayState = "running";
}
function pause() {
document.getElementById("div").style.animationPlayState = "paused";
}
.animation {
width: 50px;
height: 50px;
background-color: red;
position: relative;
animation-name: example;
animation-duration: 2s;
animation-play-state: paused;
animation-iteration-count: infinite;
}
#keyframes example {
0% {
background-color: red;
left: 0px;
top: 0px;
}
25% {
background-color: yellow;
left: 50px;
top: 0px;
}
50% {
background-color: blue;
left: 50px;
top: 50px;
}
75% {
background-color: green;
left: 0px;
top: 50px;
}
100% {
background-color: red;
left: 0px;
top: 0px;
}
}
<button onclick="play()">Play</button>
<button onclick="pause()">Pause</button><br><br>
<div id="div" class="animation"></div>
Here's the main code:
HTML:
<img id="myImg">
CSS:
#myImg {
//properties
animation: animate 2s linear infinite //infinite is important!
}
#keyframes animate {
//animation base
}
JS:
document.getElementById("myImg").style.webkitAnimationPlayState = "paused";
window.addEventListener("scroll", function() {
document.getElementById("myImg").style.webkitAnimationPlayState = "running";
setTimeout(function() {
document.getElementById("myImg").style.webkitAnimationPlayState = "paused";
}, 2000);
});
If you want Animations i recommend you create a CSS class which you toggle on a Condition whit JS:
CSS
.animation {
animation: anim 2s ease infinite;
transition: .2s
}
JS
// Select your Element
$element.document.querySelector(".yourElement");
$element.addEventListner('click', () => {
$element.classList.toggle("animation")
})

Elements rotating around centre of DIV - only last one added to page responds to onClick event

I have six divs inside a main one. They rotate around its centre as intended.
They should all have a different image and bring up some related text when clicked.
When clicked, all elements respond to the onClick events of the rotating div with the highest Z index. If more have equal Z index, then the last one added.
From what I gathered using FireBug, each rotating div is contained inside a bigger square that rotates with it, which creates a layer that covers all the other ones.
Is there a way to work around this problem?
This is the html file:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="./css/desktop.css">
<meta charset="UTF-8">
<title>Rotating images</title>
</head>
<body>
<div class="container-round">
<div class="div0" id = "0" style = "z-index : 15" onClick = "alert ( this.id + ' - ' + this.style.zIndex )"></div>
<div class="div1" id = "1" style = "z-index : 15" onClick = "alert ( this.id + ' - ' + this.style.zIndex )"></div>
<div class="div2" id = "2" style = "z-index : 10" onClick = "alert ( this.id + ' - ' + this.style.zIndex )"></div>
<div class="div3" id = "3" style = "z-index : 10" onClick = "alert ( this.id + ' - ' + this.style.zIndex )"></div>
<div class="div4" id = "4" style = "z-index : 10" onClick = "alert ( this.id + ' - ' + this.style.zIndex )"></div>
<div class="div5" id = "5" style = "z-index : 10" onClick = "alert ( this.id + ' - ' + this.style.zIndex )"></div>
</div>
</body>
</html>
And this is the CSS
div {
float : left;
}
.container-round {
width: 700px;
height: 700px;
float: left;
position: relative;
overflow: hidden;
background: #000;
border-radius: 350px;
}
.div0 {
position: absolute;
width: 18%;
height: 25%;
background: url(../images/img0.jpg) no-repeat 500px center;
animation: orbit0 10s linear infinite;
}
.div1 {
position: absolute;
width: 100%;
height: 100%;
background: url(../images/img1.jpg) no-repeat 500px center;
animation: orbit1 10s linear infinite;
}
.div2 {
position: absolute;
width: 100%;
height: 100%;
background: url(../images/img2.jpg) no-repeat 500px center;
animation: orbit2 10s linear infinite;
}
.div3 {
position: absolute;
width: 100%;
height: 100%;
background: url(../images/img3.jpg) no-repeat 500px center;
animation: orbit3 10s linear infinite;
}
.div4 {
position: absolute;
width: 100%;
height: 100%;
background: url(../images/img4.jpg) no-repeat 500px center;
animation: orbit4 10s linear infinite;
}
.div5 {
position: absolute;
width: 100%;
height: 100%;
background: url(../images/img5.jpg) no-repeat 500px center;
animation: orbit5 10s linear infinite;
}
#keyframes orbit0 {
from { transform:rotate(0deg); } to { transform: rotate(360deg); }
}
#keyframes orbit1 {
from { transform:rotate(60deg); } to { transform: rotate(420deg); }
}
#keyframes orbit2 {
from { transform:rotate(120deg); } to { transform: rotate(480deg); }
}
#keyframes orbit3 {
from { transform:rotate(180deg); } to { transform: rotate(540deg); }
}
#keyframes orbit4 {
from { transform:rotate(240deg); } to { transform: rotate(600deg); }
}
#keyframes orbit5 {
from { transform:rotate(300deg); } to { transform: rotate(660deg); }
}
The working example is here:
http://68.169.58.245/darryl/
Thank you all very much
This might help -
CSS and HTML
.container{
width: 700px;
height: 700px;
border-radius: 50%;
background-color: black;
position: relative;
animation: orbit0 10s linear infinite;
}
.img{
background: white;
position: absolute;
width: 27.78%;
height: 16.67%;
}
.first {
top: 8.33%;
left: 36.11%;
background-image: url(http://68.169.58.245/darryl/images/img1.jpg);
transform: rotate(0deg);
}
.second {
top: 25%;
left: 65.27%;
transform: rotate(60deg);
background-image: url(http://68.169.58.245/darryl/images/img1.jpg);
}
.third {
bottom: 25%;
left: 65.27%;
transform: rotate(120deg);
background-image: url(http://68.169.58.245/darryl/images/img1.jpg);
}
.forth {
bottom: 8.33%;
left: 36.11%;
transform: rotate(180deg);
background-image: url(http://68.169.58.245/darryl/images/img1.jpg);
}
.fifth {
transform: rotate(240deg);
bottom: 25%;
right: 65.27%;
background-image: url(http://68.169.58.245/darryl/images/img1.jpg);
}
.sixth {
top: 25%;
transform: rotate(300deg);
right: 65.27%;
background-image: url(http://68.169.58.245/darryl/images/img1.jpg);
}
#keyframes orbit0 {
from { transform:rotate(0deg); } to { transform: rotate(360deg); }
}
<div class="container">
<div class="img first" id="1" onclick="alert(this.id+'-'+this.className)">
</div>
<div class="img second" id="2" onclick="alert(this.id+'-'+this.className)">
</div>
<div class="img third" id="3" onclick="alert(this.id+'-'+this.className)">
</div>
<div class="img forth" id="4" onclick="alert(this.id+'-'+this.className)">
</div>
<div class="img fifth" id="5" onclick="alert(this.id+'-'+this.className)">
</div>
<div class="img sixth" id="6" onclick="alert(this.id+'-'+this.className)">
</div>
</div>
From your css, each div 1 through 5 is using the same image: background: url(../images/img1.jpg). That would explain why all divs are displaying the same image.
For the on-click: The reason you're seeing the same thing is that all your divs 1-5 are taking up 100% of the width and height. You're just offsetting where the image itself is displayed. So, when you click anywhere, you're actually clicking on the top-most element. To get around that, you probably want to move away from width:100%, height:100% and instead position the image alone. That is, set the div to the image size then apply the appropriate transform to get it to the location you want.
Might be a good idea to also add a border: 1px solid red; to the divs while you're debugging just to see where your divs actually are.

Categories