Can I make a transition go through a keyframe? - css

I have a css 'level up' bar defined like so:
.levelup-bar {
background: //;
color: //;
-webkit-transition:width .6s ease;
transition:width .6s ease;
}
And it works out quite beautifully indeed, my bar automatically animates when I change the width!
What I am doing is calculating the current rate against a max. Of course, once you reach the max, you level up and it goes back to zero. So once it is 100/100, I'm conditionally making it 0/100 to simulate a level up.
And that is the problem, my bar never reaches 100/100 and it is never truly full.
There is a million and one way to solve this in javascript, but I want to solve it using css3. Is there a way to keyframe my width so that it always goes one way, and resets only to zero once its maxed out?
Plunkr: http://plnkr.co/edit/N42aoxfJzpIjhWMofmIP?p=preview

On your onclick event on the button you have the following:
cur += 10;
if(cur>=max)
cur = 0;
If you change the if statement from >= to > the bar will fill up if it reaches 100 exactly:
cur += 10;
if(cur>max)
cur = 0;
I'm not entirely sure if this is the solution you are after.
If it goes over the 100 you could stop it at 100, then set a timer to add the remainder of the score after x ms (but then you are using Javascript again) ?

This seems to work :
// reset cur when you click and you are already at 100%
if(cur >= max)
cur = 0;
cur += 10;
if(cur > max)
cur = 0;

Related

How to stop a carousel sliding when reaches the last item?

I'm making a carousel from scratch, it contains 7 itens, the problem is when reaches the last item i still can click to the next one, i want that every time it reaches the last item it stops.
I'm using transform to make the sliding effect, so every time i press the next button it increases the translateX, i dont know if this is the better aproach.
transform: ${({ currentSlide }) =>
currentSlide ? `translateX(-${currentSlide}00%)` : "translate(0%)"};
I dont want this big space when I reach the last item
This should be the expected result when the last dog appears
Code: https://codesandbox.io/s/dogs-carousel-u7126?file=/src/App.tsx
I'd modify how currentSlide is handled.  With 3 images on screen and 7 total in the carousel, advancing currentSlide past the 5th image is what causes the empty space, so preventing it from doing that by clamping currentSlide should fix the issue:
// hopefully self-explanatory constants:
const NUM_IMAGES_VISIBLE = 3;
const NUM_IMAGES_TOTAL = 7;
// after sliding, try this, assuming currentSlide 0 is the first image:
currentSlide = Math.min( currentSlide, NUM_IMAGES_TOTAL - NUM_IMAGES_VISIBLE )

Translate CSS transition-timing-function to swiping

I am fading in a div (using CSS transitions) with a custom timing function (http://cubic-bezier.com/#1,0,1,1). The timing function is basically a more extreme version of 'ease-in'.
div {
opacity: 0;
transition: opacity 1s;
.in {
opacity: 1;
transition-timing-function: cubic-bezier(1, 0, 1, 1);
}
In addition to that, I want to be able to fade in the div by swiping across the screen. I am using the following Jquery to set the opacity according to how far the user has swiped:
documentWidth = $(document).width();
$(document).on('touchmove', function(e) {
// How much of the animation is completed (in %)
completion = e.changedTouches[0].pageX / documentWidth;
$('div').css('opacity', completion);
})
Nooooow, this is linear! Is there a clever Math-person out there who can figure out how to re-state that last line to represent my timing function?
So, for example, if completion is at 25%, the opacity should be around 2%. At 50%, it should be around 11% and at 75% it should be around 31%.
Start by finding a curve that approximates your cubic-bezier curve. With the given points and some online tools it's possible to draw a curve with this equation:
y = 464085.7 + (0.0174619 - 464085.7)/(1 + (x/22.88957)^4.174069)
in your case the x represents your completion variable and y the resulting opacity.
Then your code becomes
let completion = e.changedTouches[0].pageX / documentWidth;
let exponential = Math.pow((completion / 22.88957), 4.174069);
let opacity = 464085.7 + (0.0174619 - 464085.7)/(1 + exponential);
$('div').css('opacity', opacity);
(of course you may find a better equation that best fits your needs)

Resetting RotateY degrees without transformation

I use css 3D-transform rotateY to flip a div with css transition. I want the image to flip for a certain number of times : when the transition ends, I trigger it again until a certain counter value is reached.
What I would like to do : when the rotateY reaches 360 deg, I want reset it to 0 to restart the same rotation.
What happens: if I reset to 0, the div rotates backward first. I tried to disable the transform property before "rewinding', without any luck.
Is there an easy way to restore the 0 deg value without any transformation / rotation?
I did a codepen to illustrate: http://codepen.io/3MO/pen/QKogxE
It is not very graphical, but if you click on start, then reset, it will be obvious.
Here is my reset function:
$('#reset').click(function () {
deg = 0;
countRotations = 0;
$('#card').attr('transition', 'transform 0');
flipStarted = false;
flip($('#card'), 0);
});
You need to set the transition property to 0s, then change the rotation to 0 (this way it'll rotate instantly), then change the transition back to the "default" value.
Thanks to #Harry here's a working demo:
http://codepen.io/hari_shanx/pen/PGLKzN

CSS animation performance

I have a small hobby project in which I try to build a matrix rain: .
See demo here. Or this JSFiddle
My question is: how can I make this more efficient, as I can see it gets slow when I add a lot of columns.
I have implemented it as rendering a lot of absolute positioned divs that are animated.
Here is my CSS:
div
{
position:absolute;
width:1em;
display:inline-block;
color: black;
animation-name: example;
animation-duration: 2s;
text-shadow: none;
}
#keyframes example
{
0% {color: white; text-shadow: -1px 1px 8px white;}
15% {color: #5f5 ; text-shadow: -1px 1px 8px #5f5 ;}
100% {color: black; text-shadow: none;}
}
In javascript I set some custom styling for each div, where I vary some settings, like font-size, animation speed etc.
Main part of the JS:
var textStrip = ['诶', '比', '西', '迪', '伊', '吉', '艾', '杰', '开', '哦', '屁', '提', '维'];
var matrixcol = function()
{
var top = Math.floor(Math.random() * $(window).height() * 0.5);
var size = 10 + Math.floor(Math.random()*10);
var col = Math.floor(Math.random() * $(window).width() - size);
var ms = 500 + Math.floor(Math.random()*1500);
var timer;
var aap = function()
{
var randomNumber = Math.floor(Math.random()*textStrip.length);
var newelem = $("<div style='font-size:"+ size+ "px;top:"+top+"px; left:"+col+"px;animation-duration:"+ 2*ms + "ms'>" + textStrip[randomNumber] + "</div>" );
$('body').append(newelem);
top+=size;
setTimeout( function() {newelem.remove();}, (1.6*ms)-(ms/40));
if (top>$(window).height()-size)
{
size = 10 + Math.floor(Math.random()*10);
top=0; Math.floor(Math.random() * $(window).height() * 0.5);
col = Math.floor(Math.random() * $(window).width() -size);
ms = 500 + Math.floor(Math.random()*1500);
clearInterval(timer);
timer = setInterval(aap, ms/40);
}
}
timer = setInterval(aap, ms/40);
}
$( document ).ready(function() {
var i;
for (i = 0; i < 25; i++) {
matrixcol();
}
I have tried to use the chrome profiling, that shows my a warning:
Long frame times are an indication of jank and poor rendering
performance.
The link that is provided gives some insight; however, as far a I can see I don't have much layouting going on.
tl;dr
It is slow. What would be a good performance optimizations?
After several try, I think your best solution is looking to canvas, if the exact animation is desired.
The ending result I get is here. Not as exact as yours but get a 50+ fps.
For every modification I have added comment, please check it out.
Cache
The easiest thing you can do is cache $(window).height(). It is usually a stable number, no need to re-query it. And resize handler can be added to adapt viewport change. Cache window size changes my fps from 9~10 to 12~15. Not big, but a low-hanging fruit.
Expensive Style
The next thing you need to do is remove text-shadow, it is a very expensive style, given the node number in your case. (Why? It requires CPU paints shadow and GPU cannot help here. read more here, and html5rocks).
If you are interested in Chromium implementation, text-shadow is done in TextPainter.cpp, painted by GraphicContext, which is done primarily by CPU. And animating text-shadow is a performance nightmare. Change this boost fps to 20+.
DOM Access
The last thing is DOM access, every frame update requires a dom insertion and, correspondingly, a dom removal by yet another timer. This is painful. I try to reduce DOM removal, so I added a container for each column. And adding container does add DOM complexity, I have to wait for the animation end to update the container. After all, it saves many dom manipulations, timers and closures. Furthermore I updated setTimeout to requestAnimationFrame so that browser can orchestra DOM access better.
Combining the above three, I got a 50+ fps, not as smooth as 60fps. Maybe I can further optimize it by reducing DOM insertion, where all characters in a column is inserted once, and for each character the animation-delay is at interval.
Looking on Canvas
Still, your animation is quite harsh job for DOM based implementation. Every column is updated, and text size varies frequently. If you really want the original matrix effect, try canvas out.

How can I do an SVG Path drawing animation when the stroke has a dasharray?

Looking to animate the below SVG, which I have got working initially. However, I want it to 'draw' the second SVG in the opposite direction, WITH the dots i've defined.
Is there any way I can do this? Effectively drawing my shape from left to right with the dots.
Codepen: http://codepen.io/anon/pen/gFcAz
The normal dash offset animation trick really only works with solid lines.
This is the closest I managed to get using CSS animations.
http://jsfiddle.net/L4zCY/
Unfortunately the dashes crawl because you have no control over the step rate of the stroke-dashoffset. If you could make it step by 10 at a time, the dashes wouldn't move.
So I think the only way around it is to use Javascript.
var path = document.querySelectorAll("svg path").item(0);
animateDashedPath(path);
/*
* Animates the given path element.
* Assumes the path has a "5 5" dash array.
*/
function animateDashedPath(path)
{
var pathLength = path.getTotalLength();
var animationDuration = 2000;
var numSteps = Math.round(pathLength / (5+5) + 1);
var stepDuration = animationDuration / numSteps;
// Build the dash array so we don't have to do it manually
var dasharray = [];
while (numSteps-- > 0) {
dasharray.push(5);
dasharray.push(5);
}
dasharray.push(pathLength);
// Animation start conditions
path.setAttribute("stroke-dasharray", dasharray.join(" "));
path.setAttribute("stroke-dashoffset", -pathLength);
// We use an interval timer to do each step of the animation
var interval = setInterval(dashanim, stepDuration);
function dashanim() {
pathLength -= (5+5);
path.setAttribute("stroke-dashoffset", -pathLength);
if (pathLength <= 0) {
clearInterval(interval);
}
}
}
Demo here
Update
It looks like there is an issue with in FF. If you create the "right" number of dashes for the path length, it doesn't quite reach the end of the path. You need to add extra.
A version of the demo that works properly on FF is here

Resources