Moving in an Arc with Webkit Transitions - javascript

Right now I'm trying to put together something really simple, learn from it, and incorporate it in a bigger project.
I have a simple box I'm trying to move from one position to another using css webkit animations and the translate function (for iOS hardware acceloration purposes). I need it to move in an arc and then stay at that point at the top of the arc.
Now, I'm pretty new to CSS transitions. In the past I've used jQuery animations but that seems to run really slowly on mobile devices. I know there's probably some best practice ideas I can incorporate here for setting and manging these animations, but I'm kinda figuring them out as I go.
Right now the box moves all the way up and then appears back in the starting position. How do I get it to stay there?
http://cs.sandbox.millennialmedia.com/~tkirchner/rich/M/march_madness/tmp/
<style type="text/css">
#ball {
display:block;
position: absolute;
width: 50px;
height: 50px;
overflow: hidden;
top: 500px;
left: 100px;
background-color: red;
} #action {
display: block;
font-weight:bold;
}
.animation {
-webkit-animation-name: throwBall;
-webkit-animation-timing-function: linear;
-webkit-animation-duration: 1s;
-webkit-animation-iteration-count: 1;
}
#-webkit-keyframes throwBall {
from { -webkit-transform: translate( 0px, 0px ); }
25% { -webkit-transform: translate( 75px, -25px ) }
50% { -webkit-transform: translate( 150px, -75px ) }
75% { -webkit-transform: translate( 225px, -150px ) }
to { -webkit-transform: translate( 300px, -300px ); }
}
</style>
<script type="text/javascript">
if ( typeof(jQuery) == 'undefined' ) document.write('<scri'+ 'pt type="text/javascript" src="http://code.jquery.com/jquery-1.4.3.min.js"></scri'+'pt>');
</script>
<a id='action'>Animate Me</a>
<div id='ball'></div>
<script type="text/javascript">
$(document).ready(function(){
$('#action').bind('click',function(){
$('#ball').addClass('animation').bind('webkitAnimationEnd',function(){
});
});
});
</script>

Just add the end state of the animation to your class as properties set by animation are removed when animation ends. Adding -webkit-transform: translate(300px, -300px); to your animation class fixes your problem.
.animation {
-webkit-animation-name: throwBall;
-webkit-animation-timing-function: linear;
-webkit-animation-duration: 1s;
-webkit-animation-iteration-count: 1;
-webkit-transform: translate(300px, -300px);
}

Related

Toggling between classes keeps animation from going through, just skips to end

I'm trying to animate a hamburger menu by having the bottom and top line translate to the middle and then rotate into an X and want to reverse the animation when the X is clicked. Using jquery I'm toggling the class menu-open and menu-closed. When I remove the CSS for the menu-closed animation, it fires just fine but when I add the CSS back the animations just skip to the last frame. It forms what I want but just refuses to use the animation fully.
CSS
.navbar .mobile-menu.menu-open .line::before {
animation: menu-open-top 250ms linear forwards;
}
.navbar .mobile-menu.menu-open .line {
animation: menu-middle 250ms linear forwards;
}
.navbar .mobile-menu.menu-open .line::after {
animation: menu-open-bottom 250ms linear forwards;
}
.navbar .mobile-menu.menu-closed .line::before {
animation: menu-open-top 250ms linear reverse;
}
.navbar .mobile-menu.menu-closed .line {
animation: menu-middle 250ms linear reverse;
}
.navbar .mobile-menu.menu-closed .line::after {
animation: menu-open-bottom 250ms linear reverse;
}
Animation
#keyframes menu-open-top {
30% {
bottom: 0;
}
60% {
bottom: 0;
transform: rotate(0) translate(0);
}
100% {
transform: rotate(45deg) translate(5px, 5px);
visibility: visible;
}
}
#keyframes menu-middle {
40% {
visibility: hidden;
}
to {
visibility: hidden;
}
}
#keyframes menu-open-bottom {
30% {
top: 0;
}
60% {
top: 0;
transform: rotate(0) translate(0);
}
100% {
transform: rotate(-45deg) translate(6px, -6px);
visibility: visible;
}
}
JS
$(".mobile-menu").click(expandMenu);
function expandMenu() {
$(".primary-nav").toggleClass("menu-expand");
$(this).toggleClass("menu-open menu-closed");
}
I must be missing something or maybe I need to add new keyframes for the reverse animation but that feels like it would be unnecessary.
edit: here is the html as well
HTML
<div class="mobile-menu menu-closed">
<div class="line"></div>
</div>
Here's how to do it using simple prop value changes with careful timing. I guess it can be done using #keyframe animations as well, but I find them more difficult to follow/control/sync, at least in this case, considering it's (basically) a two-step animation.
document.querySelector('.mobile-menu').addEventListener('click', ({
target
}) => {
target.closest('.mobile-menu').classList.toggle('menu-open');
})
.mobile-menu {
--duration: 0.42s;
--size: 3rem;
--padding: 0.5rem;
--color: red;
--distance-timing: cubic-bezier(0.5, 0, 0.3, 1);
--rotation-timing: cubic-bezier(0.4, 0, 0.2, 1);
width: var(--size);
height: var(--size);
padding: var(--padding);
display: flex;
flex-direction: column;
justify-content: space-between;
cursor: pointer;
}
.mobile-menu * {
box-sizing: border-box;
}
.mobile-menu>div {
border: 1px solid var(--color);
height: 0;
width: 100%;
position: relative;
transform-origin: 50% 50%;
transition:
top calc(0.6 * var(--duration)) var(--distance-timing) calc(0.4 * var(--duration)),
bottom calc(0.6 * var(--duration)) var(--distance-timing) calc(0.4 * var(--duration)),
transform calc(0.8 * var(--duration)) var(--rotation-timing) 0s;
}
.mobile-menu> :nth-child(1) {
top: calc(var(--padding)/2);
}
.mobile-menu> :nth-child(3) {
bottom: calc(var(--padding)/2);
}
.mobile-menu.menu-open>div {
transition:
top calc(0.4 * var(--duration)) var(--distance-timing) 0s,
bottom calc(0.4 * var(--duration)) var(--distance-timing) 0s,
transform calc(0.8 * var(--duration)) var(--rotation-timing) calc(0.2 * var(--duration));
}
.mobile-menu.menu-open> :nth-child(1) {
top: calc(50% - 1px);
transform: rotate(0.125turn);
}
.mobile-menu.menu-open> :nth-child(2) {
transform: rotate(0.125turn);
}
.mobile-menu.menu-open> :nth-child(3) {
bottom: calc(50% - 1px);
transform: rotate(-0.125turn);
}
<div class="mobile-menu">
<div></div>
<div></div>
<div></div>
</div>
Same thing, in SCSS: https://jsfiddle.net/websiter/dybre2f9/
I've extracted the values into CSS vars, so it can be reused and modified with ease. Feel free to tweak it to your liking.
Note: the reason why I'm using bottom and top to animate the movement (and not translateY - which is slightly more performant) is because I wanted the two animations to be completely independent of each other, to allow me to play with various overlapping values and timing functions. What I've come up with doesn't respect the requirement 100% (as in, it slightly overlaps the rotation with the movement - but I'm doing it on purpose, as not overlapping them looks too mechanical). When overlapped, the entire animation seems more organic. It's like the button is alive and happy to have been asked to do the animation. Or maybe I'm just a bit crazy, I don't know...

Loader CSS animation

This is an example of a loader animation copied from the w3schools website:
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.loader {
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 120px;
height: 120px;
-webkit-animation: spin 2s linear infinite; /* Safari */
animation: spin 2s linear infinite;
}
/* Safari */
#-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<h2>How To Create A Loader</h2>
<div class="loader"></div>
</body>
</html>
I have a similar one on my page with function to hide the loader after seconds of the main page refresh:
setTimeout(function() {
$('#loadingAnimation').addClass('hidden');
}, 1500);
Now my question is:
Is there a way to add the animation elements using JavaScript? I mean, when I inspect the page I can change the CSS of the loader which makes it visible, so how can I add the elements on the page using JavaScript and then remove it to make it unchangeable?
#loadingAnimation{
position: fixed;
max-width: 100%;
max-height: 100%;
top:0;
left:0;
right:0;
bottom:0;
background-color: #ffffff;
opacity: 1;
transition: 0.5s;
visibility: visible;
z-index: 10;
}
#loadingAnimation.hidden{
opacity: 0;
visibility: hidden;
}
I'm new to programming :) I need your help and I hope my question is clear!
There are some things you can do namely:
Add a class into your style that does the animation (Recommended since it requires the least work) and add elements with that class / add the class to elements.
Create a new style element with a unique id (somewhat overly complex for something of this scale)
requestAnimationFrame - Very hard and easy to go wrong - Basically just for reference
Implementation for 1:
CSS:
.loadingAnimation { /* . notates a class whereas # notates an id */
position: fixed;
max-width: 100%;
max-height: 100%;
top:0;
left:0;
right:0;
bottom:0;
background-color: #ffffff;
opacity: 1;
transition: 0.5s;
visibility: visible;
z-index: 10;
}
JS:
const loader = document.createElement("div");// Create a new div element
loader.className = "loadingAnimation"; // Add the loadingAnimation class to it
document.getElementById("root").append(loader); // Add it to an element with id root
setTimeout(function() {
loader.remove(); // Remove the loader
}, 1500);
Instead of applying the hidden class to the loader element you can just simply remove it instead!
setTimeout(function() {
$('#loadingAnimation').remove();
}, 1500);
This will remove it from the DOM and stop people playing with the styling in the dev tools.
I will add though that there's really not a lot you can do to stop people fiddling with websites in their own browsers and it's not that big of an issue.

When two elements use transform to move at the same time, why there is a gap in the middle?

Here is my bug demo:
https://jsbin.com/gijabuseca/edit?html,css,js,output
bug img
Is it a browser bug?
I solved this problem by replacing transform: translateX (100%) with left: 100%
However, using left to change the position performance is much lower than transform. If insist on using transform, is there a way to solve this gap problem?
A hack around this bug can be changing slightly the movement of div 1.
Offsetting a litle bit the timing function from what the standard ease value is, we adjust it closer to div2.
I have set the transition slower so that it is easier to see if it fails
setTimeout(function(){
document.querySelector('.demo1').classList.add('right');
document.querySelector('.demo2').classList.add('right');
});
body {
background: green;
}
.wrapper {
position: relative;
width: 100px;
}
.demo1, .demo2 {
position: absolute;
width: 100%;
height: 100px;
background: #ddd;
transition: 4s;
}
.demo1 {
transform: translateX(0);
transition-timing-function: cubic-bezier(.23, 0.12, .25, 1.05);
}
.demo2 {
transform: translateX(100%);
}
.demo1.right {
transform: translateX(100%)
}
.demo2.right {
transform: translateX(200%);
}
<div class="wrapper">
<div class="demo1"></div>
<div class="demo2"></div>
</div>

How do I make a div flash from 0 opacity to 1, continuously without clicking?

It's "Scroll Down" text, and I just need it to smoothly flash back and forth from 0 opacity to 1 the whole time the user is on the page.
Here's the HTML and CSS:
<div class="begin-scroll">SCROLL<br>
<span>TO BEGIN</span>
</div>
.begin-scroll{
font-family:'Charliedontsurf';
font-size:43px;
color:#FFFFFF;
position:absolute;
bottom:20%;
width: 100%;
text-align: center;
line-height:0.7em;
opacity:0;
}
.begin-scroll span{
font-size:34px;
}
This is the code that works for the type of effect I want (minus the continuous flashing):
jQuery(document).ready(function($) {
$('.begin-scroll').delay(3500).fadeTo(1000,1).fadeTo(1000,0).fadeTo(1000,1).fadeTo(1000,0).fadeTo(1000,1);
});
This is the kind of code I want, but the console log was throwing a "too much recursion" error:
jQuery(document).ready(function($) {
$('.begin-scroll').delay(3500).fadeTo(1000,1,pulsatingOut());
function pulsatingOut(){
$('.begin-scroll').fadeTo(1000, 0, pulsatingIn());
}
function pulsatingIn(){
$('.begin-scroll').fadeTo(1000, 1, pulsatingOut());
}
});
I'm not too fond of jQuery, so forgive me if this is a poorly put together and/or dumb question. Oh, and if you want to replace the jQuery altogether with plain 'ol javascript to solve this, please feel free, any solution helps.
Must it be Javascript/jQuery? This can be solved in CSS using animations and keyframes.
#-webkit-keyframes NAME-YOUR-ANIMATION {
0% { opacity: 0; }
50% { opacity: 1; }
100% {opacity: 0; }
}
#-moz-keyframes NAME-YOUR-ANIMATION {
0% { opacity: 0; }
50% { opacity: 1; }
100% {opacity: 0; }
}
#-o-keyframes NAME-YOUR-ANIMATION {
0% { opacity: 0; }
50% { opacity: 1; }
100% {opacity: 0; }
}
#keyframes NAME-YOUR-ANIMATION {
0% { opacity: 0; }
50% { opacity: 1; }
100% {opacity: 0; }
}
#box {
-webkit-animation: NAME-YOUR-ANIMATION 2s infinite; /* Safari 4+ */
-moz-animation: NAME-YOUR-ANIMATION 2s infinite; /* Fx 5+ */
-o-animation: NAME-YOUR-ANIMATION 2s infinite; /* Opera 12+ */
animation: NAME-YOUR-ANIMATION 2s infinite; /* IE 10+, Fx 29+ */
}
<div id="box" style="width: 50px; height: 50px; background-color: red;"></div>
Remove the () from your complete parameters in the .fadeTo call. You want to simply pass a reference of that function, not the result.
;(function($){
$(function(){
// store a reference (slight cache improvement)
var $el = $('.begin-scroll');
// declare the functions
function pulsatingOut(){
$el.fadeTo(1000, 0, pulsatingIn);
}
function pulsatingIn(){
$el.fadeTo(1000, 1, pulsatingOut);
}
// call first one and have it loop through
pulsatingIn();
});
})(jQuery);
.begin-scroll { width: 100px; height: 100px; background-color: #f0f; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="begin-scroll"></div>
This is similar to Brad's answer, but a more basic approach.
As Brad said, you will want to pass a callback to the fadeTo method. Callbacks are also known as delegates, function references, etc. As soon as you add the parentheses at the end, you are telling JavaScript to execute that function reference.
Since I had already developed my fiddle while Brad was answering, here's what I came up with. It's not as self-contained, but it works and gives you a simplified idea. I did have to change your text color to black.
jsfiddle: http://jsfiddle.net/o5qgq6LL/1/
function pulsatingIn(){
$(this).fadeIn(1000, pulsatingOut);
}
function pulsatingOut(){
$(this).fadeOut(1000, pulsatingIn);
}
$('.begin-scroll').delay(3500).fadeIn(1000, pulsatingOut);
.begin-scroll{
font-family:sans-serif;
font-size:43px;
color:#000;
position:absolute;
bottom:20%;
width: 100%;
text-align: center;
line-height:0.7em;
display:none;
}
.begin-scroll span{
font-size:34px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="begin-scroll">SCROLL<br>
<span>TO BEGIN</span>
</div>

how do I add a custom css3 animation but also a transition on hover?

I have set up a basic CSS3 animation test which increases and decreases the background-size of the element, when you click one of the elements then the class active is toggled and a transition occurs. My problem is though that when the .active class is removed the animation doesnt occur anymore since I have reset animation to none. Can anyone advise how I can fix this problem?
CSS
html{height:100%;}
body {background:#000;height:100%;min-height:100%;padding:20px}
ul{height:100%;display:block}
li{float:left;margin-right:30px;position:relative;}
#-webkit-keyframes throb_outer {
0% { background-size:1px 100%; }
100% { background-size:1px 130%; }
}
#-webkit-keyframes throb_inner {
0% { background-size:1px 100%; }
100% { background-size:1px 115%; }
}
.outer{display:block;width:50px;padding:0 1px;cursor: pointer;
-webkit-animation: throb_outer 2s infinite alternate;
}
.b{background: -webkit-linear-gradient(black 30%, rgba(41,184,229,1) 50%, black 70%) repeat-x; }
.g{background: -webkit-linear-gradient(black 20%, rgba(33, 130, 61, 1) 50%, black 80%) repeat-x;}
.r{background: -webkit-linear-gradient(black 40%, rgba(255,0,0,1) 50%, black 60%) repeat-x;}
.li{height:100%;-webkit-transition: background-size .3s linear;
background-position:0 50%;
background-size:1px 100%;}
.inner{
width:100%;
display:block;
-webkit-animation: throb_inner 2s infinite alternate;
}
.outer.active{-webkit-animation:none;background-size:1px 200%;}
.outer.active .inner{-webkit-animation:none;background-size:1px 150%;}
​
JS
$(document).ready(function() {
$('#test').children().on('click', function(e) {
$(this).toggleClass('active');
});
});
Fiddle here: http://jsfiddle.net/VQaGh/22/
as my understanding you want effects something like this
try this one http://jsfiddle.net/Sk2sX/
I have modify both css and js hope it will help you
for any query regarding code post a comment

Categories