How to move progress bar slider/pointer between two dates? - javascript

I have developed progress bar with html, css and javascript/jQuery.
Now am trying to move vertical line slider / pointer image as the days progress towards end date. Slider / Pointer should move only between start date and end date.
Here's the fiddle link I tried so far:
Updated JSFiddle link
Am having issues with javascript. Below is the code:
var start = new Date(2021, 3, 20),
end = new Date(2021, 4, 20),
today = new Date(),
p = Math.round(((today - start) / (end - start)) * 100) + '%';
// Update the progress bar
$('img').css("margin-left", p).after().append(p);
Also the slider / pointer is moving outside the container. Hope I get help from you all experts :)
Thanks,
Richa

Since today can be outside of the two dates, you'll need restrict p to 0-100.
Also, your image has very wide border on each side, which showed outside of the area.
var start = new Date(2021, 3, 20),
end = new Date(2021, 4, 20),
today = new Date(),
p = Math.max(0, Math.min(100, Math.round(((today - start) / (end - start)) * 100))) + "%";
// Update the progress bar
$('.indicator').css("width", p).find("span").text(p);
// update start/end dates
$('.ldate').text(start.getDate() + "/" + (start.getMonth()+1) + "/" + start.getFullYear());
$('.rdate').text(end.getDate() + "/" + (end.getMonth()+1) + "/" + end.getFullYear());
//demo
p = Math.max(0, Math.min(100, Math.round(((today - start) / (end - start)) * 100)));
!function slider()
{
$('.indicator').css("width", p + "%").find("span").text(p + "%");
if (++p > 100)
p = 0;
setTimeout(slider, 200);
}()
.container {
background: grey;
padding: 10px;
display: block;
position: relative;
}
.container .pbcolor {
display: flex;
flex-basis: 100%;
/* padding: 23px 10px; */
background: #ffedc4;
}
.container .mdate {
font-weight: bold;
display: flex;
flex-basis: 100%;
margin-top: 10px;
color: #fcfcfc;
}
.container .mdate .ldate {
display: inline-block;
flex-basis: 50%;
}
.container .mdate .rdate {
display: inline-block;
text-align: right;
flex-basis: 50%;
}
/* added */
.container .indicator {
min-width: 2px;
top: 0px;
/* border: 1px solid black; */
overflow: hidden;
background-image: url('https://www.linkpicture.com/q/vlt_1_1.png');
background-repeat: no-repeat;
background-size: contain;
background-position: right;
text-align: center;
}
.pbcolor
{
height: 65px;
line-height: 65px;
}
.indicator > img
{
height: 65px;
float: right;
}
.ltext
{
float: left;
position: absolute;
}
.rtext
{
float: right;
position: absolute;
right: 10px; /* same as padding in .container */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="pbcolor">
<div class="ltext">Text on left</div>
<div class="indicator">
<!-- <img src="https://www.linkpicture.com/q/vlt_1_1.png"> -->
<span></span>
</div>
<div class="rtext">Text on right</div>
</div>
<div class="mdate">
<span class="ldate">20/4/2021</span><span class="rdate">20/5/2021</span>
</div>
</div>
If the image was 2px wide, it would be shown correctly, because of the "incorrect" dimensions it shows away from the right edge and before 25% it shows at wrong dimensions.

Related

How to add a Vertical line at the end of a percentage bar HTML/Javascript

I am using the following HTML/Javascipt code to make the classic percentage bar.
function update() {
var element = document.getElementById("myprogressBar");
var width = 1;
var identity = setInterval(scene, 10);
function scene() {
if (width >= 70) {
clearInterval(identity);
} else {
width++;
element.style.width = width + '%';
element.innerHTML = width * 1 + '%';
}
}
}
#Progress_Status {
width: 50%;
background-color: #ddd;
}
#myprogressBar {
width: 1%;
height: 35px;
background-color: #4CAF50;
text-align: center;
line-height: 32px;
color: black;
}
<!DOCTYPE html>
<html>
<body>
<h3>Example of Progress Bar Using JavaScript</h3>
<p>Download Status of a File:</p>
<div id="Progress_Status">
<div id="myprogressBar">1%</div>
</div>
<br>
<button onclick="update()">Start Download</button>
</body>
</html>
What I would like to obtain and I am trying to achieve with .innerHTML is the following situation
The vertical line has to appear at the same level of the specified percentage.
For the vertical bar I used an added div nested inside the #Progress_Status container. It's styled to be absolute positioned and to change its offset in % in sync with the progress bar width.
For it to work, its container was set to position:relative as the reference frame.
function update() {
//fetches the vertical bar elements
var vbar = document.querySelector("#Progress_Status .percverticalbar");
var element = document.getElementById("myprogressBar");
var width = 1;
var identity = setInterval(scene, 10);
function scene() {
if (width >= 70) {
clearInterval(identity);
} else {
width++;
//updates the left offset of the vertical bar
vbar.style.left = `${width}%`;
element.style.width = width + '%';
element.innerHTML = width * 1 + '%';
}
}
}
#Progress_Status {
width: 50%;
background-color: #ddd;
position: relative;
}
.percverticalbar{
position: absolute;
height: 100px;
width: 5px;
background: gray;
top: -25px;
left: 0;
}
#myprogressBar {
width: 1%;
height: 35px;
background-color: #4CAF50;
text-align: center;
line-height: 32px;
color: black;
margin: 50px 0;
}
<h3>Example of Progress Bar Using JavaScript</h3>
<p>Download Status of a File:</p>
<div id="Progress_Status">
<div id="myprogressBar">1%</div>
<div class="percverticalbar"></div>
</div>
<br>
<button onclick="update()">Start Download</button>
You could just add an :after pseudo element and add the following styles to it. Keep in mind that the parent, in the case #myprogressBar should be relatively positioned.
#myprogressBar {
width: 1%;
height: 35px;
background-color: #4CAF50;
text-align: center;
line-height: 32px;
color: black;
position: relative;
}
#myprogressBar:after {
width: 5px;
height: 80px;
background: #333;
content: '';
position: absolute;
right: -5px;
top: 50%;
transform: translateY(-50%);
border-radius: 5px;
}

How do I make the eyes follow the object?

Im not entirely sure what's wrong with my code. The eyes (iris) seem to be locked on the bottom right corner of the eyes and are rotating around on that fixed position. I've looked at tutorials online and tried variety of codes, but none seem to work. The end goal that I would like to achieve is to have the eyes follow the given block.
Thank you in advance!
window.addEventListener('mousemove', eyeball);
function eyeball() {
const eye = document.querySelectorAll('.eye');
eye.forEach(function(eye){
let x = (eye.getBoundingClientRect().left) + (eye.clientWidth / 2);
let y = (eye.getBoundingClientRect().top) + (eye.clientHeight / 2);
let radian = Math.atan2(event.pageX - x, event.pageY - y);
let rotation = (radian * (180 / Math.PI) * -1) + 270;
eye.style.transform = "rotate("+rotation+"deg)";
});
}
.eye-container {
position: absolute;
display: flex;
justify-content: center;
width: 100%;
margin-top: 20px;
background: pink;
}
.eyeball{
position: relative;
width: 100px;
height: 90px;
border-radius: 50%;
background: white;
margin-right: 20px;
box-shadow: inset 0 0 5px black;
overflow: hidden;
}
.pupil{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 50px;
height: 45px;
border-radius: 50%;
background: black;
border: 10px solid purple;
box-sizing: border-box;
}
.block{
position: absolute;
top: 50%;
width: 100px;
height: 100px;
background: red;
}
<section class="eye-container">
<div class="eyelids eyelid-left"></div>
<div class="eyelids eyelid-right"></div>
<div class=" eyeball left-eye">
<div class=" eye pupil left-pupil"></div>
</div>
<div class="eyeball right-eye">
<div class="eye pupil right-pupil"></div>
</div>
</section>
<div class="block"></div>
What you want is probably something more like this. This will translate the pupils into place, giving it that "following look".
function eyeball(target_x, target_y) {
const eye = document.querySelectorAll('.eye');
eye.forEach(function(eye){
let x = (eye.getBoundingClientRect().left) + (eye.clientWidth / 2);
let y = (eye.getBoundingClientRect().top) + (eye.clientHeight / 2);
let radian = Math.atan2(target_x - x, target_y - y);
let transform_x = Math.round(Math.sin(radian) * 100);
let transform_y = Math.round(Math.cos(radian) * 100);
eye.style.transform = "translate(" + transform_x + "%, " + transform_y + "%)";
});
}
You will also need to change the initial style of the pupils to get a correct offset for the position change.
.pupil{
margin-left: 25px;
margin-top: 25px;
width: 50px;
height: 45px;
border-radius: 50%;
background: black;
border: 10px solid purple;
box-sizing: border-box;
}

Bootstrap + jQuery: Resizing diagram based on screen width

I made a diagram that changes its size based on the screen width (recreated (poorly) in the fiddle), but when I use this code on my page, the final circle/glyph falls to the following line when I decrease the screen width, when it should stay on the same line (as in the fiddle).
My fiddle
Here's my code:
html
<div class="glyphicon-belt">
<div id="rectangle"></div>
<div class="container circle-container circle-1">
<i class="icon-steak" style="font-size: 60px"></i>
</div>
<div class="container circle-container circle-2">
<i class="icon-brain" style="font-size: 60px"></i>
</div>
<div class="container circle-container circle-3">
<i class="icon-happy" style="font-size: 60px"></i>
</div>
</div>
css
.circle-container {
background-color: #FDA220;
width: 100px;
height: 100px;
border-radius: 100px;
text-align: center;
display: inline-block;
line-height: 100px;
margin-top: -60px;
}
.glyphicon-belt {
width: 50%;
left: 25%;
position: absolute;
text-align: center;
margin-top: 100px;
// background-color: black;
}
#rectangle {
width: 80%;
margin-left: 10%;
height: 20px;
background: #E7292A;
}
.circle-1 {
margin-right: 26%;
}
.circle-2 {
margin-right: 26%;
}
.circle-3 {
// margin-right: -5%;
}
.glyph-connect {
// left-margin: 25%;
text-align: center;
padding: 0px;
background-color: black;
}
jQuery
var screen = $(window).width();
var fontRatio = 60 / screen;
var circleRatio = 100 / screen;
var barRatio = 20 / screen;
$(window).on('resize', function() {
var screen = $(window).width();
var fontSize = screen * fontRatio;
var circleSize = screen * circleRatio;
var lineHeight = circleSize + "px";
var barHeight = screen * barRatio
$(".icon-steak").css("font-size", fontSize);
$(".icon-brain").css("font-size", fontSize);
$(".icon-happy").css("font-size", fontSize);
$(".circle-container").css("width", circleSize);
$(".circle-container").css("height", circleSize);
$(".circle-container").css("line-height", lineHeight);
$("#rectangle").css("height", barHeight);
});
If I understand your question right and playing around with it, it looks like you need to fix the circle-3:
.circle-3 {
margin-right: 1%;
}
Not sure why you had it commented out, but that it seems to fix the problem when you uncomment it and play with the %'s.

Move to specific div based on button click

I was trying to move the divs (here it's question number) based on the prev and next button. So that the selected question is always visible on screen.
Here is the demo : http://jsfiddle.net/arunslb123/trxe4n3u/12/
Screen :
click and question number and click prev or next button to understand my issue.
My code :
$("#next")
.click(function () {
$(".c.current-question")
.each(function () {
var divIdx = $(this)
.attr('id');
var scrollTo = $('#' + divIdx)
.position()
.left;
$("#scrollquestion")
.animate({
'scrollLeft': scrollTo
}, 800);
});
});
$("#prev")
.click(function () {
$(".c.current-question")
.each(function () {
var divIdx = $(this)
.attr('id');
var scrollTo = $('#' + divIdx)
.position()
.left;
$("#scrollquestion")
.animate({
'scrollLeft': -scrollTo
}, 800);
});
});
Using scrollLeft is a bit tricky. I did a small redo of your use-case based on positioning and then moving it based on left of the container. The tricky part is to reliably calculate the negative position when scrolled to the extreme right. Also, need to take into account the widths and margins.
Check the below snippet:
var $wrap = $("#numWrap"), $strip = $("#strip"),
$leftArrow = $(".wrapper > .arrows").first(),
wrapWidth = $wrap.width() + $leftArrow.width(),
margin = 10;
fill(20); select($(".numberItem").first());
$strip.on("click", ".numberItem", function() { select($(this)); });
function select($elem) {
$(".numberItem").removeClass("selected");
$elem.addClass("visited").addClass("selected");
focus($elem[0]);
}
function focus(elem) {
var stripPos = $strip.position(),
numPos = $(elem).offset(),
elemWidth = $(elem).width() + margin,
numRight = numPos.left + elemWidth;
if (numRight > wrapWidth) {
$strip.css({"left": stripPos.left - elemWidth});
}
if (numPos.left < (margin + $leftArrow.width())) {
$strip.css({"left": stripPos.left + elemWidth});
}
}
$(".wrapper").on("click", "a.arrow", function() {
var stripPos = $strip.position();
if (this.id == "lft") {
$strip.css({"left": stripPos.left + (wrapWidth / 2)});
} else {
$strip.css({"left": stripPos.left - (wrapWidth / 2)});
}
});
$(".controls").on("click", "a.arrow", function() {
var $sel = $(".selected"), numPos, $sel, elemWidth;
$elem = $sel.length > 0 ? $sel.first() : $(".numberItem").first();
if (this.id == "lft") {
$sel = $elem.prev().length > 0 ? $elem.prev() : $elem;
select($sel);
} else {
$sel = $elem.next().length > 0 ? $elem.next() : $elem;
select($sel);
}
numPos = $sel.offset(); elemWidth = $sel.width() + margin;
numRight = numPos.left + elemWidth;
if (numPos.left > wrapWidth) {
$strip.css({"left": -($sel.text()) * $sel.width() });
}
if (numRight < 0) {
$strip.css({"left": +($sel.text()) * $sel.width() });
}
});
function fill(num){
for (var i = 1; i <= num; i++) {
var $d = $("<a href='#' class='numberItem'>" + i + "</a>");
$strip.append($d);
}
}
* { box-sizing: border-box; padding: 0; margin: 0; font-family: sans-serif; }
div.wrapper {
background-color: #ddd; width: 100vw; height: 64px;
clear: both; overflow: hidden; margin-top: 16px;
}
div.arrows {
float: left; width: 10%; min-width: 24px; height: 64px; line-height: 64px;
text-align: center; vertical-align: middle; overflow: hidden;
}
div.numWrap {
float: left; height: 64px; line-height: 64px;
width: 80%; vertical-align: middle;
overflow: hidden; position: relative;
}
div.strip {
position: absolute; left: 0px;
width: auto; white-space: nowrap;
transition: left 1s;
}
a.numberItem {
display: inline-block; text-align: center; margin: 0px 8px;
background-color: #fff; border-radius: 50%; width: 48px; height: 48px;
font-size: 1.2em; line-height: 48px; text-decoration: none;
}
a.numberItem.visited { background-color: #fff; color: #000; border: 2px solid #01aebc; }
a.numberItem.selected { background-color: #01aebc; color: #fff; }
div.controls { clear: both; }
div.controls > div.arrows { width: auto; margin: 0 12px; }
a, a:focus, a:active, a:link, a:visited {
display: inline-block;
text-decoration: none; font-weight: 600;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div class="arrows">
<a id="lft" class="arrow" href="#">〈</a>
</div>
<div id="numWrap" class="numWrap">
<div id="strip" class="strip"></div>
</div>
<div class="arrows">
<a id="rgt" class="arrow" href="#">〉</a>
</div>
</div>
<div class="controls">
<div class="arrows">
<a id="lft" class="arrow" href="#">〈 Previous</a>
</div>
<div class="arrows">
<a id="rgt" class="arrow" href="#">Next 〉</a>
</div>
<div>
Explanation:
Using absolute positioning on the number container, which is nested to get 100% width.
Markup:
<div class="wrapper">
<div class="arrows"><a id="lft" class="arrow" href="#">〈</a></div>
<div id="numWrap" class="numWrap">
<div id="strip" class="strip"></div> <!-- nesting here -->
</div>
<div class="arrows"><a id="rgt" class="arrow" href="#">〉</a></div>
</div>
CSS:
div.wrapper {
background-color: #ddd; width: 100vw; height: 64px;
clear: both; overflow: hidden; margin-top: 16px;
}
div.arrows {
float: left; width: 10%; min-width: 24px; height: 64px; line-height: 64px;
text-align: center; vertical-align: middle; overflow: hidden;
}
div.numWrap {
float: left; height: 64px; line-height: 64px;
width: 80%; vertical-align: middle;
overflow: hidden; position: relative; /* relatively positioned */
}
div.strip {
position: absolute; left: 0px; /* absolutely positioned */
width: auto; white-space: nowrap;
transition: left 1s; /* instead of jquery animate */
}
With this structure, we can now use left to control the scrolling.
For partially obscured numbers, try to gently focus-in (nudge into view) a number which is partially obscured. This can be done by checking the position relative to parent and adding the width/margin to it and also accounting for width of the left arrow (it might peep thru).
Javascript:
function focus(elem) {
var stripPos = $strip.position(),
numPos = $(elem).offset(),
elemWidth = $(elem).width() + margin,
numRight = numPos.left + elemWidth;
// if it is towards right side, nudge it back inside
if (numRight > wrapWidth) {
$strip.css({"left": stripPos.left - elemWidth});
}
// if it is towards left side, nudge it back inside
if (numPos.left < (margin + $leftArrow.width())) {
$strip.css({"left": stripPos.left + elemWidth});
}
}
Once the user has scrolled the list too far and then tries to click on previous / next buttons to select a question, then we need to move the entire container upto the selected number. We can easily do this by multiplying the question number with element width and then changing the left in positive (if towards right) or in negative (if towards left).
Javascript:
// if left of element is more than the width of parent
if (numPos.left > wrapWidth) {
$strip.css({"left": -($sel.text()) * $sel.width() });
}
// if right of element is less than 0 i.e. starting position
if (numRight < 0) {
$strip.css({"left": +($sel.text()) * $sel.width() });
}
Here is a fiddle to play with: http://jsfiddle.net/abhitalks/aw166qhx/
You will need to further adapt it to your use-case, but you get the idea.

Limit a DIV to appear within another DIV of specific size

I'm currently working on this small project that randomly displays a div (#box) of 100px width and height. I want this div to appear ONLY in another div (#boxBorder) so it appears to be limited to a specific area on the page.
Here is the content of my HTML:
<h1>Test your reactions!</h1>
<p id="directions">Click the shape as fast as you can!</p>
<p id="scoreC">Click score: <span id="cScore">0</span>s</p>
<p id="scoreT">Total score: <span id="tScore">0</span>s</p>
<div id="boxBorder"></div>
<div id="box"></div>
Here is the CSS:
#boxBorder {
height: 500px;
width: 500px;
margin: 20px auto;
left: 0;
right: 0;
background-color: white;
border: 1px black solid;
position: absolute;
z-index: 0;
}
#box {
margin: 0 auto;
height: 100px;
width: 100px;
background-color: red;
display: none;
border-radius: 50px;
position: relative;
z-index: 1;
}
h1 {
margin: 15px 0 0 0;
}
#directions {
margin: 0;
padding: 5px;
font-size: 0.8em;
}
#scoreT, #scoreC {
font-weight: bold;
margin: 10px 50px 0 0;
}
#tScore, #cScore {
font-weight: normal;
}
h1, #directions, #scoreT, #scoreC {
width: 100%;
text-align: center;
}
And lastly, the javascript function for random position:
//Get random position
function getRandomPos() {
var pos = Math.floor((Math.random() * 500) + 1);
console.log("POS: " + pos + "px");
return pos + "px";
}
Which I call within a timeout method:
setTimeout(function() {
createdTime = Date.now();
console.log("make box: " + createdTime);
document.getElementById("box").style.top=getRandomPos();
document.getElementById("box").style.left=getRandomPos();
document.getElementById("box").style.backgroundColor=getRandomColor();
document.getElementById("box").style.borderRadius=getRandomShape();
document.getElementById("box").style.display="block";
}, rTime);
I'm not very skilled in positioning and I can't seem to get these two divs to align so that the #box div can recognize the size of the #boxBorder div and stay within those limits. Any help would be appreciated!
Couple things wrong here:
You need the box div nested inside the borderBox div if you want to use the relative positioning.
<div id="boxBorder">
<div id="box"></div>
</div>
The randomPos function needs to take into account the size of the box, so only multiply by 400 instead of 500.
function getRandomPos() {
var pos = Math.floor((Math.random() * 400));
return pos + "px";
}
Set the style to inline-block, not block for the box.
Use setInterval instead of setTimeout to have it repeat.
var rTime = 1000;
function getRandomPos() {
var pos = Math.floor((Math.random() * 400));
console.log("POS: " + pos + "px");
return pos + "px";
}
function getRandomColor() {
return ['#bf616a', '#d08770', '#ebcb8b', '#a3be8c', '#96b5b4', '#8fa1b3', '#b48ead'][(Math.floor(Math.random() * 7))];
}
function randomizeBox() {
createdTime = Date.now();
console.log("make box: " + createdTime);
document.getElementById("box").style.top = getRandomPos();
document.getElementById("box").style.left = getRandomPos();
document.getElementById("box").style.backgroundColor = getRandomColor();
}
setInterval(randomizeBox, rTime);
#boxBorder {
height: 500px;
width: 500px;
margin: 20px auto;
left: 0;
right: 0;
background-color: white;
border: 1px black solid;
position: absolute;
z-index: 0;
}
#box {
margin: 0 auto;
height: 100px;
width: 100px;
border-radius: 50px;
position: relative;
z-index: 1;
display: inline-block;
}
h1 {
margin: 15px 0 0 0;
}
#directions {
margin: 0;
padding: 5px;
font-size: 0.8em;
}
#scoreT,
#scoreC {
font-weight: bold;
margin: 10px 50px 0 0;
}
#tScore,
#cScore {
font-weight: normal;
}
h1,
#directions,
#scoreT,
#scoreC {
width: 100%;
text-align: center;
}
<h1>Test your reactions!</h1>
<p id="directions">Click the shape as fast as you can!</p>
<p id="scoreC">Click score: <span id="cScore">0</span>s</p>
<p id="scoreT">Total score: <span id="tScore">0</span>s</p>
<div id="boxBorder">
<div id="box"></div>
</div>

Categories