Stack DIVs in one line if there is enough room - javascript

I'm looking to create a horizontal timeline and avoid one extremely performance price issue.
suppose we have 3 events represented as 3 divs.
<div class="timeline">
<div id="Ev1">
Event 1
</div>
<div id="Ev2">
Event 2
</div>
<div id="Ev3">
Event 3
</div>
now I want them to display each in its required time according to the horizontal axis i have tried to use margin for that but sure did not work because they are not set to float:left;
the issue is i don't want them to float left i want to control which event is displayed where on the horizontal axis either by using margin or left:##px or any other means that can be converted to a time calculation.
so here is the CSS:
body {
background: #AAA;
}
.timeline {}
.timeline div {
height: 30px;
}
#Ev1 {
background: #e10b1f;
width: 600px;
margin-left: 231px
}
#Ev2 {
background: #fb7d29;
width: 230px;
}
#Ev3 {
background: #96cf67;
width: 460px;
}
I know i could use JS to calculate how many events i have in parallel and fix the top property according to the offset and so on but this is exactly what i am trying to avoid because it causes a sever performance hit when we are looking at hundreds of events on the timeline.
I am looking for an elegant way to tell the browser that if there is enough room on a single line display the DIVs one after the other but if not then stack them one on top with there respective offset according to the event time.
CodePen: http://codepen.io/arthurv/pen/WwbmRr

I'm not sure I got what you are trying to do.
Does this work?
.timeline div {
display: inline-block;
}

1. The CSS architecture
Use percentage based widths + floats for your indidivual events :
body {
background : #AAA;
}
.timeline {
width : 100%;
}
.timeline div {
float : left;
height : 30px;
}
#Ev1 {
background : #e10b1f;
width : 41.51%;
}
#Ev2 {
background : #fb7d29;
width : 17.82%;
margin-left : 5%;
}
#Ev3 {
background : #96cf67;
width : 27.66%;
margin-left : 8%;
}
#Ev4 {
background : #ffc901;
width : 17.82%;
}
#Ev5 {
background : #88aaff;
width : 67%;
margin-left : 12%;
}
<div class="timeline">
<div id="Ev1">
Event 1
</div>
<div id="Ev2">
Event 2
</div>
<div id="Ev3">
Event 3
</div>
<div id="Ev4">
Event 4
</div>
<div id="Ev5">
Event 5
</div>
</div>
2. Generating the values for your widths
To dynamically calculate the actual percentages for the widths of #Ev1, #Ev2, #Ev3, you could use eg. PHP, Less or Sass.
For example, if you'd use Sass, you could using this code to generate the CSS shown in #1 :
$events : (
1 : (0, 41.51%, #fb7d29),
2 : (5%, 17.82%, #fb7d29),
3 : (8%, 27.66%, #96cf67),
4 : (0, 17.82%, #ffc901),
5 : (12%, 67%, #88aaff)
);
#mixin generate-events() {
#each $key, $value in $events {
#Ev#{$key} {
background : nth($value, 3);
width : nth($value, 2);
$margin-left : nth($value, 1);
#if $margin-left != 0 {
margin-left : $margin-left;
}
}
}
}
body {
background : #AAA;
}
.timeline {
width : 100%;
}
.timeline div {
float : left;
height : 30px;
}
#include generate-events();

Related

How to use javascript to dynamically set css animation's keyframe?

I want to make an animation on my product page. When user clicks "add to cart" the product image will be animated moving and shrinking to the cart icon in the nav bar.
Here is a sample html
$('div.test').on('animationend', (e) => {
$(e.target).remove();
})
//user click
$('div.test').addClass('animateTest');
.test {
position : fixed;
top : 200px;
left : 600px;
background : red;
width : 200px;
height : 300px;
}
#keyframes toCart {
25% {
top : 850px;
left : 550px;
width : 200px;
height : 300px;
}
100% {
top : 100px;
left : 1100px;
width : 0;
height : 0
}
}
.animateTest {
animation : toCart 2s;
/* animation-fill-mode: forwards; */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="test">
</div>
The hard part is, since users' viewports vary, I probably need to use javascript to get the cart icon's position(unless I can get it from CSS which I don't think is possible):
whereIsCart = $('#cartIcon').offset()
and I need to do something like
100% {
top : whereIsCart.top;
left : whereIsCart.left;
width : 0;
height : 0
}
But how can I do this?
Or, is there any better practice to achieve the same goal?
It may be easier to use css transitions instead of keyframe animations:
.test {
// ...
transition: transform 1s ease-in-out;
}
// on click
whereIsCart = $('#cartIcon').offset();
$('div.test').css('transform', 'translate(' + whereIsCart.left + 'px, ' + whereIsCart.top + 'px) scale(0)');
When working with CSS in JavaScript you may want to use the CSSOM API; more specifically, its factory functions for unit values, e.g. CSS.px(), CSS.percent().
Note that parts of the CSSOM API are still experimental, e.g. the factory functions. In production, you should make sure that the target browsers support the features you use.
Regardless of using CSS or JS for the animation itself: To get the element's current position in the viewport you can use Element.getBoundingClientRect(), or more generally Element.getClientRects() for all its relevant boxes.
CSS Custom Properties
You can use custom properties for the initial position. You can set them via JavaScript, and even provide a fallback value in CSS.
If you use them for the animating (not as animated) properties, it should just work:
const divTest = document.querySelector("div.test");
// Example for non-empty custom properties
divTest.style.setProperty("--top", CSS.px(20));
divTest.style.setProperty("--left", CSS.px(80));
// Should happen on click:
toCart(divTest);
function toCart(element) {
const rect = element.getBoundingClientRect();
element.style.setProperty("--top", CSS.px(rect.top));
element.style.setProperty("--left", CSS.px(rect.left));
element.classList.add("animateTest");
}
.test {
position: fixed;
top: var(--top, 10%);
left: var(--left, 10%);
width: 200px;
height: 300px;
background: red;
}
#keyframes toCart {
25% {
top: 80%;
left: 50%;
width: 200px;
height: 300px;
}
100% {
top: 10%;
left: 100%;
width: 0;
height: 0;
}
}
.animateTest {
animation: toCart 2s;
}
<div class="test"></div>
Sidenote: If you want to animate custom properties themselves, you have to define the in a #property rule. Otherwise CSS cannot animate it since its type may be anything (animating e.g. from a length to a color is impossible).
Web Animations API
In JavaScript, you can use the Web Animations API, which is essentially CSS animations but in JS.
You can define keyframes, duration, fill-mode and more. Since Animation.finished is a promise, you can simply react to the animation's end via await or Promise.then().
Example:
const divTest = document.querySelector("div.test");
// Should happen on click:
const animation = animateToCart(divTest);
animation.finished.then(() => console.log("Animation finished. This could start a new animation!"));
function animateToCart(element) {
const rect = element.getBoundingClientRect();
const keyframes = [
{
offset: .25,
top: CSS.percent(80),
left: CSS.percent(50),
width: CSS.px(rect.width),
height: CSS.px(rect.height)
}, {
top: CSS.percent(10),
left: CSS.percent(100),
width: 0,
height: 0
}
];
return element.animate(keyframes,
{
duration: 2000,
easing: "ease" // Is default in CSS, but not in Web Animations...
}
);
}
.test {
position: fixed;
top: 10%;
left: 10%;
width: 200px;
height: 300px;
background: red;
}
<div class="test"></div>
Multi-step animations are also easily done with Web Animations, since you can start another animation after the first animation's promise has resolved.
CSS variables sample code...
const
bluElm = document.querySelector('#blue_elm')
, btAnim = document.querySelector('#bt-anim')
, btMovE = document.querySelector('#bt-movE')
, elTest = document.querySelector('.test')
;
btMovE.onclick = () =>
{
bluElm.classList.toggle('move');
}
btAnim.onclick = () =>
{
let rect = bluElm.getBoundingClientRect();
/* change CSS variables values as style Property ------------- */
elTest.style.setProperty('--p_top', `${rect.bottom}px`);
elTest.style.setProperty('--p_left', `${rect.left}px`);
elTest.classList.add('animateTest');
}
elTest.onanimationend = () =>
{
elTest.classList.remove('animateTest');
}
#blue_elm {
position : fixed;
top : 20px;
left : 300px;
width : 20px;
height : 20px;
border-radius : 10px;
background : cornflowerblue;
}
#blue_elm.move {
top : 50px;
left : 150px;
}
.test {
position : fixed;
top : 200px;
left : 600px;
background : red;
width : 200px;
height : 300px;
--p_top : 0; /* CSS variables declaration */
--p_left : 0;
}
.animateTest {
animation : toCart 2s;
}
#keyframes toCart {
25% {
top : 850px;
left : 550px;
width : 200px;
height : 300px;
}
100% {
top : var(--p_top); /* CSS variables usage */
left : var(--p_left);
width : 0;
height : 0
}
}
<button id="bt-anim"> show animation</button>
<button id="bt-movE"> move element +- 150px</button>
<div id="blue_elm"></div>
<div class="test"></div>

AngularJS UI Bootstrap merge two progress bars

I have set up two progress bars in UI Bootstrap, however my goal is to merge them into one, main bar and a secondary one which only is in the form of a vertical bar.
Here is the HTML code for my progress bars:
<div>
<h3 class="inline-block no-margin">Main</h3>
<uib-progressbar value="95"
class="progress-xs no-radius"
type="success"></uib-progressbar>
<h4 class="no-margin">SubCategory</h4>
<uib-progressbar value="50"
class="progress-xs no-radius no-margin"
type="danger"></uib-progressbar>
</div>
My question is how can I merge these two so that the secondary one only appears as a small vertical bar on the main bar?
So that the main bar showing 95% of the bar and the subcategory showing only a vertical bar at 50%.
There is an example similar to this in here called limit lines, but it is another old library.
You can make use of ng-style to update the position of a vertical line div in order to solve this problem. This will allow you to easily set the position and value of the vertical bar. Check out this working example on plunkr.:
HTML
<uib-progressbar animate="false" value=95 type="success"><b>95%</b></uib-progressbar>
<div id="vertical-mark" ng-style="style()"> {{mark}}%</div>
JS
$scope.mark = 50;
$scope.style = function() {
return {
'left': $scope.mark + '%'
}
}
CSS
#vertical-mark {
position:relative;
border-left:1px solid #000;
height:50px;
top:-57px;
}
As an alternative I've also added a similar implementation with a stacked progress-bar.
I haven't tried this out on my own, but it's what came to mind when I saw your question.
You could modify the CSS of the ui-progressbars such that they were overlaid on one another; perhaps with an position:absolute or what have you. This would allow the numerical markers to all sit next to each other like you want.
Next, you could modify the CSS of the bars themselves. Any bars less than the max, perhaps give them a .no-fill class or something. It would be tricky, but that way you can have any bars less than max % to only consist of a border-right property, while the max % bar would be the one with the background and all the normal progress bar styles.
A gotcha that might show up is that you may also have to order the bars' z-index so that the largest value bar sits behind the lesser values and their vertical lines show up.
Good luck!
FINAL UPDATE (with plunkr)
JS
$scope.stacked = [{
value: 55,
type: 'info'
}, {
value: 95,
type: 'success'
}];
HTML
<div class="progress-wrapper">
<uib-progress max="100" ng-repeat="bar in stacked | orderBy:'value':true">
<uib-bar value="bar.value" type="{{bar.type}}">
<span class="marker" ng-hide="bar.value < 5">
{{bar.value}}%
</span>
</uib-bar>
</uib-progress>
</div>
CSS
/* wrapper to help us contain the bars and their positioning */
.progress-wrapper {
position: relative;
padding-top: 30px;
}
/* make all progress bars the same, no bg so they 'stack' */
.progress-wrapper .progress {
width: 100%;
position: absolute;
overflow: visible;
background: none;
}
/* first child is the background bar, give it color */
.progress-wrapper .progress:first-child {
background: #EEE;
}
/* make all bars invisible and with a right, border;
except the last/furthest back bar, give it color only
*/
.progress-wrapper .progress .progress-bar {
background: none;
border-right: solid 2px #FFF;
}
.progress-wrapper .progress:first-child .progress-bar {
background: #0AF;
border-right: none;
}
/* makes sure that markers behave,
otherwise they'll fly away
*/
.progress .progress-bar {
position: relative;
}
/* style for marker element and drop triangle */
.progress .marker {
position: absolute;
padding: 0 2px;
top: -30px;
right: -15px;
color: #FFF;
background: #000;
}
.progress .marker:after {
content: '';
width: 10px;
height: 10px;
background: #000;
position: absolute;
left: 30%;
bottom: -5px;
transform: rotate(45deg);
}
You can make use of stacked progressbar with some custom styles.
<uib-progress><uib-bar ng-repeat="bar in stacked track by $index" value="bar.value" type="{{bar.type}}"><span ng-hide="bar.value < 5">{{bar.value}}%</span></uib-bar></uib-progress>
$scope.stacked = [{
value: 10,
type: 'info'
}, {
value: 35,
type: 'success'
}];
});
Refer progressbar in this https://angular-ui.github.io/bootstrap/
My example is here https://plnkr.co/edit/9qCP1lV40BQ9DYDHvayo?p=preview
I have edited the code in stacked progressbar. Think this helps.
You can add custom styles to this to show the progress value.

create a full animation in jquery for a menu

I'm new with Jquery and I try to do something too difficult for me.
I try to create a menu with animated links : when you hover a link, a other one'll appear and hide the first link.
I try with a simple menu and 2 links : one with an id="link" and the other with id="hover".
When a mouse is on #link (the first link), the #hover (the second one) will appear, grow up and hide #link.
But, when I put the mouse on #link : nothing !!! I test to change CSS and I use Chrome for searching the matter without success.
$("document").ready(function() {
$("#link").hover(function () {
$(this).next("#hover").stop().animate({
display : block,
margin-top : -31px,
height : 30px
}, 500);
},function () {
$(this).next("#hover").stop().animate({
display : block,
margin-top : -31px,
height : 30px
}, 300);
});
});
#link {
display : block;
position : absolute;
border : 1px solid black;
height : 30px;
width : 100px;
}
#hover{
display : none;
position : absolute ;
background-color : orange;
color : blue;
height : 0px;
width : 100px;
}
nav{
position : relative;
height : 100px;
width :100px;
border : 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav>
<div id="link">A</div>
<div id="hover">A</div>
</nav>
thanks for your help.

How to place a div inside a bigger scrollable div at particular position

I have a div with height = 10*screen-height.
I want to add another smaller div to it with height = screen height
Assuming that I can add 10 such smaller div's onto the bigger div, I want to add this div at particular position on the bigger div. Say starting from 4*screenheight pixel. How do I do that using jQuery?
Presumably you already have the screen height stored, and the two divs created at the correct heights, so:
$(inner_div).css('position', 'relative').css('top', 4*screen_height);
You may not need position:relative in your style if it's in your css already
See here how you can access and manipulate the body's height and the big div's inners afterwards;
JSfiddle
HTML
<div id="biggy">
<div class="smally">Smally :)</div>
<div class="smally">Smally 2, don't forget me :D</div>
</div>
CSS
html, body {
height: 100%;
padding: 0px;
margin: 0px;
}
#biggy {
width: 200px;
background-color: orange;
position: relative;
}
.smally {
width: 100%;
background-color: blue;
color: white;
text-align: center;
position: absolute;
}
JavaScript
$(document).ready(function() {
var bh = $('body').height();
var smally_offset = (bh / 10);
// Set biggy to be the body's height
$('#biggy').css('height', bh);
// Make all smallies 10% of the set height
$('.smally').css('height', smally_offset);
// Handle the different smallies :)
$('.smally:nth-child(1)').css('top', smally_offset * 0);
$('.smally:nth-child(2)').css('top', smally_offset * 1);
});

Vertical middle alignment of image within div? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
vertical alignment of image inside a div
OK, this what I'm trying to do :
I'm having an empty div (a box), with almost no height.
I'm making an AJAX request to load some content into it.
Before loading the content, I want to display in a typical "ajax loading" rotating gif.
I've managed to :
Center the img horizontally (by putting it inside another div with text-align:center;)
What is left :
Be able to give some height to that empty div. (easy)
Vertically align the image, so that it appears on the very center of the box. (I've got absolutely no idea how to do this. I'm currently setting an upper margin, which works for one particular box, but which wouldn't work if the box already has some different height...)
How would you go about it?? (Any possible idea is acceptable. CSS/Javascript whatever...)
http://jsfiddle.net/teresko/5mG2y/
The basic idea is the use display: table-cell; and then vertical-align: middle;
the HTML
<div class="container">
<div class="holder">
<img class="stuff" src="path/to/image.png">
</div>
</div>
the CSS:
.container{
/* the container in which image is placed */
height: 300px;
margin: 0 auto;
width: 200px;
}
.holder{
display: table-cell;
width: 100%;
vertical-align: middle;
height: inherit;
}
.stuff{
display: block;
}
This way the placement of image will not depend on dimensions of container. It also can be adjusted to be in horizontal center too. All you have to do is to add .stuff{ margin: 0 auto;}.
Don't forget that table-cell is not the correct usage. You don't want images to be trated as table cells, since table cells should only contain table data.
Just raising a caution flag. Stick to the semantics.
it's better to use the answer from that other thread.
This:
#container { position: relative; }
#container img {
position: absolute;
left: 50%;
top: 50%;
margin-top: /* -1/2 the height of the image */
margin-left: /* -1/2 the width of the image */
}
Good luck!
With jQuery
//HTML
<div><img src="loader.gif" class="loader" alt="Loader" /></div>
//JS
$.fn.centeringIn = function(){
var pere = this.parent();
(pere.css("position") == 'static') ? pere.css("position","relative"):pere.css("position");
this.css({
'position' : 'absolute',
'top' : ( pere.height() - this.height() )/2+'px',
'left' : ( pere.width() - this.width() )/2+'px'
});
}
$(document).ready( function() {
$('.loader').centeringIn();
});
Add some margin-top to the image style so that it is aligned in the middle of the div. Say your div is 50px height and your image has a height of 5px. Then make your margin-top 20px to put it in the middle.

Categories

Resources