How to make a 3D slick slider - javascript

Using slick, I'm trying to achieve a slider that looks like this:
In the above, the center slide is the default selected when slick initiates. Then, when an arrow is clicked, the slick-current class will shift onto a new div and css translate can be used to scale the image.
Here is my current code:
$(function(){
$("#downloadNow__slick").slick({
slidesToShow: 3,
// initialSlide: 2,
centerMode: true,
centerPadding: "53px",
arrows: true,
dots: false,
infinite: true,
cssEase: 'linear',
});
});
.downloadNow {
background: grey;
padding: 60px 0px;
&__wrapper {
position: relative;
}
.downloadNowCard{
background: white;
padding: 100px;
}
.slider {
max-width: 1110px;
margin: 0 auto;
}
.slick-track {
padding-top: 53px;
padding-bottom: 53px;
}
.slick-slide {
text-align: center;
transition: transform 0.3s ease-in-out;
}
.slick-slide.slick-current {
transform: scale(1.35);
position: relative;
z-index: 1;
}
.slick-slide img {
width: 100%;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.js" integrity="sha512-XtmMtDEcNz2j7ekrtHvOVR4iwwaD6o/FUJe6+Zq+HgcCsk3kj4uSQQR8weQ2QVj1o0Pk6PwYLohm206ZzNfubg==" crossorigin="anonymous"></script>
<section class="downloadNow">
<div class="downloadNow__wrapper">
<div class="downloadNow__slides" id="downloadNow__slick">
<div class="downloadNowCard">Card 1</div>
<div class="downloadNowCard">Card 2</div>
<div class="downloadNowCard">Card 3</div>
</div>
</div>
</section>
Current issues:
Cannot achieve row layout (flex on wrapper not playing nicely)
If I uncomment out initialSlide: 2, the slider breaks. I'm trying to get the center slide as the active slide with this.
Slick not changing slide

Here you go...
It took me some time to figure it out because of some strange behavior of the slick slider.
To begin with, the slick slider didn't work with newer versions of jQuery. After a lot of trial and error (and searching through older answers here on SO), I figured out it works only with the older version of jQuery for some strange reason. This is the main reason it didn't work for you too. I used older version of jQuery than you did and it still didn't work. It works with v1.12.3 (it might also work with some newer versions of jQuery, but I didn't test which is the latest compatible version).
To achieve a 3D effect, you can use box-shadow: 0.1vw 0.8vw 1vw rgba(0, 0, 0, 0.35);. With the current values (i.e. 0.1vw 0.8vw 1vw), you get a blurry shadow at the bottom and a little bit on the right side. The first value (i.e. 0.1vw) defines a horizontal offset. The second value (i.e. 0.8vw) defines a vertical offset. The third value (i.e. 1vw) defines blur radius so that the shadow is "smooth" (try to remove 1vw and you'll get the point).
Also, I managed to solve all your issues:
the row layout works,
the middle card is always an "active card" (a little bit bigger box, a little bit bigger font) and
arrows work too (but you have to use z-index: 100; to push them to the front).
$(document).on('ready', function() {
$('.slider').slick({
dots: true,
centerMode: true,
infinite: true,
centerPadding: '60px',
slidesToShow: 3,
speed: 400
});
});
html {
height: 100%;
overflow-x: hidden;
}
body {
background: #F8F8F8;
height: 100%;
margin: 0;
}
.slider {
width: 90%;
left: 5%;
}
h3 {
background: #fff;
color: #202020;
font-size: 3.5vw;
line-height: 23vh;
margin: 6.5vw;
margin: 6.5vw;
padding: 1vw;
position: relative;
text-align: center;
box-shadow: 0.1vw 0.8vw 1vw rgba(0, 0, 0, 0.35);
}
.slick-center {
transition: 0.2s ease-in-out;
-webkit-transform: scale(1.3);
-moz-transform: scale(1.3);
transform: scale(1.3);
}
.slick-center h3 {
font-size: 4vw;
}
.slick-prev,
.slick-next {
z-index: 100 !important;
}
.slick-prev:before {
transition: 0.2s ease-in-out;
color: #303030 !important;
font-size: 2vw !important;
margin-right: -10vw;
}
.slick-next:before {
transition: 0.2s ease-in-out;
color: #303030 !important;
font-size: 2vw !important;
margin-left: -10vw;
}
.slick-dots li button:before {
transition: 0.2s ease-in-out;
font-size: 0.7vw !important;
}
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>Document</title>
<script src='https://ajax.googleapis.com/ajax/libs/jquery/1.12.3/jquery.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.js'></script>
<link href='https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.css' rel='stylesheet' />
<link href='https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick-theme.min.css' rel='stylesheet' />
</head>
<body>
<div class='slider'>
<div>
<h3>1</h3>
</div>
<div>
<h3>2</h3>
</div>
<div>
<h3>3</h3>
</div>
<div>
<h3>4</h3>
</div>
<div>
<h3>5</h3>
</div>
<div>
<h3>6</h3>
</div>
</div>
</body>
</html>

For a smooth 3D Effect you need to play with shadows, by increasing/decreasing the shadow you get a visual illusion of a popping out/in box, for the animation effects play with css transition use only transform for better performance.
You don't need external libraries to achieve this, the example below is a pure HTML/CSS/Javascript slider.
Edit: add some explanatory comments to the example.
// Button click events
const buttons = document.querySelectorAll('b')
buttons.forEach((button, i) =>
button.onclick = () => slide(i)
)
// Slide function
function slide(right) {
// Get the SLider elements
const container = document.querySelector('div');
const slider = document.querySelector('ul');
const items = document.querySelectorAll('li');
const featured = document.querySelector('.featured');
// Get the featured item
const featuredIndex = [...items].indexOf(featured);
// Set the move if not right then left
const move = right ? 1 : -1;
// Check the slider limits
// if right and index 0: do nothing
// if left and last item: do nothing
if((!right && !featuredIndex)
|| (right && featuredIndex === items.length -1))
return;
// Get the next item element
const nextIndex = featuredIndex + move;
const nextItem = items[nextIndex]
// Remove "featured" class from last item
featured.classList.remove("featured");
// Add "featured" class the next item
nextItem.classList.add("featured");
// Get the container size
const { width: containerSize } = container.getBoundingClientRect();
// Get the next item size and position
const itemSize = containerSize / 3; // Display only 3 items
const itemPosition = itemSize * (nextIndex + 1); // Current position
// Compute the slider next position
const position = containerSize - itemPosition - itemSize;
// Move the slider on its X axis using css transform and transitionX
slider.style.transform = `translateX(${position}px)`
}
body{
background: #ddd;
}
/* Some csss reboot */
ul, li{
margin: 0;
padding: 0;
list-style-type: none;
}
/* Container */
div {
position: relative;
width: 100%;
overflow-x: hidden;
transform: transitionX(0);
}
/* Preview/Next Buttons */
b {
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 0.5em;
width: 20px;
height: 20x;
line-height: 20px;
font-size: 1.2em;
color: #666;
cursor: pointer;
transform: translateY(-50%) scale(1);
transition: transform .1s ease-out;
}
b:last-child {
left: unset;
right: 0.5em;
}
/* Button animation on hover */
b:hover{
transform: translateY(-50%) scale(1.3);
}
/* Slider */
ul {
position: relative;
padding: 2em 0;
display: flex;
width: 100%;
height: 150px;
transform: translateX(0);
transition: transform .3s ease-out;
}
/* Items */
li {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
font-size: 4em;
font-weight: 500;
font-family: arial;
color: #555;
min-width: calc(100% / 3);
background: #fff;
border-radius: .5em;
/* Animation */
transform: translateX(10%) scale(0.5);
box-shadow: 0 2px 2px 0 rgb(0 0 0 / 14%), 0 3px 1px -2px rgb(0 0 0 / 12%), 0 1px 5px 0 rgb(0 0 0 / 20%);
transition: all .3s ease-out;
}
/* Featured Item */
.featured {
z-index: 1;
/* Animation */
transform: scale(1);
box-shadow: 0 16px 24px 2px rgb(0 0 0 / 14%), 0 6px 30px 5px rgb(0 0 0 / 12%), 0 8px 10px -7px rgb(0 0 0 / 20%)
}
.featured + li {
/* Offset the next element under the selected element */
transform: scale(.5) translateX(-25%) !important;
}
<div> <!-- Container -->
<ul> <!-- Slider -->
<li>1</li> <!-- Ithems -->
<li class="featured">2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
<b>ᐸ</b> <!-- Preview button -->
<b>ᐳ</b> <!-- Next button -->
</div>

Related

How to turn off hover effect after certain amount of time?

I have a hover effect where when the icon image is hovered over, an larger image appears (for clarity). I want this larger image effect to end after three seconds AND still have the hover ability. If the image is moved off of, then came back to; I want the larger image to load for another three seconds. Every time the image is hovered over, the effect would last three seconds.
I have tried CSS animations, transitions, setTimeout and none of them are working like I need. Any help is appreciated. I have a LOT of code on this project, so I will try to only include the relevant parts. Thanks in advance.
I will have the code added to the question, once i figure out what I am doing wrong.
Code for building levels for hover image
#PlayerMarker1 {
position: absolute;
left:2%;
top: 2%;
z-index: 9;
}
#Player1Final{
position: absolute;
left:2%;
top: 2%;
z-index: 9;
}
/* Elements for Image load on hover*/
.playerMarker img{
margin: 0 5px 5px 0;
}
.playerMarker:hover{
background-color: transparent;
}
.playerMarker:hover img{
border: 1px;
}
.playerMarker span{ /*CSS for enlarged image*/
position: absolute;
padding: 0px;
left: -1000px;
/*background-color: black ;*/
visibility: hidden;
color: black;
text-decoration: none;
}
.playerMarker span img{ /*CSS for enlarged image*/
border-width: 0;
padding: 2px;
}
.playerMarker:hover span{ /*CSS for enlarged image*/
visibility: visible;
top: 0;
left: 100px; /*position where enlarged image should offset horizontally */
z-index: 50;
}
Code for defining the images.
<div id="Player1Test">
<a id="PlayerMarker1" href="#thumb1"><img src="Player Markers/Morty_Icon.png" width="auto" height="auto"/><span><img src="Player Images/Morty_Token.png" /><br /></span></a>
</div>
This script adds the playerMarker classes to the element I need.
/* Script to add class to player marker ID items */
function Player1Function() {
var Player1FinalTest = document.getElementById("PlayerMarker1");
Player1FinalTest.classList.add("playerMarker");
Player1FinalTest.id='Player1Final';
}
Seems like css animations to pulse the image would work fine. Run the code snippet to try.
img {
margin: 25px;
width: 100px;
height: auto;
}
img:hover {
animation: pulse 2s 1;
}
#keyframes pulse {
0% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.7);
}
50% {
transform: scale(1.4);
box-shadow: 0 0 0 10px rgba(0, 0, 0, 0);
}
100% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
}
<h4>Hover the mouse over image<h4>
<img src="https://stackoverflow.design/assets/img/favicons/apple-touch-icon.png">
Yogi has a good answer using an animation that could be adapted to clearly move your element. I wanted to add an answer manipulating the left and top values using delay.
You are essentially moving a hidden image from off screen onto the screen. Though this feels a bit strange to do, as there may be more clear ways of accomplishing this task, you can immediately move the left into view, and delay moving the top out of view.
A different delay is needed for the hover and for the base element, so it returns to the original position immediately and is available for reuse, but delays moving away on hover.
This might keep in spirit of your current project.
If you have any questions, please ask 🚀
.playerMarker {
background-color: lightblue;
--size: 4rem;
height: var(--size);
width: var(--size);
}
.playerMarker span {
position: absolute;
padding: 0px;
top: 0;
left: -1000px;
visibility: hidden;
color: black;
text-decoration: none;
transition: top 0s;
}
.playerMarker span img {
border-width: 0;
padding: 2px;
}
.playerMarker:hover span {
transition: top 0s 2s;
visibility: visible;
top: -1000px;
left: 100px;
z-index: 50;
}
<div class="playerMarker">
<span>
<img src="https://stackoverflow.design/assets/img/favicons/apple-touch-icon.png" />
</span>
</div>

CSS and JavaScript Transition Translation Jumpy

The transition of my header and navigation finish in the desired places and have the correct look I am going for, however, the transition is jumpy, almost as if something is trying to restrict the translation from happening. Any advice on how I can go about fixing this erratic transition behavior?
With a similar problem, the padding of the initial element style was the problem (before JavaScript changed the style class.)
I expect the transition to be smooth and seamless, not fighting to transition into it's proper end place.
Thanks again for any help.
PS: I'm not sure how to reference the JS file at the bottom of the HTML code to allow the JS code snippet to change the html elements properly. The CSS which is supposed to make the 'headerLogoBackground' sticky at the very top of the window and change is not being applied which is needed to answer the question. However, I don't have this problem on my local machine - sorry guys, working on fixing this in the stackoverflow code snippet editor:/
// JavaScript
window.onscroll = function() {
headerSlide_and_change();
}; // end window.onscroll
function headerSlide_and_change() {
if (document.body.scollTop > 30 || document.documentElement.scrollTop > 30) {
// vars
var headerLogoBackground = document.getElementById("headerLogoBackground");
var headerLogo = document.getElementById("headerLogo");
headerLogoBackground.classList.remove("headerLogoBackground"); // header background
headerLogoBackground.classList.add("headerLogoBackGroundTransform");
headerLogo.classList.add("headerMove"); //header title slide
} else {
/*---header background---*/
var headerLogoBackground = document.getElementById("headerLogoBackground");
headerLogoBackground.classList.remove("headerLogoBackGroundTransform");
headerLogoBackground.classList.add("headerLogoBackground");
/*---header---*/
var headerLogo = document.getElementById("headerLogo");
headerLogo.classList.remove("headerMove");
headerLogo.classList.add("headerLogo");
} // end else
} // end navChangeOnScrollUp
html,
body {
margin: 0;
height: 100vw;
}
html {
scroll-behavior: smooth;
}
body {
background-color: #796651;
background-position: center;
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
}
// Initial CSS
// header
.headerLogoBackground {
background-color: none;
transition: background-color .2s linear;
}
.headerLogo {
text-align: center;
transition: all .2s;
}
.headerLogo a {
font-family: 'Squada One', cursive;
font-size: 40px;
text-decoration: none;
color: #d8cfc3;
}
.headerLogo a:focus {
outline: none;
color: darkslategrey;
}
// navigation bar
.navRaise {
position: -webkit-sticky;
position: static;
z-index: 2;
}
nav {
text-align: center;
font-weight: bold;
width: 100%;
margin-bottom: 8%;
}
.navUlStyle {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: rgba(0, 0, 0, 0.26);
height: 45px;
line-height: 45px;
}
nav li {
float: left;
width: 8%;
padding-left: 12.5%;
padding-right: 12.5%;
}
nav a {
font-family: 'Squada One', cursive;
font-size: 20px;
text-decoration: none;
color: #d8cfc3;
display: block;
text-align: center;
position: relative;
z-index: 3;
}
// JavaScript CSS Classes used to change initial CSS
.headerLogoBackGroundTransform {
/* to replace 'headerLogoBackground' */
position: sticky;
top: 0;
background-color: white !important;
transition: all .2s linear;
z-index: 10;
}
.headerMove { /* to replace 'headerLogo' */
margin-block-start: 0;
transform: translate(-10%, 0px) !important;
transition: transform .2s;
display: inline-block;
width: 30%;
float: left;
}
<html>
<head>
<title>...</title>
<link href="https://fonts.googleapis.com/css?family=Pinyon+Script|Squada+One&display=swap" rel="stylesheet">
</head>
<body id="body">
<div id="Welcome"><!--Wrapper div-->
<!-------------------------Top Section------------------------->
<header id="headerLogoBackground" class="headerLogoBackground">
<!--header-->
<h1 id="headerLogo" class="headerLogo"><a id="headerLogoLink" href="index.html">Lorem Dolo</a></h1>
<!--navigation-->
<nav id="navRaise" class="navRaise">
<ul id="navUl" class="navUlStyle">
<li><a id="topNav" title="Top" href="#Welcome"> Top </a></li>
<li><a id="servicesNav" title="Services" href="#Services"> Services </a></li>
<li><a id="contactNav" title="Contact" href="#Contact"> Contact </a></li>
</ul>
</nav>
</header>
</div><!-- end wrapper div -->
<!------------------------JavaScript Documents--------------------------->
<!-- I don't think path to external JavaScript file is relevant for stackoverflow code snippet -->
</body>
</html>

slider with autoplay and next and previous arrows with CSS HTML JS

I am a beginner and I'd like to know if it's possible to create a responsive slider with autoplay(fade or slide effect, no matters) and next and previous arrows for navigation without affecting the autoplay, just to change from one slide to another.
I've already tried with multiple js solutions I found on the net like "setinterval" function with javascript, but I have always the same problem, one effect works and not the other.
Actually I've done a slider in html and css but I can't get this done by adding a javascript function.
I am open to all the solutions, even if it is possible only with js.
I'm following a course right now and that is part of a project.
could it be done??
Thank you in advance!
/*progress bar effect*/
#keyframes loading {
0% {
transform: scaleX(0);
}
100% {
transform: scaleX(100%);
}
}
/*autoplay effect*/
#keyframes fade {
0% {
opacity: 1
}
45% {
opacity: 1
}
50% {
opacity: 0
}
95% {
opacity: 0
}
100% {
opacity: 1
}
}
#keyframes fade2 {
0% {
opacity: 0
}
45% {
opacity: 0
}
50% {
opacity: 1
}
95% {
opacity: 1
}
100% {
opacity: 0
}
}
/*Section slider*/
.slider {
width: 100%;
height: 550px;
margin: 20px auto;
position: relative;
}
.slide1,
.slide2 {
position: absolute;
width: 100%;
height: 100%;
}
.slide1 {
background: url('images/bg1.jpg') no-repeat center;
background-size: cover;
animation: fade 30000s infinite linear;
-webkit-animation: fade 30000s infinite linear;
}
.slide2 {
background: url('images/bg2.jpg') no-repeat center;
background-size: cover;
animation: fade2 30000ms infinite linear;
-webkit-animation: fade2 30000ms infinite linear;
}
/*progress bar*/
.progress-bar {
position: absolute;
bottom: -76px;
left: 0px;
height: 80px;
width: 100%;
background: color: rgba(192, 194, 192, 0.8);
border-radius: 0 0 1px 1px;
box-shadow: inset 0px 11px 14px -10px #737373, inset 0px -11px 8px -10px #CCC;
}
.loaded {
height: 4px;
width: 100%;
background: #5cadd3;
animation: 15000ms infinite linear loading normal;
transform-origin: 0%;
}
/*Slider buttons left or right*/
.slider #button_left {
position: absolute;
top: 45%;
left: 0px;
background-color: rgba(70, 70, 70, 0.6);
width: 35px;
height: 70px;
border-radius: 0px 50px 50px 0px;
}
.slider #button_right {
position: absolute;
top: 45%;
right: 0px;
background-color: rgba(70, 70, 70, 0.6);
width: 35px;
height: 70px;
border-radius: 50px 0px 0px 50px;
}
#button_left:hover,
#button_right:hover {
transition: .3s;
background-color: rgba(99, 99, 99, 1);
color: #ffffff;
}
/*left and right arrows for slider with font-awesome*/
.fas.fa-chevron-left {
position: absolute;
left: 0;
top: 30%;
margin-left: 5px;
color: #fff;
font-size: 25px;
}
.fas.fa-chevron-right {
position: absolute;
right: 0;
top: 30%;
margin-right: 5px;
color: white;
font-size: 25px;
}
<section id="slideshow">
<div class='slider'>
<div class='slide1'>
<div class="text-slider">
<h1><span class="textblue">WEBAGENCY</span>: lorem ipsum <br> lorem ipsum</h1>
<p> lorem ipsum</p>
lorem ipsum
</div>
</div>
<div class='slide2'>
<div class="text-slider">
<h1><span class="textblue">WEBAGENCY</span>: lorem ipsum <br> lorem ipsum</h1>
<p> lorem ipsum</p>
lorem ipsum
</div>
</div>
<!--<div class="progress-bar"></div>-->
<div class="progress-bar">
<div class="loaded"></div>
</div>
<i class="fas fa-chevron-left"></i>
<i class="fas fa-chevron-right"></i>
</div>
</section>
No need for a lib or framework to do that.
So, i already have to do this, it's not as complicated as it seems.
First, i used css pseudo class :checked and input type radio to choose which slide to display:
<div id="slider">
<input type="radio" name="slider" checked> <!-- The first slide is displayed by default -->
<div class="slide">...</div>
<input type="radio" name="slider">
<div class="slide">...</div>
...
<div class="prev-button">prev</div>
<div class="next-button">next</div>
</div>
And a bit of css :
#slider > .slide,
#slider > input[type="radio"]{
display:none;
}
#slider > input[type="radio"]:checked + .slide{display:block;}
Then, it should be simpler with js:
window.addEventListener("load",function(){
let timer; let delay = 4000;
let loader = document.querySelector("#slider .loader");
let inputs = Array.from(document.querySelectorAll('#slider > input[type="radio"]'));
//first, the go function that choose which input to check
let go = (prev=false)=>{
let checked = document.querySelector('#slider > input[type="radio"]:checked');
let index = inputs.indexOf(checked);
let next = ((prev) ? index -1 : index + 1);
if(next >= inputs.length) next = 0; //restart from beginning
else if(next < 0 ) next = inputs.length -1; //go to the last slide
//restart the progress bar
loader.classList.remove("loader");
loader.classList.add("loader");
inputs[next].checked = true;
};
//Allow you to define some sort of recursive timeout, otherwise it works only once
let defineTimer = (callback)=>{
timer = setTimeout(()=>{
callback();
defineTimer(callback);
},delay);
};
//next, autoplay :
defineTimer(go);
//next, buttons:
let next = document.querySelector("#slider > .next-button");
let prev = document.querySelector("#slider > .prev-button");
//clear the timer with `clearTimeout` each time you click on a button, if you don't
//and the user click on a button 3900ms after the autoplay call, the next/prev slide will only be displayed
//for 100ms and then switch to the next, which can be disturbing for the user.
next.addEventListener("click",()=>{
go();
clearTimeout(timer);
defineTimer(go);
});
prev.addEventListener("click",()=>{
go(true);
clearTimeout(timer);
defineTimer(go);
});
});
Why it should works :
Every time the go function is called, it will select the next or previous input to check, accordingly to the currently checked one. So you can call it from the timer or not, it doesn't makes any difference.
Hope it answer your question :)

Issue while using transitions + opacity change + overflow hidden

If you see the code sample I have shared, you can see the overlay going outside the box. I traced the issue down to the transition attribute.
I want to remove the content outside of the div. Overflow isn't working as it is supposed to. (removing transition works, but I would like to keep it if possible)
Any help is appreciated
Codepen Link
CODE
var timer = setInterval(function() {
document.querySelector(".qs-timer-overlay").style.opacity = (document.querySelector(".qs-timer-overlay").style.opacity * 1) + 0.1;
if (document.querySelector(".qs-timer-overlay").style.opacity * 1 == 1) {
clearInterval(timer);
}
}, 1000);
.qs-main-header .qs-timer {
padding: 13px 10px;
min-width: 130px;
text-align: center;
display: inline-block;
background-color: #dd8b3a;
color: #FFF;
font-size: 20px;
border-radius: 50px;
text-transform: uppercase;
float: right;
cursor: pointer;
position: relative;
overflow: hidden;
}
.qs-main-header .qs-timer-overlay {
z-index: 1;
width: 10%;
max-width: 100%;
position: absolute;
height: 100%;
top: 0;
left: 0;
background-color: #c7543e;
opacity: 0.0;
/* border-radius: 50px 50px 0px 50px; */
}
.qs-main-header .qs-timer-content {
z-index: 2;
position: relative;
}
.scale-transition {
-webkit-transition: all 1s;
transition: all 1s;
}
<div class="qs-main-header">
<div class="qs-timer scale-transition ng-hide" ng-show="visibility.timer">
<div class="scale-transition qs-timer-overlay"></div>
<div class="qs-timer-content ng-binding">0 <span class="ng-binding">Sec(s)</span>
</div>
</div>
</div>
Actually it is the border-radius that is not getting respected when the transition is happening. This is because of creation of compositing layers for accelerated rendering and can be explained by having a look at the following articles:
HTML5 Rocks - Accelerated Rendering in Chrome
GPU Accelerated Compositing in Chrome.
Why does the issue not happen when transition is disabled?
When styles change but none of the criteria that necessitates the creation of a compositing layer is satisfied (that is, no animation or transition or 3D transform etc):
There is no compositing layer and so the whole area seems to get repainted at every change. Since a full repaint happens there is no issue.
View the below snippet (in full screen mode) after enabling "Show paint rects" and "Show composited layer borders" from Dev tools and observe the following:
No areas with an orange border (compositing layer) are created.
Every time the styles are modified by setting the focus on one of the a tags, the whole area gets repainted (a red or green blinking area).
.outer {
position: relative;
height: 100px;
width: 100px;
margin-top: 50px;
border: 1px solid red;
overflow: hidden;
}
.border-radius {
border-radius: 50px;
}
.inner {
width: 50px;
height: 50px;
background-color: gray;
opacity: 0.75;
}
a:focus + .outer.border-radius > .inner {
transform: translateX(50px);
height: 51px;
opacity: 0.5;
}
<a href='#'>Test</a>
<div class='outer border-radius'>
<div class='inner'>I am a strange root.
</div>
</div>
Why does adding a transition create a problem?
Initial rendering has no compositing layer because there is no transition yet on the element. View the below snippet and note how when the snippet is run a paint (red or green blinking area) happens but no compositing layer (area with orange border) is created.
When transition starts, Chrome splits them into different compositing layers when some properties like opacity, transform etc are being transitioned. Notice how two areas with orange borders are displayed as soon as the focus is set on one of the anchor tags. These are the compositing layers that got created.
The layer splitting is happening for accelerated rendering. As mentioned in the HTML5 Rocks article, the opacity and transform changes are applied by changing the attributes of the compositing layer and no repainting occurs.
At the end of the transition, a repaint happens to merge all the layers back into a single layer because compositing layers are no longer applicable (based on criteria for creation of layers).
.outer {
position: relative;
height: 100px;
width: 100px;
margin-top: 50px;
border: 1px solid red;
overflow: hidden;
}
.border-radius {
border-radius: 50px;
}
.inner {
width: 50px;
height: 50px;
background-color: gray;
transition: all 1s 5s;
/*transition: height 1s 5s; /* uncomment this to see how other properties don't create a compositing layer */
opacity: 0.75;
}
a:focus + .outer.border-radius > .inner {
transform: translateX(50px);
opacity: 0.5;
/*height: 60px; */
}
<a href='#'>Test</a>
<div class='outer border-radius'>
<div class='inner'>I am a strange root.
</div>
</div>
This illustrates that when the layers are merged back and full repaint happens, the border-radius on the parent also gets applied and respected. However, during transition only the compositing layer's properties are changed, so the layer seems to become unaware of the properties of other layers and thus doesn't respect the border-radius of the parent.
I would assume this to be because of the way rendering of layers work. Each layer is a software bitmap and so it kind of becomes equivalent to having a circular image and then placing a div on top of it. That would obviously not result in any clipping of content.
The comment in this bug thread also seems to confirm that a repaint happens when a separate layer is no longer required.
We want to repaint if "gets own layer" is going to change
Note: Though they are Chrome specific, I think the behavior should be similar in others also.
What is the solution?
The solution seems to be to create a separate stacking context for the parent (.qs-timer) element. Creating a separate stacking context seems to result in a separate compositing layer being created for the parent and this solves the issue.
As mentioned by BoltClock in this answer, any one of the following options would create a separate stacking context for the parent and doing one of them seems to resolve the issue.
Setting a z-index on the parent .qs-timer to anything other than auto.
var timer = setInterval(function() {
document.querySelector(".qs-timer-overlay").style.opacity = (document.querySelector(".qs-timer-overlay").style.opacity * 1) + 0.1;
if (document.querySelector(".qs-timer-overlay").style.opacity * 1 == 1) {
clearInterval(timer);
}
}, 1000);
.qs-main-header .qs-timer {
padding: 13px 10px;
min-width: 130px;
text-align: center;
display: inline-block;
background-color: #dd8b3a;
color: #FFF;
font-size: 20px;
border-radius: 50px;
text-transform: uppercase;
float: right;
cursor: pointer;
position: relative;
overflow: hidden;
z-index: 1; /* creates a separate stacking context */
}
.qs-main-header .qs-timer-overlay {
z-index: 1;
width: 10%;
max-width: 100%;
position: absolute;
height: 100%;
top: 0;
left: 0;
background-color: #c7543e;
opacity: 0.0;
/* border-radius: 50px 50px 0px 50px; */
}
.qs-main-header .qs-timer-content {
z-index: 2;
position: relative;
}
.scale-transition {
-webkit-transition: all 1s;
transition: all 1s;
}
<div class="qs-main-header">
<div class="qs-timer scale-transition ng-hide" ng-show="visibility.timer">
<div class="scale-transition qs-timer-overlay"></div>
<div class="qs-timer-content ng-binding">0 <span class="ng-binding">Sec(s)</span>
</div>
</div>
</div>
Setting opacity to anything less than 1. I have used 0.99 in the below snippet as it doesn't cause any visual difference.
var timer = setInterval(function() {
document.querySelector(".qs-timer-overlay").style.opacity = (document.querySelector(".qs-timer-overlay").style.opacity * 1) + 0.1;
if (document.querySelector(".qs-timer-overlay").style.opacity * 1 == 1) {
clearInterval(timer);
}
}, 1000);
.qs-main-header .qs-timer {
padding: 13px 10px;
min-width: 130px;
text-align: center;
display: inline-block;
background-color: #dd8b3a;
color: #FFF;
font-size: 20px;
border-radius: 50px;
text-transform: uppercase;
float: right;
cursor: pointer;
position: relative;
overflow: hidden;
opacity: 0.99; /* creates a separate stacking context */
}
.qs-main-header .qs-timer-overlay {
z-index: 1;
width: 10%;
max-width: 100%;
position: absolute;
height: 100%;
top: 0;
left: 0;
background-color: #c7543e;
opacity: 0.0;
/* border-radius: 50px 50px 0px 50px; */
}
.qs-main-header .qs-timer-content {
z-index: 2;
position: relative;
}
.scale-transition {
-webkit-transition: all 1s;
transition: all 1s;
}
<div class="qs-main-header">
<div class="qs-timer scale-transition ng-hide" ng-show="visibility.timer">
<div class="scale-transition qs-timer-overlay"></div>
<div class="qs-timer-content ng-binding">0 <span class="ng-binding">Sec(s)</span>
</div>
</div>
</div>
Adding a transform to the element. I have used translateZ(0px) in the below snippet as this also doesn't create any visual difference.
var timer = setInterval(function() {
document.querySelector(".qs-timer-overlay").style.opacity = (document.querySelector(".qs-timer-overlay").style.opacity * 1) + 0.1;
if (document.querySelector(".qs-timer-overlay").style.opacity * 1 == 1) {
clearInterval(timer);
}
}, 1000);
.qs-main-header .qs-timer {
padding: 13px 10px;
min-width: 130px;
text-align: center;
display: inline-block;
background-color: #dd8b3a;
color: #FFF;
font-size: 20px;
border-radius: 50px;
text-transform: uppercase;
float: right;
cursor: pointer;
position: relative;
overflow: hidden;
transform: translateZ(0px) /* creates a separate stacking context */
}
.qs-main-header .qs-timer-overlay {
z-index: 1;
width: 10%;
max-width: 100%;
position: absolute;
height: 100%;
top: 0;
left: 0;
background-color: #c7543e;
opacity: 0.0;
/* border-radius: 50px 50px 0px 50px; */
}
.qs-main-header .qs-timer-content {
z-index: 2;
position: relative;
}
.scale-transition {
-webkit-transition: all 1s;
transition: all 1s;
}
<div class="qs-main-header">
<div class="qs-timer scale-transition ng-hide" ng-show="visibility.timer">
<div class="scale-transition qs-timer-overlay"></div>
<div class="qs-timer-content ng-binding">0 <span class="ng-binding">Sec(s)</span>
</div>
</div>
</div>
The first two approaches are more preferable than the third because the third one works only on a browser that supports CSS transforms.
Yes, adding opacity: 0.99; to .qs-timer issue will fixed.
When opacity: 1 OR NOT define:
In this special case, there is no transparency involved so that gfx could avoid doing the expensive things.
In case Opacity: 0.99:
nsIFrame::HasOpacity() decides that there is an opacity, so gfx include valuable things. ( likes opacity with border-radius)
For more help Special case opacity:0.99 to treat it as opacity:1 for graphics , This ticket is not providing the opinion of our actual goal, but giving the idea about what is happening inside of CSS.

vertical scrolling menu in JavaScript

Is there any way to create a vertical scrolling menu that when you click on a link the whole menu will shift up or down? It's really hard to explain. The best example I could find of what I'm trying to do is the old xbox nxe dashboard.
http://www.youtube.com/watch?v=Q2PyNpbteuU
Where if your links are (HOME - ABOUT - CONTACT) and you click contact; that link will now be centered in the list and about will be on top and home underneath.
Home
About
Contact (than you click on contact)
-
About
Contact
Home (And now it would look like this)
-
Is this possible? Using HTML5? CSS? Javascript?
Here you will find a horizontal and a vertical implementation I did with jquery:
http://codepen.io/rafaelcastrocouto/pen/kuAzl
The HTML code of the vertical menu:
<nav id="vmenu">
<section>
<div class="active"><a>First</a></div>
<div><a>Second</a></div>
<div><a>One more</a></div>
<div><a>XBox</a></div>
<div><a>Menu</a></div>
<div><a>Last</a></div>
</section>
</nav>
The CSS:
#vmenu {
border: 1px solid green;
overflow: hidden;
position: relative;
padding: 5%;
}
#vmenu section {
position: relative;
margin-top: 10%;
transition: top 0.5s;
}
#vmenu section div {
float: left;
display: inline-block;
padding: 0; margin: 0;
transform: scale(1);
transition: transform 0.5s;
}
#vmenu section div.active {
transform: scale(1.2);
}
#vmenu section div a {
text-align: center;
background: #ddd;
box-shadow: 0 0 1em #444;
border-radius: 1em;
display: block;
width: 60%; height: 60%;
padding: 10%;
margin: 10%;
}
#vmenu section div.active a {
background: #ccc;
box-shadow: 0 0 1em;
}
And the JS:
var size = 200;
var count = $('#vmenu section').get(0).children.length;
$('#vmenu').height(2 * size).width(size);
$('#vmenu section').height(size * count);
$('#vmenu section div').height(size).width(size).on('click', function(){
$('#vmenu section div').removeClass('active')
$(this).addClass('active');
var c = this.parentNode.children;
var i = Array.prototype.indexOf.call(c, this);
$('#vmenu section').css('top', i * size * -1);
});

Categories