I have a progress bar with several 'checkpoints'.
I want the checkpoints to be filled linearly as the progress bar reaches reach point in a way which appears natural. As the progress bar fills from left to right, it should fill a checkpoint and then stop until it receives some further user interaction.
I previously used jQuery to fill each checkpoint when the progress bar reached it by applying a CSS class to it when the progress fill animation was over. This looked very amateur though. I want to make the same fill animation is applied to both the progress bar and the checkpoint it stops at.
How can I do this using pure CSS?
<div class="progress">
<div class="progress-bar">
<div class="progress-indicator item-0"></div>
<div class="progress-indicator item-1"></div>
<div class="progress-indicator item-2"></div>
<span class="progress-bar-fill-area" style="width: 22.5%;"></span>
</div>
</div>
If you consider base64 encoded images in CSS as pure CSS, you can use them as a mask, by overlaying them over your progress-bar.
var n = 0;
setInterval(function() {
var bar = document.querySelector(".progress");
bar.classList.remove("step" + ((n + 4) % 5));
bar.classList.add("step" + n);
n = (n + 1) % 5;
}, 2000);
.progress-bar {
width: 500px;
height: 32px;
background-color: gray;
position: relative;
}
.progress-indicator {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAH0AAAAgCAYAAAA/kHcMAAABFUlEQVRo3u3YPW6DMBiAYX8VMyLeQb4DopUyVaIsmfFteoWexGLuQL1GSiNfAg6A2JHcsR36k6FSVed9Vjz51YcNUtd1VLgqN2wB0UF0EB1EB9FBdBAdRAfR8QtERGmtvTHmEEKQEIIYYw5aay8i7+v4DZuGLMu2qqr2zrnzZ8+ttc00Tcdt2zImPZEJL8uy+yq4Uko5585VVe1FhElPQVEUr977u0vWdl33wo4BAJDIxY+L3P+ntfbjOD5csrZt2xOfbAlYlqW11jY/rev7/n5d11uiJyDGqKZpOn4X3lrbzPM8xhh5vSd1Vouo3W7n8zx/Gobh+cN0Py7L0sYYOdOvFa93ooPoIDqIDqKD6CA6iI4/9AZ+BlVJ/MotKwAAAABJRU5ErkJggg==');
float: left;
}
.progress-bar-fill-area {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: #63BC62;
width: 0%;
}
.step1 .progress-bar-fill-area {
width: 25%;
transition: width 1s;
}
.step2 .progress-bar-fill-area {
width: 50%;
transition: width 1s;
}
.step3 .progress-bar-fill-area {
width: 75%;
transition: width 1s;
}
.step4 .progress-bar-fill-area {
width: 100%;
transition: width 1s;
}
<div class="progress">
<div class="progress-bar">
<div class="progress-bar-fill-area"></div>
<div class="progress-indicator"></div>
</div>
</div>
Related
I'm trying to figure out how Medium made their bottom action / menu bar slide up when your mouse enters the bottom of the document. The slide up effect is not triggered by moving the mouse over the invisible div (it slides up & down via transform translateY).
Besides, the menu bar is only 44px in height, but its is-visible class gets triggered way before your mouse is near it — but by what? When using Inspect Element, I can't see any hidden divs that could be triggering it..
I've searched for countless of ways, e.g. "show element when mouse enters specific part of document" but all search results involve when the mouse enters or moves over a div element, which is not the solution I'm looking for.
Obviously, you can solve this problem by putting the slide up menu inside a hidden container like I've done here, and then you get the desired result:
(function() {
var actionBar = document.querySelector('.action-bar');
var actionBarWrapper = document.querySelector('.action-bar-detection');
function showDiv() {
actionBar.classList.add('js-is-visible')
}
function hideDiv() {
actionBar.classList.remove('js-is-visible')
}
actionBarWrapper.onmouseover = showDiv;
actionBarWrapper.onmouseout = hideDiv;
})();
* {
margin: 0;
padding: 0;
box-sizing: border-box;
line-height: 1.5;
}
body {
height: 100%;
}
.wrapper {
width: 90%;
max-width: 600px;
margin: 5% auto;
}
.action-bar {
position: absolute;
left: 0;
bottom: 0;
border: 1px solid #252321;
background: #fff;
padding: 16px;
width: 100%;
min-height: 50px;
opacity: 0;
transform: translateY(100%);
transition: all .5s;
z-index: 99;
}
.action-bar-detection {
height: 150px;
width: 100%;
opacity: 1;
position: fixed;
bottom: 0;
left: 0;
z-index: 1;
}
.js-is-visible {
opacity: 1;
transform: translateY(0%);
}
<html>
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<div class="wrapper">
<p>When mouse enters the hidden action bar element, slides up.</p>
<p>But it's only happening because the action-bar is inside an invisible detection layer class (action-bar-detection) with a height of 150px.</p>
</div>
<div class="action-bar-detection">
<div class="action-bar">
Bottom Menu
</div>
</div>
</body>
</html>
However, this doesn't seem to be what Medium have done, and if this can be done without adding more HTML & CSS, I want to learn how! :-)
I think I'm not phrasing the problem correctly, since I can't find any solutions even remotely close (I've searched A LOT).
Any advice? What should I read up on? :-)
Get height of viewport, track onmousemove, and compare clientY from the mouse event to the viewport height:
(function() {
var actionBar = document.querySelector('.action-bar');
var viewHeight = window.innerHeight - 150;
function toggleDiv(e) {
if (e.clientY >= viewHeight) {
actionBar.classList.add('js-is-visible');
} else {
actionBar.classList.remove('js-is-visible');
}
}
window.onmousemove = toggleDiv;
})();
* {
margin: 0;
padding: 0;
box-sizing: border-box;
line-height: 1.5;
}
body {
height: 100%;
}
.wrapper {
width: 90%;
max-width: 600px;
margin: 5% auto;
}
.action-bar {
position: absolute;
left: 0;
bottom: 0;
border: 1px solid #252321;
background: #fff;
padding: 16px;
width: 100%;
min-height: 50px;
opacity: 0;
transform: translateY(100%);
transition: all .5s;
z-index: 99;
}
.action-bar-detection {
height: 150px;
width: 100%;
opacity: 1;
position: fixed;
bottom: 0;
left: 0;
z-index: 1;
}
.js-is-visible {
opacity: 1;
transform: translateY(0%);
}
<div class="wrapper">
<p>When mouse comes within 150px of the bottom part of the screen, the bar slides up.</p>
<p>When the mouse leaves this defined area of the screen, the bar slides down.</p>
</div>
<div class="action-bar-detection">
<div class="action-bar">
Bottom Menu
</div>
</div>
You could do this by listening to the mousemove event on the document, you will want to invest effort into making this performant as it will be triggered frequently. The most common way to regulate events like this is through throttling.
Once you are hooked into the mousemove event you will need to get the Y coordinate of the cursor and compare that to the height of the window, if it is within a threshold then you can reveal your panel, once it moves out you can proceed to hide it again.
Here is an example showing a basic implementation jsFiddle
// Using underscore for the throttle function though you can implement your own if you wish
document.addEventListener('mousemove', _.throttle(mouseMoveEventAction, 200));
function mouseMoveEventAction(e) {
doPanelStuff(isInsideThreshold(e.clientY));
}
function doPanelStuff(isActive) {
var panelElement = document.querySelector('.panel');
if (isActive) {
panelElement.style.background = 'red';
} else {
panelElement.style.removeProperty('background');
}
}
function isInsideThreshold(cursorY) {
var threshold = 200;
var clientHeight = document.documentElement.clientHeight;
return cursorY > (clientHeight - threshold);
}
html, body {
height: 100%;
}
.container, .content {
height: 100%;
width: 100%;
}
.panel {
height: 50px;
position: absolute;
bottom: 0;
width: 100%;
background: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<div class="container">
<div class="content"></div>
<div class="panel"></div>
</div>
I have a div that contains a single div that acts as an overlay, along with another div that contains some images. The overlay has an opacity so that the images can be seen, but text can be still be read.
However, when I animate the opacity of the image, it ignores the overlay during the animation, until it is finished.
He's the code:
HTML
<div class="container">
<div class="overlay"></div>
<div class="image-container">
<img src="https://placeholdit.imgix.net/~text?txtsize=33&txt=350%C3%97150&w=350&h=150">
</div>
</div>
CSS
.container {
position: absolute;
top: 0;
z-index: 1;
height: 100%;
overflow: hidden;
background-color: yellow;
}
.overlay {
position: absolute;
width: 100%;
height: 100%;
top: 0px;
opacity: 0.78;
background-color: rgb(245, 245, 245);
}
JavaScript
var image = document.getElementsByTagName("IMG")[0];
image.style.opacity = 0;
setTimeout(function() {
image.style.transition = "opacity 3s linear";
image.style.opacity = 1;
}, 1000);
I also have a jsfiddle example:
https://jsfiddle.net/ygqov8t4/
I have tested this in Chrome, Firefox, and Safari on Mac. All browser have the same behaviour, so maybe this is by design?
I have tried doing this using JavaScript, but I was not able to get the animation functioning, and I am concerned about performance because this is going to be run on a lot (100+) images.
image.onload = function() {
var self = this;
for (var i = 0; i < 1000; i++) {
setTimeout(function() {
self.style.opacity = i/1000;
}, i);
}
}
Try forcing the overlay to be above the image by setting its z-index to 1
.overlay {
position: absolute;
width: 100%;
height: 100%;
top: 0px;
opacity: 0.78;
background-color: rgb(245, 245, 245);
z-index: 1;
}
https://jsfiddle.net/05pwwtm7/1/
I have 3 divs positioned next to each other, and background div. We'll call the background div bg div. Whenever one of the 3 divs get selected, the bg div gets positioned on top of the selected div. (Basically, something like a segmented controller.)
When I try making the bg div transition to its new position. I did the following to transition:
transition: left linear .2s;
When you select a div for the first time, the animation doesn't happen, but the bg div does move over to its correct position (just without the animation). After the second click and on, it works.
How can I add a transition to the bg div?
JSFiddle
$('.inner').click(function() {
var pos = $(this).position();
$("#back").css('left', pos.left + "px");
});
#wrapper {
width: 600px;
height: 30px;
position: relative;
}
.inner {
display: block;
float: left;
width: calc(100% / 3);
height: 50px;
}
#back {
background-color: yellow;
height: 100%;
z-index: -1;
width: calc(100% / 3);
position: relative;
transition: left linear 1s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="wrapper">
<div class="inner">First Option</div>
<div class="inner">Second Option</div>
<div class="inner">Third Option</div>
<div id="back"></div>
</div>
For a transition to take effect, there should be an initial value defined from which the animation would start.
Here #back does not have an initial left value due to which on the first click, it would jump directly to the left position defined on click.
This will take be the initial value for the next transition and hence from then on you would see the animation in transition.
So all you need to do is add left:0px; in css for #back.
Hope this helped.
You should add left: 0; property for css #back
$('.inner').click(function() {
var pos = $(this).position();
$("#back").css('left', pos.left + "px");
});
#wrapper {
width: 600px;
height: 30px;
position: relative;
}
.inner {
display: block;
float: left;
width: calc(100% / 3);
height: 50px;
}
#back {
background-color: yellow;
height: 100%;
z-index: -1;
width: calc(100% / 3);
position: relative;
transition: left linear 1s;
left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="wrapper">
<div class="inner">First Option</div>
<div class="inner">Second Option</div>
<div class="inner">Third Option</div>
<div id="back"></div>
</div>
I am trying to add transition to a growing div.
Here is a jsfiddle: http://jsfiddle.net/fL5rLr2y/
This jsfiddle represent my real world problem.
I have the following markup:
<div class="container">
<div class="inner">
</div>
</div>
And the following css:
html, body {
height: 100%; } .container {
position: relative;
height: 80%;
background-color: blue; }
.inner {
position: absolute;
top: 30px;
left: 0;
right: 0;
height: 50px;
background-color: red; }
.inner.open {
height: initial;
bottom: 20px; }
Here is my js:
$('.inner').click(function() {
$(this).toggleClass('open');
});
I am trying to add the transition using pure css. How can I do it?
If pure css solution is not possible, how can I use js in order to solve it?
UPDATE
After a lot of investigations, it seems that using calc is the only option to do it in pure css.
Unfortunately I have bed experience with calc, especially with safari and mobile (browser crashes and other surprises). I prefer to avoid using calc for now and use javascript solution to simulate that.
Any idea how?
Edit your .inner and .inner.open classes as demonstrated below ... you need to set a predetermined height to .open
If you're going to use CSS3 transitions you can opt to use calc() to determine your .open height without compromising browser compatibility.
Check demo
.inner {
position: absolute;
top: 30px;
left: 0;
right: 0;
height: 50px;
background-color: red;
transition: height 1s;
-webkit-transition: height 1s;
-moz-transition: height 1s;
-ms-transition: height 1s;
-o-transition: height 1s;
}
.inner.open {
height: calc(100%-50px);
bottom: 20px;
}
You can use the dynamic height by updating the style below. Demo at http://jsfiddle.net/fL5rLr2y/8/
.inner {
position: absolute;
top: 30px;
left: 0;
right: 0;
height: 50px;
background-color: red;
-webkit-transition: height 1s;
transition:height 1s;
}
.inner.open {
height: calc(100% - 50px); /* top 30px + bottom 20px */
}
Or you can use jQuery animation. See the output at http://jsfiddle.net/8mn90ueb/3/ and code below
Remove the open class and the toggle type
$('.inner').click(function() {
var currentHeight = $(this).height();
if(currentHeight > 50){
currentHeight = 50;
}
else{
currentHeight = $('.container').height() - 50;
}
$(this).animate({
height:currentHeight
},1000,function(){});
});
The CSS transition property is what you need. The height calculation of .inner is now made with jQuery.
Demo with jQuery calculation
$('.inner').click(function() {
var parentHeight = $(this).parent().outerHeight() - 50; // Get parent height - 50px
var innerHeight = $(this).outerHeight(); // Get inner height
// if the inner height = 50px then change height to the parent height calculation
// otherwise return to 50 height
if (innerHeight === 50) {
$(this).height(parentHeight);
} else {
$(this).height(50);
}
});
html,
body {
height: 100%;
}
.container {
position: relative;
height: 80%;
background-color: blue;
}
.inner {
position: absolute;
top: 30px;
left: 0;
right: 0;
height: 50px;
background-color: red;
transition: height 0.5s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<div class="container">
<div class="inner"></div>
</div>
If you change your mind about calc()
The CSS transition property is what you need along with height: calc(100% - 50px) on the open class. The calc gets you a 30px gap at the top and 20px gap at the bottom when open. The bottom property has been removed.
Compatibility:
The transition property is unlikely to need browser prefixes. Have a look here for its browser support.
calc() enjoys widespread support including, importantly, IE9 + support. More information here. To provide a fallback height for IE 8 and below, provide a normal height percentage property before the calc height for older browsers to use. Something like height: 70%
Demo with CSS only
$('.inner').click(function() {
$(this).toggleClass('open');
});
html,
body {
height: 100%;
}
.container {
position: relative;
height: 80%;
background-color: blue;
}
.inner {
position: absolute;
top: 30px;
left: 0;
right: 0;
height: 50px;
background-color: red;
transition: height 0.5s;
}
.inner.open {
height: 70%; /* pick a percentage height for IE 8 and below */
height: calc(100% - 50px); /* 100% height minus 30px at the top + 20px at the bottom */
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<div class="container">
<div class="inner"></div>
</div>
<body>
<div id="parent_scroll">
<div id="slider">
<div class="slides">Slide1</div>
<div class="slides">Slide2</div>
<div class="slides">Slide3</div>
</div>
</div>
</body>
<style>
#parent_scroll{
width: 800px;
height: 350px;
border: 1px solid grey;
margin-left: auto;
margin-right: auto;
overflow: hidden;
position: relative;
}
#slider{
width: 2430px;
border: 1px solid green;
height: 350px;
position: absolute;
}
.slides{
width: 800px;
height: 350px;
border: 1px dotted blue;
float: left;
background-color: grey;
font-size: 30px;
text-align: center;
}
I am trying to implement a slide show sort of a feature. But i am not sure what logic goes into the javascript over here, i know i need to use a setInterval() function. The only part is how i would work out the width of the element with the id:"slider". Pointers would be helpful
EDIT: trying to implement this without jQuery
I see in your CSS that your widths are static, but if you were to add slides you should
calculate the #slider width using the width of .slides times the amount of slides..
Then, save your .slides width (including margin) as your offset, and animate #slider's left position using the offset..
EDIT: Actually, there's another technique I've been fiddling with so you won't have to calculate the widths, and that's using display inline-block like this:
#slider { white-space:nowrap;}
.slides { display:inline-block;}
this will automatically have all your slides on the same line and then you can animate using margins.
Let me know if that clears it up for you.. do you need a code example?
EDIT: example (using css animations)
Javascript
var slider, slides, offset, amount, _timer, _curindex = 0;
function initSlider() {
slider = document.getElementById("slider");
slides = document.getElementsByClassName("slides");
offset = slides[0].offsetWidth+2;
amount = slides.length;
slider.style.width = offset*amount;
_timer = setInterval(moveSlide, 3000);
}
function moveSlide() {
_curindex = (_curindex == amount-1) ? 0 : _curindex+1;
slider.style.left = -_curindex*offset+"px";
}
initSlider();
FIDDLE
Like this? Pure HTML CSS:
<div id="parent_scroll">
<div id="slider">
<div class="slides"><img src="http://placehold.it/350x150/ff0000/000000" alt=""></div>
<div class="slides"><img src="http://placehold.it/350x150/00ff00/000000" alt=""></div>
<div class="slides"><img src="http://placehold.it/350x150/0000ff/000000" alt=""></div>
</div>
</div>
DIV#parent_scroll{
width: 350px;
height: 150px;
overflow: hidden;
}
DIV#slider{
position: relative;
width: 1050px;
animation: slideme 5s infinite;
-webkit-animation: slideme 5s infinite;
}
#keyframes slideme {
0% {left: 0;}
33% {left: -350px;}
67% {left: -700px;}
100% {left: 0;}
}
#-webkit-keyframes slideme {
0% {left: 0;}
33% {left: -350px;}
67% {left: -700px;}
100% {left: 0;}
}
DIV.slides{
float: left;
}
DIV#slider:before, DIV#slider:after{
display: table;
content: "";
}
DIV#slider:after{
clear: both;
}
DIV#slider{
zoom: 1
}
http://jsfiddle.net/yLTKe/3/
Add JS to make it dynamic
you should try this code
<div id="div1" class="slides" style="width:800px...">Slide1</div>`
and in js code
var slide1 = document.getElementById("div1");
//if you want to add width
slide1.style.width= parseInt(slide1.style.width) + 100 + "px";`