I have a text element that uses a font size of 7vw and sizes correctly with IE. I want to play an animation on it that changes font-size over time from 7vw to 10vw and back to 7vw, but the 7vw in the keyframe animation is at least 10 times as big as the 7vw already applied to the element.
I have tried to use other units like em, pt and %. I have also tried setting body font-size in order to use the em unit. Furthermore I tried to parent it to other elements in the HTML, but to no avail.
.text-bot {
padding-top: 10vw;
font: bold 7vw Calibri;
text-transform: uppercase;
color: #009999;
z-index: 50;
-webkit-animation: text-bounce 1s linear;
-moz-animation: text-bounce 1s linear;
animation: text-bounce 1s linear;
}
#keyframes text-bounce {
0% {
font-size: 7vw;
}
10% {
font-size: 12vw;
}
100% {
font-size: 7vw;
}
}
<b class="text-bot anim-text" id="count">0</b>
I expected the 7vw to remain 7vw given the fact that the animation is applying it to the same element. What happens instead is that the 7vw from the animation jumps out of all bounds, more than 10 times the expected size.
If I remove the animation from .text-bot it is all sized correctly and working well.
This is all in IE, Chrome was easy to setup (I also have webkit keyframes identical to the IE ones).
Sizing up the window while the animation is playing makes the text get bigger and bigger until it hits a point and it starts from the smallest size and continues to get bigger again in a loop (Depending on how much I increase the window size). Something like small, medium, large, xlarge, small, medium, large, xlarge, small, etc, dependent on how large the browser window is.
Note: Try the script in Chrome and then compare in IE to see the difference. The IE one jumps in size.
I could not get it to work with css. I tried zoom, but that was bugged in IE too. I solved it with the following JQuery code:
$('#count').stop().animate({ fontSize: '8vw' }, { duration: 100 })
.animate({ fontSize: '7vw' }, {
duration: 900
});
I am still interested in a keyframe solution.
Related
I'm having a strange problem where rendering an emoji rotated to certain angles results in the emoji failing to appear.
This seems consistent across browsers, so I'm struggling to pinpoint the issue or a reasonable solution.
The code:
<style type="text/css">
.container {
background-color: #55d;
height: 500px;
padding: 50px;
width: 500px;
}
.text {
color: #fff;
font-size:2em;
margin: 100px;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
transform: rotate(45deg);
}
</style>
<div class="container">
<div class="text">This is some text 😂</div>
</div>
See http://codepen.io/anon/pen/ORgQjb for a working example, note that changing the rotation, even to 44.5 degrees will bring the emoji back.
Does anyone have a suggestion as to why this occurs, or any workarounds?
Update
Thanks to Paulie_D and some digging, it seems this issue only manifests itself on OSX (all browsers), and not Windows (tried IE/Firefox/Chrome).
I sure don't know why it happens, but after some tinkering, I do have a couple of fixes to share.
Webkit
If a Webkit-only fix is sufficient (e.g., if you're building an Electron app), you only need to add the CSS transform perspective(0), which has no visible effect, other than causing emoji to actually render.
So:
transform: rotate(45deg) perspective(0);
instead of:
transform: rotate(45deg);
Here's a fork of your example demonstrating that fix:
https://codepen.io/troywarr/pen/yEgEdr
and a reduced test case including a reference emoji to illustrate that perspective(0) doesn't change the emoji's appearance:
https://codepen.io/troywarr/pen/aKpKmx
Cross-browser
If you need a cross-browser fix, you can use a CSS animation that rotates starting at 45 degrees (or whichever multiple of 45 degrees that you need to fix) but is eternally paused:
#keyframes spin {
0% {
transform: rotate(45deg);
}
}
.working-rotated-thing {
animation: spin 1ms; /* animation-duration must be > 0 */
animation-play-state: paused;
}
Here's a fork of your example demonstrating that fix (note that I enabled Autoprefixer to avoid messing with vendor prefixes):
https://codepen.io/troywarr/pen/mKRKZB
and a reduced test case:
https://codepen.io/troywarr/pen/oyByMx
This seems to work across browsers; I checked the latest Chrome, Firefox, and Safari in macOS High Sierra, and all were well.
So, for my club calendar I'm trying to make a transition play where the td's will get bigger when hovered over. However, when I do, although the text inside gets bigger, the box doesn't really grow. Also, the colored boxes have this strange after-effect where the top and/or bottom portion stays bigger. Here is the fiddle (I'm new to this, so if the link doesn't work, I apologize):
http://jsfiddle.net/glenohumeral13/Lcs68/
And here is, what I think, is the relevant code:
td {
width: 35px;
text-align:center;
font-family: Helvetica, Arial;
font-size: 16px;
font-weight: 100;
transition: transform 1s ease-in;
}
td:hover {
-ms-transform: scale(1.2,1.2);
-webkit-transform: scale(1.2,1.2);
-moz-transform: scale(1.2,1.2);
transform: scale(1.2,1.2);
}
I'd really appreciate if, if you can fix it, you could also take the time why the solution works because I want to be able to fix this, and related issues, in the future by myself. Thanks!
Edit: Thanks everyone for your speedy responses! I tried all the solutions, but the boxes just go white once the transition is over. Could this just be my computer in particular for some reason?
Edit: Nevermind: problem solved! Thanks everybody!
Last edit: Sorry! I was using Chrome, if it helps anybody now.
LIVE DEMO
Update:
Nowadays for modern browser (>= IE10) you don't need to prefix the property of transform in the transition declaration.
td {
width: 35px;
text-align: center;
font-family: Helvetica, Arial;
font-size: 16px;
font-weight: 100;
transition: /*transform*/ 1s ease-in;
}
(The problem was solve by removing transform so it get the default value all)
... transition: all 1s ease-in; ...
I'd like a 60fps or better opacity transition and am testing with Chrome dev tools timeline. I've created a jsfiddle (http://jsfiddle.net/jnoody/Zt8nd/) to demonstrate, but basically it's just a div that has a class toggled every few seconds and transitions on opacity for the two states. It looks fine to the eye, but I've been led to believe 60fps should be achievable.
HTML:
<div id="fader"></div>
CSS:
#fader {
height: 1000px;
width: 1000px;
background-color: grey;
opacity: 1;
transition: 1s opacity;
}
#fader.fadeOut {
opacity: 0;
transition: 1s opacity;
}
JavaScript:
var fader = document.getElementById('fader');
setInterval(function () {
fader.classList.toggle('fadeOut');
}, 2000);
I've tried the null transform hack, too, but either way it appears to be in its own layer (using "render composited border layers").
Not really sure if that helps, but I do get 60fps in the fiddle, going full window (that is appending /show to the fiddle)
There is only one moment where the frame rate drops, but I believe it's when the javascript executes (so, nothing to do with posible GPU acceleration)
Trying to get font-weight to gracefully transition from '400' to '600' using CSS but it doesn't appear to be working in Chrome. Is this a known bug or am I doing something wrong?
Check the Fiddle here for an example
The problem is that font weights, when represented numerically, must be a multiple of 100. To animate between 400 and 600, the font would change from 400 to 500 to 600 (3 'frames', if you like) and wouldn't look very smooth. An animation wouldn't increment the weight by 1 each time (400, 401, 402...) it would increment the weight by 100 (400, 500, 600). If your animation lasted 2 seconds, after 1 second the weight would suddenly become 500, and after 2 seconds the weight would suddenly become 600; there are no in-between variations.
A further problem with what you're attempting here is that the font you're using (or JSFiddle's default, at least) doesn't have anything different for font-weight:500, meaning it defaults to 400:
<p style="font-weight:400;">a - 400, normal</p>
<p style="font-weight:500;">a - 500 (no support, defaults to 400)</p>
<p style="font-weight:600;">a - 600 (bold)</p>
<p style="font-weight:650;">a - 650 (not valid, defaults to 400)</p>
http://jsfiddle.net/r4gDh/6/
Numeric font weights for fonts that provide more than just normal and bold. If the exact weight given is unavailable, then 600-900 use the closest available darker weight (or, if there is none, the closest available lighter weight), and 100-500 use the closest available lighter weight (or, if there is none, the closest available darker weight). This means that for fonts that provide only normal and bold, 100-500 are normal, and 600-900 are bold.
https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight
This basically means that you can't smoothly animate font-weight. Even if you had support for all weights between 100 and 900 the change wouldn't be pleasant and there would be a dramatic change between 500 and 600 (where lighter weight meets darker weight).
I came here to find out the answer myself for how to transition font weight, and was disappointed when I read the approved answer above saying that it can't be done (or at least not very well).
With font-weight animation unavailable, I decided to try another effect, which actually gives you a font-weight effect... which I didn't even think would work for this type of transition.
Here is how to make the weight grow:
.weightGrow:hover {
text-shadow:
-1px -1px 0 #2DD785,
1px -1px 0 #2DD785,
-1px 1px 0 #2DD785,
1px 1px 0 #2DD785;
-webkit-transition: all .5s;
-moz-transition: all .5s;
-o-transition: all .5s;
transition: all .5s;
}
Perfectly smooth and exactly what I was looking for when I first arrived on this page. Hope it helps someone.
Font-weight animation is currently not supported in Chrome and IE-10 based on numerous tests. This may change in the future.
Fonts are not simple vector image collections (that's why svg fonts never took off). Opentype fonts include all kinds of magic to clamp fonts to the pixel grid, which makes it unrealistic to expect a font family to ever include every possible weight value.
Because fonts are not simple vector image collections they will also never resize linearly – there will be bumps to take the pixel grid into account.
This Google whitepaper explains why linear resizing does not work for text on current screens
https://docs.google.com/document/d/1wpzgGMqXgit6FBVaO76epnnFC_rQPdVKswrDQWyqO1M/edit
which is why no browser will attempt it and they will all rely on pre-computed font weights.
That may change in a decade when retina displays are the lowest common denominator but current font tech targets current screens.
This Microsoft whitepaper documents some standard font weight values
http://blogs.msdn.com/cfs-filesystemfile.ashx/__key/communityserver-components-postattachments/00-02-24-90-36/WPF-Font-Selection-Model.pdf
(but just because they are documented does not mean the number of fonts that actually include all of them is not quite small)
You can probably achieve the effect you want with an svg font and some javascript. The svg font will be crap for text use though.
I seem to have obtained a "font-weight" transition effect accidentally, by doing a quick color transition (light gray to dark blue) at the same time.
Although there is no direct way to get the font-weight to smoothly transition, I have found a way to do this indirectly (although with some limitations).
In essence you can simply use one of the pseudo elements in order to make a mirror copy of the element whom font you want to transition to bold, where the font-weight on the pseudo element is bold and the opacity is 0. And on hover simply transition the pseudo element's opacity and that does the trick with a very smooth transition.
You can see a demo here:
http://jsfiddle.net/r4gDh/45/
HTML:
<div class="element" data-text="text will turn to bold on hover">
text will turn to bold on hover
</div>
In the HTML you can already see one of the limitations, because we are using pseudo elements we need to pass the contents of the element as an attribute as well, so the content is duplicated.
CSS:
.element,
.element::before {
background: red;
font-weight:normal;
font-size:40px;
text-align:center;
display: inline-block;
padding: 0px 30px 0px 30px;
position: relative;
top: 0;
left: 0;
}
.element::before {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 0;
content: attr(data-text);
opacity: 0;
font-weight: bold;
transition: all 1s linear;
}
.element:hover::before {
opacity: 1;
}
The second limitation comes from the nature of the technique, namely because you are simply overlaying the pseudo element over the original, then the element needs to have a solid background or this will not work, because the original element will remain visible in the background.
One thing you should really keep an eye on is that the pseudo element and the original element have the same dimensions, to this end, in the demo, I've removed the padding on the pseudo element, so you may need to do something similar.
As you can see, this way of getting the font-weight transition is not without it's problems and far from ideal, but this is the best I can currently think of and in limited cases like for buttons and what not this is quite useful and if done correctly will look decent even in legacy browsers.
Smooth font-weight transitions and animations are possible using variable fonts which are not limited to increments of 100. More info on variable fonts here: https://web.dev/variable-fonts/
Try this snippet with eg. https://fonts.google.com/specimen/Raleway
#font-face {
font-family: 'Raleway Flex';
src: url('Raleway-VariableFont_wght.ttf');
font-weight: 100 900;
}
h1 {
font-family: 'Raleway Flex', sans-serif;
animation: example 1s linear 0s infinite alternate both;
}
#keyframes example {
from {
font-weight: 100;
}
to {
font-weight: 900;
}
}
<h1>HELLO</h1>
<p>I look way smoother if you use a variable font.</p>
I've tweaked #Idra's fiddle to make the normal to bold transition a bit smoother. Here's the fiddle: http://jsfiddle.net/y7rLwx95/
Changes... Since the text width will get wider when going to bold, I've added an initial "stretch" transition by increasing the letter spacing:
.element:hover {
letter-spacing: 0.9px;
transition: all .3s linear;
}
Then delayed the fading in of the bold ::before element:
.element:hover::before {
opacity: 1;
transition-delay: .2s
}
Also some additional tweaks here:
.element::before {
transition: all .3s linear; /* replace */
letter-spacing: 0; /* additional */
}
The transition timing is just whatever feels right to me. The original idea #Idra posted is significant to me. I accept that fact that the widths should be different between normal and bold, as that's the intent of different font weights. So really the challenge, IMHO, is to figure out how to go between the 2 looks in a smooth, no jarring way. This seems to be the best solution so far I've found.
I use following keyframe animation on several elements:
#keyframes redPulse {
from { background-color: #bc330d; box-shadow: 0 0 9px #333; }
50% { background-color: #e33100; box-shadow: 0 0 18px #e33100; }
to { background-color: #bc330d; box-shadow: 0 0 9px #333; }
}
#-webkit-keyframes redPulse {
from { background-color: #bc330d; box-shadow: 0 0 9px #333; }
50% { background-color: #e33100; box-shadow: 0 0 18px #e33100; }
to { background-color: #bc330d; box-shadow: 0 0 9px #333; }
}
.event_indicator {
display: inline-block;
background-color: red;
width: 5px;
margin-right: 5px;
-webkit-animation-name: redPulse;
-webkit-animation-duration: 1s;
-webkit-animation-iteration-count: infinite;
animation-name: redPulse;
animation-duration: 1s;
animation-iteration-count: infinite;
}
On my computer I am getting around 40% CPU usage both in Chrome and Firefox.
Is it the current state of animations (nice but not usable for now) or am I missing some magic property?
You can check the following sample with the same animation:
http://jsfiddle.net/Nrp6Q/
Yes, this is normal because you have several infinite-loop animations on the page. The CPU is therefore continually doing work while these elements are rendered. There is a "magic" property that will significantly cut-down the CPU usage and that is:
transform: translateZ(0);
This will composite the elements into their own layers (by tricking the browser into thinking it will be doing 3D transforms) and the browser should, in most cases, take advantage of GPU acceleration, lessening the burden on the CPU. For me this cut it down by about 20% (almost half).
To read more about this technique take a look at: http://ariya.blogspot.com/2011/07/fluid-animation-with-accelerated.html
Additionally, the more keyframes you have in the animation, the more taxing it will be as well. Just try the animation with the middle keyframe cut out and you will see another substantial (~10-12%) drop in CPU usage.
Lastly, not all properties are equal -- box-shadow is much harder for the browser to animate smoothly than, say, background-color. Leaving all of the keyframes intact but dropping the box-shadow property, using the translateZ(0) trick had my CPU usage hovered at only 10-11%.
As much as it pains me to say this, for infinite-loop animations an animated .gif is going to perform much, much better than CSS3 in the current state of browser animation, especially if you plan for many of them to remain rendered on the page for some time.
Update 2017:
For those still finding their way to this question and answer, translate3d(0, 0, 0) provides the same benefit as translateZ(0), you're just also setting translateX() and translateY() at the same time. Please ignore the comment by #Farside as he uses translate3d(X, Y, Z) in his demo but does not compare it to translate(X, Y), which would show that using this technique still makes a significant difference.
According to this question, some people have found better performance across all browsers, especially Chrome, with transform: rotateZ(360deg).
One of the possible ways to reduce the load on CPU, is to use a so called null transform hack, which is often hailed as something of a silver bullet. In many cases it will drastically improve rendering performance in WebKit and Blink browsers like Chrome, Opera and Safari.
Usage of the "Null transform hack" (a hardware compositing mode)
The null transform hack basically does two things:
It switches on the hardware compositing mode (assuming it's supported for the platform)
It creates a new layer with its own backing surface
To "force" a browser, simply add one of these CSS properties to the element:
transform: translateZ(0);
/* or its friend: */
transform: translate3d(0, 0, 0);
When working with 3D transforms, it's good to have these properties as well to improve the performance:
backface-visibility: hidden;
perspective: 1000;
Caveats of the "null transform hack"
Enabling a hardware acceleration in CSS3 for a lot of objects may slow down performance!
Apparently, each null 3D transform creates a new layer. However, force-hacking layer creation may not always be the solution to certain performance bottlenecks on a page. Layer creation techniques can boost page speed, but they come with a cost: they take up memory in system RAM and on the GPU. So even if the GPU does a good job, the transfer of many objects might be a problem so that using GPU acceleration might not be worth it. The cite from W3C:
However, setting up the element in a fresh layer is a relatively expensive operation, which can delay the start of a transform animation by a noticeable fraction of a second.
Moving a few big objects has a higher performance, than moving lots of small items when using 3D-acceleration. So they must be used wisely and you need to make sure that hardware-accelerating your operation will really help the performance of your page, and that a performance bottleneck is not being caused by another operation on your page.
Moreover, a GPU is designed specifically for performing the complex mathematical/geometric calculations, and offloading operations onto the GPU can yield massive power consumption. Obviously, when hardware kicks in, so does the battery of the target device.
The modern way: the will-change property
The progress is not standing on the one place... W3C introduced the will-change CSS property. To cut the long story short, the will-change property allows you to inform the browser ahead of time of what kinds of changes you are likely to make to an element, so that it can set up the appropriate optimizations before they're needed.
Here's what they say in the the draft:
The will-change property defined in this specification allows an author to declare ahead-of-time what properties are likely to change in the future, so the UA can set up the appropriate optimizations some time before they’re needed. This way, when the actual change happens, the page updates in a snappy manner.
Using will-change, hinting to the browser about an upcoming transformation can be as simple as adding this rule to the element that you’re expecting to be transformed:
will-change: transform;
When developing for mobile, developers are forced to take the wide array of device constraints into consideration while writing mobile web apps. Browsers are becoming smarter, and sometimes, it's better to leave the decision to the platform itself, instead of overlapping acceleration and forcing the behavior in a hacky-way.
I had a similar case of high CPU usage when animating some elements with CSS3. I was animating the "left"-property of ~7 elements, with some opacity- and shadow-properties used in my whole page. I decided to switch to jQuery.animate, which sadly didn't improve the performance at all. My CPU (i7) was still at ~9-15% while displaying the page, several tricks (translateZ, etc) didn't really improve the performance either - while having my layout messed up (some absolute-positioned elements were involved, ouch!).
Then I stumbled upon this wonderful extension: http://playground.benbarnett.net/jquery-animate-enhanced/
I simply referenced the .js-file, didn't make a single change at the jQuery transitions, and my CPU usage is now 1-2% on the very same page.
My recommendation: when facing CPU issues using CSS3 transitions, switch to jQuery + the animate-enhanced-plugin.
You can also use this on any of the following class elements where you want to use GPU instead of CPU
.no-cpu {
transform: translateZ(0);
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
}
<element class="event_indicator no-cpu">animation...</element >
To a particular case of 'pulsing' background animation, reported here, I've come up with a css+js solution.
In my case the background animation was on background-position property rather than on the background-color, but the principle is the same.
Ok, let's say you have a block with a particular background:
<div class="nice-block">...</div>
Let's style it: (scss)
.nice-block {
background-color: red;
//or it can be: background: linear-gradient(45deg, #red, #white, #red);
//and: background-size: 600% 600%;
//the transform and will-change properties
//are here to only enable GPU
transform: translateZ(0);
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
will-change: transform;
transition: background-color 5s ease;
//if you want to add a pulsing effect
//to a gradient, see the following two lines:
// background-position: 0% 50%!important;
// transition: background-position 5s ease;
&.animated {
background-color: white;
//and in case of gradient animation:
// background-position: 100% 50%!important;
}
}
Now it's time to make the effect happen by adding a class 'animated' to the block with some JavaScript:
var bgAnimateTimer;
function animateBg () {
clearTimeout(bgAnimateTimer);
bgAnimateTimer = setTimeout(function () {
clearTimeout(bgAnimateTimer);
bgAnimateTimer = setTimeout(function () {
document.querySelector('.nice-block').classList.toggle('animated');
//jQuery alternative is:
// $('.nice-block').toggleClass('animated');
animateBg ();
}, 5000); //5 seconds for the animation effect
}, 2500); //2.5 seconds between each animation
}
animateBg ();
This improved performace in my case by ~15 times.
(i) Note to calculate seconds for transition and timeouts correctly both in css and js if you want values different from 5 seconds.
Besides all these answers, I've found another way to optimize it. The answer is simply to change animation Xs inifinite linear to animation Xs inifinite steps(20,end). Or even steps(60,end).
The rationale is that the web is doing very advanced calculation to calculate the animation from one state to another endlessly (and they try to smooth things out). By applying a step function, it cuts a (close to) continuous transition to 20 or 60 different separate states. Theoretically it will provide less smooth result compared with linear, but however, if you get a step function to 60, human eyes could tell no difference among a lot of animations.
I hope this helps!
Animate endless loops with JS instead of CSS infinite to increase CPU perfomance
Method 1 - Change animation to ''
#keyframes comet4Translate {
0% {
transform: translateX(0%) translateY(-50%);
}
100% {
transform: translateX(100%) translateY(100%);
}
}
#keyframes comet1Opacity {
0% {
opacity:1;
}
100% {
opacity:0;
}
}
#XMLID_640_{
transform: scale(.8) translateX(-20%) translateY(-20%) translateZ(0);
animation: comet4Translate 7.5s 3s ease-in-out alternate, comet1Opacity 8s .5s ease-in-out alternate;
will-change: transform;
will-change: opacity;
}
createAnimation = (selector, intervalDelay) => {
setInterval(() => {
$(selector).css('animation', 'none')
setTimeout(() => {
$(selector).css('animation', '')
}, 3000)
}, intervalDelay)
}
// first comet
createAnimation('#XMLID_640_', 16000)
Method 2 - Use transition instead of animation
#XMLID_640_{
transform: scale(.8) translateX(-20%) translateY(-20%) translateZ(0);
animation: comet4Translate 7.5s 3s ease-in-out alternate, comet1Opacity 8s .5s ease-in-out infinite alternate;
will-change: transform;
will-change: opacity;
opacity: 1;
}
#XMLID_640_.animated {
transform: scale(.8) translateX(150%) translateY(150%) translateZ(0);
opacity:0;
}
let switcher
const animateViaJs = selector => {
setInterval(() => {
if (!switcher) {
switcher = true
$(selector).css('transition', 'opacity 3s ease-in-out, transform 4s ease-in-out')
} else {
switcher = false
$(selector).css('transition', 'none')
}
document.querySelector(selector).classList.toggle('animated')
}, 5000)
}
animateViaJs('#XMLID_640_')
Method 3 - AnimeJs
#XMLID_640_{
will-change: transform;
will-change: opacity;
translateZ: 0;
}
const createAnimeJsLoop = (selector, intervalTimer, cfg) => {
const tl = anime({ loop:true, targets: selector, easing: 'easeInQuad', ...cfg })
setInterval(() => {
tl.pause()
setTimeout(() => tl.restart(), 5000)
}, intervalTimer)
}
createAnimeJsLoop('#XMLID_640_', 30000, {
opacity: [
{ value: 1, duration: 0 },
{ value: 0, duration: 2000 }
],
translateX: [
{ value: '-20%', duration: 0 },
{ value: '150%', duration: 3000 }
],
translateY: [
{ value: '-20%', duration: 0 },
{ value: '150%', duration: 3000 }
],
translateZ: 0
delay: 3000
})