I'm building an animated menu based off of Smooth as Butter: Achieving 60 FPS Animations with CSS3.
The canonical demo which I'm comparing my code against is: http://codepen.io/Onyros/pen/jAJxkW
This is my demo code:
var navLayer = document.querySelector('.nav-layer'),
open = document.querySelector('.open'),
close = document.querySelector('.close');
function toggleNav() {
navLayer.classList.add('nav-layer__animating');
if (navLayer.classList.contains('nav-layer__visible')) {
navLayer.classList.remove('nav-layer__visible');
} else {
navLayer.classList.add('nav-layer__visible');
}
}
open.addEventListener('click', toggleNav, false);
close.addEventListener('click', toggleNav, false);
navLayer.addEventListener('transitionend', function() {
navLayer.classList.remove('nav-layer__animating');
}, false);
.nav-layer {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
z-index: 2;
pointer-events: none;
}
.nav-layer__visible {
pointer-events: auto;
}
.header {
position: fixed;
top: 0;
left: 0;
background-color: beige;
height: 10vh;
width: 100vw;
z-index: 1;
}
.nav {
position: relative;
height: 100vh;
width: 90vw;
background-color: aquamarine;
z-index: 3;
transform: translateX(-91vw);
will-change: transform;
}
.nav-layer__animating .nav {
transition: all 300ms ease-in;
}
.nav-layer__visible.nav-layer__animating .nav {
transition: all 300ms ease-out;
}
.nav-layer__visible .nav {
transform: none;
}
<div class="nav-layer">
<nav class="nav">
<button class="close">Close</button>
</nav>
</div>
<header class="header"><button class="open">Menu</button></header>
<article>
<p>O hai</p>
</article>
When I run the canonical demo with paint flashing enabled in Chrome DevTools, I don't see any paint events.
When I run my demo, I see a flash of paint when I close the menu.
The event log in DevTools shows that there was a paint on #document and another on nav.nav.
The answer I'm looking for here is: find the CSS property in the canonical demo which is preventing this flash of paint. Or, maybe the difference is in how I've structured my HTML. I think the JS is equivalent, so it's unlikely to be there.
Please provide your methodology, too!
Related
I am trying to implement a simple Imageviewer that displays an image that fits screen and when clicked, the image shoud be shown in fullsize.
Everything almost works, but when image is displayed in full-size, there are no scrollbars, so I can only see the center of the image and not explore the rest.
const image = document.getElementById("image");
const imageContainer = document.getElementById("image-container");
image.addEventListener("click", function() {
if (image.classList.contains("full-size")) {
image.classList.remove("full-size");
imageContainer.classList.remove("full-size");
} else {
image.classList.add("full-size");
imageContainer.classList.add("full-size");
}
});
#image-container {
position: relative;
width: 100%;
overflow: hidden;
}
#image {
width: 100%;
height: auto;
transition: transform .5s;
cursor: zoom-in;
}
#image.full-size {
cursor: zoom-out;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: auto;
z-index: 10;
transform: scale(2);
}
#image-container.full-size {
overflow: auto;
}
<div id="image-container">
<img id="image" src="https://bigbookofamigahardware.com/bboah/media/download_photos/cybervisppc_4_big.jpg">
</div>
The implementaion can be seen here:
https://codepen.io/bobittjek/pen/qBMWpdy
I'm new to the web development world and wanted to know if there is a way to disable background scrolling.
I've tried z-index for the pop-up to display above all the elements, but some background content was getting overlapped with the pop-up.
I'm not much familiar with JS but was not able to get any help.
Below please find my code
body {
height: 200vh;
}
.bg-noscroll {
}
.overlay {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.7);
transition: opacity 500ms;
visibility: hidden;
opacity: 0;
}
.overlay:target {
visibility: visible;
opacity: 1;
}
.popup {
transform: translateY(-60px);
margin: 70px auto;
padding: 20px;
background: #fff;
border-radius: 5px;
width: 30%;
position: relative;
transition: all 5s ease-in-out;
}
.popup .close {
position: absolute;
top: 20px;
right: 30px;
transition: all 200ms;
font-size: 30px;
font-weight: bold;
text-decoration: none;
color: #333;
}
.content {
height: 250px;
}
.popup .content {
overflow-y: scroll;
}
#media screen and (max-width: 700px){
.popup{
width: 70%;
}
<body class="bg-noscroll bg-scroll">
<span><a class="popupBG-Disable" href="#popup">Full Recipe</a></span>
<div id="popup" class="overlay">
<div class="popup">
<h3>Foxtail Millet Porridge:</h3>
<a class="close" href="#">×</a>
<div class="content">
<span>Ingredients:<br>here are some things that you'd use to make this<br> isn't this amazing?<br>Yes, it is!<br>
this is getting loooooong<br>this will take me a while!<br>oh... yes it will<br>we're getting close<br>and we should be there <br>or not...<br>Im losing hope<br>and patience<br>with how long this is taking<br>I could really cry<br>
but we'll get there soon<br>safe and sound<br>free as pie<br>I dont know what I meant by that<br>
this is taking long mannnn<br>
</span>
Thank you for your help!
I have a live codepen with your original code so you can just copy and paste if you wish.
Using Jquery, we can enable and disable overflow using some simple code:
const modal = document.querySelector("#btn");
const body = document.querySelector("body");
const showModal = function (e) {
modal.classList.toggle("hidden");
if (!modal.classList.contains("hidden")) {
body.style.overflow = "hidden";
} else {
body.style.overflow = "hidden";
}
}; // just reversed for re-enabling scroll, as seen in the codepen
Currently, you have to make use of javascript and add or remove the scrollbar-properties or css-class using a hashchange event-listener for example:
window.addEventListener("hashchange", event => {
const newHash = new URL(event.newURL).hash,
el = document.getElementById(newHash.substr(1));
if (el && el.classList && el.classList.contains("overlay")) {
document.body.style.overflow = "hidden";
// or document.body.classList.add("bg-noscroll");
} else {
document.body.style.overflow = "";
// or document.body.classList.remove("bg-noscroll");
}
});
Starting from chromium 101 the support for the :has()-selector has been implemented (experimental flag only) and the current chromium 105 dev channel brings the :has()-selector enabled by default.
With the has()-selector it will be possible using:
body:has(.overlay:target) {
overflow: hidden;
}
Keep also mind, it may take some more time for other browsers to implement the has()-selector. Therefor the best would be to stick with the javascript method for a while.
I have a block over a text and i want to reveal the text and make the width of the block zero
I'm using gsap but as far as i know it is bad for performance to animate width,
and since i'm gonna use this animation quite a lot i'm worried to animate width
so is there is a better solution for my little problem ?
gsap.to('.block', {
duration: 1, width: 0, ease: Power4.easeIn}, 0.2);
h1 {
position: relative;
display: inline-block
}
.block {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
background: black;
width: 100%;
height: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.6/gsap.min.js"></script>
<h1>
hello world
<span class="block"></span>
</h1>
You can consider an animation using transform and have better performance
gsap.to('.block', {
duration: 1, transform: 'translateX(-100%)', ease: Power4.easeIn}, 0.2);
h1 {
position: relative;
display: inline-block;
overflow:hidden;
}
.block {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
background: black;
width: 100%;
height: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.6/gsap.min.js"></script>
<h1>
hello world
<span class="block"></span>
</h1>
The only thing I can think of to increase the performance while still using Gsap is to lower the duration of the width transition of the block, as well as lower the fps of the animation by adding gsap.ticker.fps('framerate') to the javascript.
the default framerate is 60 fps, so try changing it to anything lower than that. Though I'm not entirely sure if that would make a huge improvement.
gsap.ticker.fps(24);
//caps framerate at 24
gsap.to('.block', {duration: 0.5, width: 0, ease: Power4.easeIn}, 0.2);
h1 {
position: relative;
display: inline-block;
}
.block {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
background: black;
width: 100%;
height: 100%;
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.6/gsap.min.js">
</script>
<h1>
hello world
<span class="block"></span>
</h1>
</body>
</html>
There definitely is a solution in animation properties of CSS. Using CSS instead of
JavaScript (JS) will delegate computing the animated values to the browser's layout engine instead of re-implementing them in your code.
At a high level your animation will be a from, to state where the from is full
width and to is 0 width. You express this with a #keyframe rule where you can animate ANY CSS property. This way you can combine #keyframes on different properties.
These are some good introductory notes about animations in CSS css-animation-101, using CSS animations
For you particular use case something like this should do
#keyframes unveil {
from {
width: 100%;
}
to {
width: 0%;
}
}
.block {
animation-name: unveil;
animation-duration: 1s;
animation-timing-function: ease-in;
}
Then you can either add the block class to the span directly in the HTML or use some JavaScript to toggle/add the class in response to an event.
You can use css animations:
function toggleAnimation() {
$('.block').toggleClass("animated");
}
h1 {
position: relative;
display: inline-block
}
.block {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
background: black;
width: 100%;
height: 100%;
}
#keyframes custom-animation {
from {
width: 100%;
}
to {
width: 0%;
}
}
.block.animated {
animation: custom-animation 1s ease-in 0.5s 1 normal forwards;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1>
hello world
<span class="block"></span>
</h1>
<button onclick="toggleAnimation();">Toggle animation</button>
In my sample, the block needs the animated class to actually animates. For the demo, I used a button to add this class.
I have a video tag that I want to play continuously while a user can simultaneously do stuff on the site. However I have found that if the background transitions between background images that the video starts buffering. I have a runnable example in the snippet below.
Note: The buffering does not seem to occur if the snippet is run normally, but does occur if you put the snippet in 'full page'.
function changeBackground() {
const randomColor = '#'+Math.floor(Math.random()*16777215).toString(16);
const element = document.getElementById('background');
const currentOpacity = element.style.opacity;
const currentBackground = element.style.backgroundImage;
switch (currentBackground) {
case 'url("https://cdn.freebiesupply.com/logos/large/2x/stackoverflow-com-logo-png-transparent.png")': {
element.style.backgroundImage = 'url("https://i5.walmartimages.ca/images/Large/428/5_r/6000195494285_R.jpg")';
break;
}
case 'url("https://i5.walmartimages.ca/images/Large/428/5_r/6000195494285_R.jpg")': {
element.style.backgroundImage = 'url("https://cdn.freebiesupply.com/logos/large/2x/stackoverflow-com-logo-png-transparent.png")';
break;
}
default: {
break;
}
}
}
const element = document.getElementById('background');
element.style.backgroundImage = 'url("https://cdn.freebiesupply.com/logos/large/2x/stackoverflow-com-logo-png-transparent.png")'
#background {
display: flex;
position: absolute;
width: 100%;
height: 100%;
z-index: -10;
background-size: contain;
transition: background-image 3s ease-in-out;
}
#button {
display: flex;
width: 200px;
height: 100px;
background-color: orange;
text-align: center;
}
#video {
display: flex;
position: absolute;
top: 0;
right: 0;
width: 400px;
height: 350px;
}
<div id='root' style='width: 100%; height: 500px'>
<div id='background'></div>
<div id='button' onClick="changeBackground()">Click me to change the background!</div>
<video
id='video'
autoplay
muted
loop
controls
src="https://s3.amazonaws.com/codecademy-content/courses/React/react_video-cute.mp4"/>
</div>
What could be the cause and is there a way to prevent the video from buffering while still having a background image transition?
Edit: May be important to add that I'm on Chrome on MacOS.
Edit 2: From the responses I have gathered not everyone can reproduce the problem, so I went to an old-timey windows PC and tried it there. Found out that the background-transition was being really slow and laggy but the video kept playing without problem. It also works on safari on MacOS so this appears to be a Chrome MacOS-only problem.
I have to admit I'm not entirely sure of what happens here... Given it's not a 100% repro case, it's also hard to be sure any workaround actually works...
But here are some comments and ideas.
It seems this happens only with .mp4 files. I could reproduce with other .mp4 videos but never with any .webm file.
So one thing you may want to try is to reencode your video in webm, it could be that Chrome's mp4 decoder has some issues.
It seems that CSS animations do not cause this issue. So you could rewrite your transition code into a CSS animation, with the major problem that you won't be able to stop it in the middle (but it seems background-transitions are bad at this anyway).
function changeBackground() {
const element = document.getElementById('background');
if(element.classList.contains('apple')) {
element.classList.remove('apple');
element.classList.add('so');
}
else {
element.classList.add('apple');
element.classList.remove('so');
}
}
#background {
display: flex;
position: absolute;
width: 100%;
height: 100%;
z-index: -10;
background-size: contain;
background-image: url("https://cdn.freebiesupply.com/logos/large/2x/stackoverflow-com-logo-png-transparent.png");
}
#background.apple {
animation: apple-to-SO 3s ease-in-out forwards;
}
#background.so {
animation: SO-to-apple 3s ease-in-out forwards;
}
#keyframes apple-to-SO {
from {
background-image: url("https://i5.walmartimages.ca/images/Large/428/5_r/6000195494285_R.jpg")
}
to {
background-image: url("https://cdn.freebiesupply.com/logos/large/2x/stackoverflow-com-logo-png-transparent.png");
}
}
#keyframes SO-to-apple {
from {
background-image: url("https://cdn.freebiesupply.com/logos/large/2x/stackoverflow-com-logo-png-transparent.png");
}
to {
background-image: url("https://i5.walmartimages.ca/images/Large/428/5_r/6000195494285_R.jpg")
}
}
#button {
display: flex;
width: 200px;
height: 100px;
background-color: orange;
text-align: center;
}
#video {
display: flex;
position: absolute;
top: 0;
right: 0;
width: 400px;
height: 350px;
}
<div id='root' style='width: 100%; height: 500px'>
<div id='background'></div>
<div id='button' onClick="changeBackground()">Click me to change the background!</div>
<video
id='video'
autoplay
muted
loop
controls
src="https://s3.amazonaws.com/codecademy-content/courses/React/react_video-cute.mp4"/>
</div>
Now if you prefer js control, it seems that Web-Animations aren't affected either.
let current = 1;
const urls = [
"https://cdn.freebiesupply.com/logos/large/2x/stackoverflow-com-logo-png-transparent.png",
"https://i5.walmartimages.ca/images/Large/428/5_r/6000195494285_R.jpg"
];
function changeBackground() {
const element = document.getElementById('background');
element.animate({
backgroundImage: ['url(' + urls[current] + ')', 'url(' + urls[+(!current)] + ')']
}
, {
duration: 3000,
iterations: 1,
fill: 'both'
}
);
current = (current + 1) % 2;
}
#background {
display: flex;
position: absolute;
width: 100%;
height: 100%;
z-index: -10;
background-size: contain;
background-image: url(https://cdn.freebiesupply.com/logos/large/2x/stackoverflow-com-logo-png-transparent.png);
}
#button {
display: flex;
width: 200px;
height: 100px;
background-color: orange;
text-align: center;
}
#video {
display: flex;
position: absolute;
top: 0;
right: 0;
width: 400px;
height: 350px;
}
<div id='root' style='width: 100%; height: 500px'>
<div id='background'></div>
<div id='button' onClick="changeBackground()">Click me to change the background!</div>
<video
id='video'
autoplay
muted
loop
controls
src="https://s3.amazonaws.com/codecademy-content/courses/React/react_video-cute.mp4"/>
</div>
Try to enable / disable hardware-acceleration on Chrome.
I can't reproduce the problem so it might be an issue with your ISP and latency (I don't have that problem at 145 Mbps). Looking at your code it isn't very efficient with that switch. The demo below uses an array (added another image as well). BTW add flex to elements that contain children elements otherwise it's pointless.
document.getElementById('button').onclick = changeBackground;
let i = 0;
function changeBackground(e) {
const images = ["https://cdn.freebiesupply.com/logos/large/2x/stackoverflow-com-logo-png-transparent.png", "https://i5.walmartimages.ca/images/Large/428/5_r/6000195494285_R.jpg", "https://media.istockphoto.com/vectors/circuit-board-seamless-pattern-vector-background-microchip-technology-vector-id1018272944"];
const background = document.getElementById('background');
i++;
if (i >= images.length) {
i = 0;
}
background.style.backgroundImage = `url(${images[i]})`;
return false;
}
#background {
position: absolute;
width: 100%;
height: 100%;
z-index: -10;
background-size: contain;
background-image: url(https://cdn.freebiesupply.com/logos/large/2x/stackoverflow-com-logo-png-transparent.png);
transition: background-image 3s ease-in-out;
background-repeat: repeat-x;
}
#button {
width: 200px;
height: 100px;
background-color: orange;
text-align: center;
}
#video {
position: absolute;
top: 0;
right: 0;
width: 400px;
height: 350px;
}
<div id='root' style='width: 100%; height: 500px'>
<div id='background'></div>
<div id='button'>Click me to change the background!</div>
<video id='video' autoplay muted loop controls src="https://s3.amazonaws.com/codecademy-content/courses/React/react_video-cute.mp4"></video>
</div>
This question is interesting.
I think the buffer may be due to
This line Math.floor(Math.random()*16777215).toString(16);
also about the video resolution and to what extent it has been resized
also may be due to some unnecessary codes if any.
i am looking for this kind of template . Moving the page to left and then page to right. Can anyone tell me how can i make this or is there any javascript example similar to this.
Create two <div>s, put them next to each other, make them take up the whole window, and change them as needed.
HTML:
<div class="left">left</div>
<div class="right">right</div>
CSS:
body {
margin: 0;
}
.left {
background-color: green;
bottom: 0;
left: 0;
position: fixed;
top: 0;
transition: width 1s;
width: 0;
}
.left.active {
width: 200px;
}
.right {
background-color: red;
bottom: 0;
left: 0;
position: fixed;
right: 0;
top: 0;
transition: left 1s;
}
.right.active {
left: 200px;
}
JS (width jQuery):
$('.right').on('click', function() {
$('.left').toggleClass('active');
$('.right').toggleClass('active');
});
And here's a fiddle.
Using .toggle(effect,options,duration) method to moving the page to left to right.
// Set the effect type
var effect = 'slide';
// Set the options for the effect type chosen
var options = { direction: 'right' };
// Set the duration (default: 400 milliseconds)
var duration = 700;
$('#Id').toggle(effect, options, duration);
Taken via this link
If you want it to animate smooth on all devices you should use css transitions and transforms. Hiding and showing would be as basic as toggling a class then.
The example in jsfiddle
<style media="screen">
.wrapper {
width: 100%;
overflow: hidden;
}
.menu {
height: 100vh;
width: 100px;
background: #ABC;
color: white;
position: absolute;
left:0;
transition: transform 0.3s;
transform: translateX(-100px);
}
.content {
transition: transform 0.3s;
}
.active .menu {
transform: translateX(0);
}
.active .content {
transform: translateX(100px);
}
</style>
<button class="toggle">Toggle</button>
<div class="wrapper">
<div class="menu">
My menu
</div>
<div class="content">
My content
</div>
</div>
<script type="text/javascript">
document.querySelector('.toggle').addEventListener('click', function(event) {
event.preventDefault();
document.querySelector('.wrapper').classList.toggle("active");
});
</script>
NB! Supported from IE10. IE 9 will support without the animation and you probably should add the needed -ms-, -webkit-, -moz-, etc prefixes to support the older browsers if needed for transition and transform properties.
Also I advise not animating body or html with this method and put the content of page in the wrapper (in .content in the examples case). Moving body and html directly may lead to unpleasant surprises later.