Adding tooltip to SVG elements with text from an array - javascript

I have multiple SVG circles and would like each to have unique text appear in a tooltip on hover.
I will be creating my SVG elements in Adobe Illustrator, so need a way to bind text for the tooltip to the id's of the SVGs.
Below I have tried to do this by creating an array with object "color: white" matching the name of the SVG circle id="#white" How can I bind this data to my tooltip?
var tooltipText = [
{"color":"white", "text":"About white"},
{"color":"black", "text":"About black"}
]
var tooltip = d3.select('.tooltip').text(function(d) {return tooltipText})
d3.select('#white')
.on("mouseover", function(){
return tooltip.style("visibility", "visible");
})
.on("mousemove", function(){
return tooltip
.style("top",(d3.event.pageY+10)+"px")
.style("left",(d3.event.pageX+10)+"px");
})
.on("mouseout", function(){
return tooltip.style("visibility", "hidden");
});
.container {
position: relative;
}
.tooltip {
font-family: Arial;
font-size: 10px;
padding: 10px;
width: 100px;
height: 150px;
border: 1px solid black;
position: absolute;
top: 0;
left: 200px;
visibility: hidden;
background: white;
}
#white {
fill: white;
stroke: black;
stroke-width: 2px
}
#black {
fill: black
}
circle:hover {
stroke: green; !important
fill: orange;
stroke-width: 2px
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div class="container">
<svg>
<g id="selection">
<circle id="white" class="st0" cx="49.2" cy="49.2" r="48.7"/>
<circle id="black" class="st1" cx="128.4" cy="49.2" r="48.7"/>
</g>
</svg>
</div>
<div class="tooltip"></div>

First, we get the Id of the circle with this line inside your mousemove:
var thisId = d3.select(this).attr("id");
Then, we loop through your array and get the corresponding value:
var index;
for(var i = 0; i < tooltipText.length; i++){
if(tooltipText[i].color == thisId){
index= i
}
};
tooltip.html(tooltipText[index].text)
Here is your updated snippet:
var tooltipText = [
{"color":"white", "text":"About white"},
{"color":"black", "text":"About black"}
]
var tooltip = d3.select('.tooltip').text(function(d) {return tooltipText})
d3.selectAll("circle")
.on("mousemove", function(d){
var thisId = d3.select(this).attr("id");
var index;
for(var i = 0; i < tooltipText.length; i++){
if(tooltipText[i].color == thisId){ index= i}
};
tooltip.html(tooltipText[index].text)
.style("visibility", "visible")
.style("top",(d3.event.pageY+10)+"px")
.style("left",(d3.event.pageX+10)+"px");
})
.on("mouseout", function(){
return tooltip.style("visibility", "hidden");
});
.container {
position: relative;
}
.tooltip {
font-family: Arial;
font-size: 10px;
padding: 10px;
width: 100px;
height: 20px;
border: 1px solid black;
position: absolute;
top: 0;
left: 200px;
visibility: hidden;
background: white;
}
#white {
fill: white;
stroke: black;
stroke-width: 2px
}
#black {
fill: black
}
circle:hover {
stroke: green; !important
fill: orange;
stroke-width: 2px
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div class="container">
<svg>
<g id="selection">
<circle id="white" class="st0" cx="49.2" cy="49.2" r="48.7"/>
<circle id="black" class="st1" cx="128.4" cy="49.2" r="48.7"/>
</g>
</svg>
</div>
<div class="tooltip"></div>

Related

How to use javascript to open the modal when connecting the API so that the user can see the animation effect?

let btn = document.querySelector('.btn');
let modal = document.querySelector('.modal');
let wrap = document.querySelector('.wrap');
let photo = document.querySelector('.photo');
let svg = document.querySelector('svg');
let data = [{
title: "dog",
age: 10,
rank: [{
rankStatus: 'behind'
},
{
rankStatus: 'generally',
rankNum: '20'
},
{
rankStatus: 'excellent'
}
]
}]
let rankStatusArr = data[0].rank.map(item => item.rankStatus);
let rankNum = data[0].rank.find(item => item.rankNum).rankNum;
btn.addEventListener('click', function(e) {
svg.style.display = "block"
axios.get("https://dog.ceo/api/breeds/image/random")
.then((response) => {
// 设置处理程序以在加载图像后显示图像
photo.addEventListener('load', () => {
svg.style.display = "none"
modal.style.display = "flex";
});
// 添加“加载”处理程序后设置图像源
photo.src = response.data.message;
})
.catch((error) => console.log(error));
// 排名狀態在 popup 打開後再載入動畫
let progress = rankNum;
let dot = document.getElementById("dot");
var dotPosition = (progress / 30) * 100;
dot.style.left = dotPosition + "%";
let txt = document.querySelectorAll('.txt');
for (let i = 0; i < txt.length; i++) {
txt[i].innerHTML = rankStatusArr[i];
}
})
wrap.addEventListener('click', function(e) {
e.stopPropagation();
})
modal.addEventListener('click', function() {
modal.style.display = "none"
})
.modal {
width: 100%;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: none;
justify-content: center;
align-items: center;
}
.wrap {
width: 300px;
height: 300px;
padding: 20px;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
border-radius: 20px;
flex-direction: column;
}
.wrap .photo {
width: 100%;
height: 200px;
object-fit: cover;
margin-bottom: 20px;
}
.wrap #progress-bar-container {
width: 100%;
height: 5px;
background-color: #fff;
position: relative;
display: flex;
flex-wrap: nowrap;
}
.wrap #progress-bar-container .progress-bar {
width: 33.33%;
height: 5px;
background-color: #ccc;
margin-right: 8px;
border-radius: 20px;
}
.wrap #progress-bar-container .progress-bar .txt {
text-align: center;
color: #ccc;
margin-top: 20px;
}
#progress-bar-1 {
background-color: #ddd;
}
#progress-bar-2 {
background-color: #ddd;
}
#progress-bar-3 {
background-color: #ddd;
margin-right: 0;
}
#dot {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #333;
position: absolute;
top: -3px;
left: 0;
transition: left 0.2s ease-out;
}
svg {
display: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
svg .load {
stroke-dasharray: 0 500;
animation: rot 1s linear infinite;
}
#keyframes rot {
100% {
stroke-dasharray: 500 500;
}
}
.green {
color: yellowgreen;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.3.2/axios.min.js"></script>
<button class='btn'>open</button>
<div class="modal">
<div class="wrap">
<img class="photo" src="" alt="photo">
<div id="progress-bar-container">
<div class="progress-bar" id="progress-bar-1">
<p class="txt">1</p>
</div>
<div class="progress-bar" id="progress-bar-2">
<p class="txt">2</p>
</div>
<div class="progress-bar" id="progress-bar-3">
<p class="txt">3</p>
</div>
<div id="dot"></div>
</div>
</div>
</div>
<!-- load -->
<svg width="240px" height="240px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<circle cx="110" cy="110" r="90" stroke-width="10" stroke="gainsboro" fill="none"></circle>
<circle cx="110" cy="110" r="90" stroke-width="10" stroke="darkturquoise" fill="none" class="load"></circle>
</svg>
I encountered a little difficulty in the following requirements. The first difficulty is that I hope that the popup will not be opened until the picture is fully loaded! But I hope that when the popup is opened, the dot can add animation and run to the specified progress position, but it seems that the animation I want to appear has already run before it is opened, and the user will not see the moving animation effect. The second problem is that I want to add a green color to the rankStatus font on the screen if the rankNum is included in the rankStatus object, but I am a novice in programming and don't know which section to add to achieve this. Hope to get your help, thank you.
To solve the first difficulty, you can add an event listener to the photo element which will be triggered when the image is loaded. Inside this event listener, you can set the display of the modal to 'flex' and the display of the svg element to 'none'.
To solve the second difficulty, you can use an if statement inside your rankStatusArr loop. This statement should check if the rankNum is included in the rankStatus object and if so, add a class to the txt element which adds the green color style.
let txt = document.querySelectorAll('.txt');
for (let i = 0; i < txt.length; i++) {
txt[i].innerHTML = rankStatusArr[i];
if (rankNum === rankStatusArr[i]) {
txt[i].classList.add('green');
}
}

Custom cursor not growing/scaling on hover [duplicate]

Hellol,I have a code for a custom cursor, and the cursor, which is a ball/circle, was supposed to grow/scale when hovering over a link, if you see the code below, this function is there, but it is not working, does anyone know what's wrong? Thank you in advance. Note, I am unable to create a snippet here. The code is from codepen: https://codepen.io/clementGir/pen/RQqvQx
<div class="cursor">
<div class="cursor__ball cursor__ball--big ">
<svg height="30" width="30">
<circle cx="15" cy="15" r="12" stroke-width="0"></circle>
</svg>
</div>
<div class="cursor__ball cursor__ball--small">
<svg height="10" width="10">
<circle cx="5" cy="5" r="4" stroke-width="0"></circle>
</svg>
</div>
</div>
<style>
body .cursor {
pointer-events: none;
}
body .cursor__ball {
position: fixed;
top: 0;
left: 0;
mix-blend-mode: difference;
z-index: 1000;
}
body .cursor__ball circle {
fill: #f7f8fa;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
<script>
const $bigBall = document.querySelector('.cursor__ball--big');
const $smallBall = document.querySelector('.cursor__ball--small');
const $hoverables = document.querySelectorAll('a');
// Listeners
document.body.addEventListener('mousemove', onMouseMove);
for (let i = 0; i < $hoverables.length; i++) {
if (window.CP.shouldStopExecution(0)) break;
$hoverables[i].addEventListener('mouseenter', onMouseHover);
$hoverables[i].addEventListener('mouseleave', onMouseHoverOut);
}
// Move the cursor
window.CP.exitedLoop(0); function onMouseMove(e) {
TweenMax.to($bigBall, .4, {
x: e.clientX - 15,
y: e.clientY - 15
});
TweenMax.to($smallBall, .1, {
x: e.clientX - 5,
y: e.clientY - 7
});
}
// Hover an element
function onMouseHover() {
TweenMax.to($bigBall, .3, {
scale: 4
});
}
function onMouseHoverOut() {
TweenMax.to($bigBall, .3, {
scale: 1
});
}
</script>```
Growing cursor on hovering a link.
The codepen you are trying to copy links to external scripts and has been adapted to work inside codepen.
What I did to get the pen to work here (I think you only needed step 4):
For CSS & JS clicked down arrow top right, view compiled
Copy over all code
Settings>JS>External Scripts>Copy link into Stack Overflow snippet's external scripts
Removed 'window.CP' code junk which I assume is something to do with codepen and getting the window object of their sub-document
const $bigBall = document.querySelector('.cursor__ball--big');
const $smallBall = document.querySelector('.cursor__ball--small');
const $hoverables = document.querySelectorAll('.hoverable');
// Listeners
document.body.addEventListener('mousemove', onMouseMove);
for (let i = 0; i < $hoverables.length; i++) {
$hoverables[i].addEventListener('mouseenter', onMouseHover);
$hoverables[i].addEventListener('mouseleave', onMouseHoverOut);
}
// Move the cursor
function onMouseMove(e) {
TweenMax.to($bigBall, .4, {
x: e.pageX - 15,
y: e.pageY - 15 });
TweenMax.to($smallBall, .1, {
x: e.pageX - 5,
y: e.pageY - 7 });
}
// Hover an element
function onMouseHover() {
TweenMax.to($bigBall, .3, {
scale: 4 });
}
function onMouseHoverOut() {
TweenMax.to($bigBall, .3, {
scale: 1 });
}
body {
height: 100vh;
background: #010101;
cursor: none;
margin: 0;
display: flex;
font-family: monospace;
}
body h1,
body p,
body a {
color: #fff;
}
body a {
border-bottom: 2px solid #fff;
padding: 10px 0;
margin-top: 25px;
}
body .cursor {
pointer-events: none;
}
body .cursor__ball {
position: fixed;
top: 0;
left: 0;
mix-blend-mode: difference;
z-index: 1000;
}
body .cursor__ball circle {
fill: #f7f8fa;
}
body .left,
body .right {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
body .right {
background: #fff;
}
body .right a {
border-bottom: 2px solid #000;
}
body .right h1,
body .right p,
body .right a {
color: #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
<div class="cursor">
<div class="cursor__ball cursor__ball--big ">
<svg height="30" width="30">
<circle cx="15" cy="15" r="12" stroke-width="0"></circle>
</svg>
</div>
<div class="cursor__ball cursor__ball--small">
<svg height="10" width="10">
<circle cx="5" cy="5" r="4" stroke-width="0"></circle>
</svg>
</div>
</div>
<div class="left">
<h1>Hello</h1>
<p>Check out this link:</p>
<a class="hoverable">Hover meh</a>
</div>
<div class="right">
<h1>Hello</h1>
<p>Check out this link:</p>
<a class="hoverable">Hover meh</a>
</div>

jQuery .each update automatically and create global variable?

I'm very new to code. I'm trying to create a scrolling effect using greensock draggable. I'd like my div to drag only if my content of another div exceeds the height value of it's parent's height. Here is the codepen I'm working on - https://codepen.io/kbeats/pen/vVYGOX
So far, I have for my dragging and scrolling code:
Draggable.create("#scrollIcon", {
type: "y",
bounds: "#toc",
onDrag: scroll
});
$(".tile").each(function(){
var height = $(this).height();
});
function scroll(){
if(height > $(toc).height()){
TweenMax.set(".tile", {y: this.y * -1})
}
}
The console.log is saying 'height' is undefined, so I'm guessing it's only being stored as a local variable? Is there a way to iterate through each .tile class and create a global variable of the sum total height that will update automatically when the height changes? (Or maybe just update each time a .tile element is clicked?)
Ultimately, I'm trying to have my 'scroll' div only scroll when the .tile class content exceeds the parent (#toc) height and then somehow create an equation where the scroll amount will adjust depending on the total height of the .tile class. (So it will always be able to scroll through all the content, but not overshoot).
The problem is that in this section:
$(".tile").each(function(){
var height = $(this).height();
});
height is only available in that scope. Also, you want to sum up the heights, not just the height of one of the .tiles. Instead, you should do the each inside of the scroll function, and sum up the heights.
Also, you will want to scale the tween by the height ratio between the tile total and the toc, because the amount you'll want to scroll is not only based on the absolute height of the scrollbar. Instead of this.y * -1, you probably want something more like this.y * height / $(toc).height() * -1.
function scroll(){
var height= 0;
$('.tile').each(function() {
height += $(this).height();
});
if(height > $(toc).height()) {
TweenMax.set(".tile", {
y: this.y * height / $(toc).height() * -1
});
}
var base = document.getElementById("Base");
var tab = document.getElementById("Tab");
var arrows = document.getElementById("tocArrows");
var content = document.getElementsByClassName("tile");
var toc = document.getElementById("toc");
//
// variables for slides
var oneSlideOne = document.getElementById("oneSlideOne");
var oneSlideTwo = document.getElementById("oneSlideTwo");
var twoSlideOne = document.getElementById("twoSlideOne");
var twoSlideTwo = document.getElementById("twoSlideTwo");
// menu open and close timeline
var tl = new TimelineMax({
paused: true,
reversed: true
});
tl.to(base, .5, {
x: 280,
ease: Sine.easeInOut
});
tl.to("#start1", 0.8, {
morphSVG: "#end1",
ease: Back.easeInOut
}, 0);
tl.to("#start2", 0.8, {
morphSVG: "#end2",
ease: Back.easeInOut
}, 0);
arrows.addEventListener("mousedown", openMenu);
function openMenu() {
tl.reversed() ? tl.play() : tl.reverse();
}
// making the accordion menu
var acc = document.getElementsByClassName("tile");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener('click', function() {
this.classList.toggle("active");
var $panel = $("ul, li", this);
this.classList.contains("active") ? $panel.show() : $panel.hide();
})
}
// gsap for custom scroll bar?
Draggable.create("#scrollIcon", {
type: "y",
bounds: "#toc",
onDrag: scroll
});
function scroll() {
var height = 0;
$('.tile').each(function() {
height += $(this).height();
});
if (height > $(toc).height()) {
TweenMax.set(".tile", {
y: this.y * height / $(toc).height() * -1
})
}
}
.base {
height: 250px;
width: 280px;
background-color: #17307F;
position: absolute;
left: -280px;
top: 0px;
}
.tab {
background-color: #17307F;
position: absolute;
width: 80px;
height: 80px;
left: 280px;
border-radius: 0px 0px 20px 0px;
}
#scrollIcon {
background-color: white;
width: 25px;
height: 50px;
position: relative;
float: right;
margin-top: 20px;
margin-right: 15px;
border-radius: 30px;
cursor: pointer;
}
#tocArrows {
width: 50px;
height: 50px;
display: inline;
margin: 0 auto;
padding-top: 15px;
padding-left: 15px;
cursor: pointer;
}
#tocReverse {
width: 50px;
height: 50px;
display: inline;
margin: 0 auto;
cursor: pointer;
position: relative;
top: -52px;
visibility: hidden;
}
ul#toc {
list-style: none;
display: block;
height: 200px;
overflow: hidden;
/*overflow-y: scroll;
overflow-x: hidden;*/
}
li {
position: relative;
left: -40px;
text-decoration: none;
display: block;
}
li .subTile {
display: none;
}
.tile {
background-color: #74A3EB;
width: 220px;
padding-top: 10px;
padding-bottom: 10px;
border-radius: 10px;
color: white;
font-family: lato;
font-weight: 700;
font-size: 24px;
line-height: 40px;
text-align: center;
cursor: pointer;
margin: 10px 2px 0px 10px;
transition: 0.4s ease-in-out;
display: block;
top: 0px;
left: -40px;
}
.active,
.tile:hover {
background-color: #3C72F0;
/* change this color */
}
.subTile {
display: none;
position: relative;
background-color: #99B4FF;
width: 200px;
height: 40px;
border-radius: 10px;
display: none;
overflow: hidden;
max-height: 100%;
color: white;
font-family: lato;
text-align: center;
line-height: 40px;
font-size: 20px;
margin: 0px 10px 6px 10px;
cursor: default;
transition: background-color 0.2s ease-in-out;
}
.subTile:hover {
background-color: #F2BB22;
}
#twoSlideTwo {
font-size: 16px;
height: 60px;
line-height: 30px;
}
#twoSlideThree {
font-size: 16px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.0.2/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.0.1/utils/Draggable.min.js"></script>
<div class=base id="Base">
<div class=tab id="Tab">
<svg id="tocArrows" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 47.95 63.28"><title>tocArrows</title><polyline id="start1" points="15.07 3 43.71 31.64 15.07 60.28" fill="none" stroke="#fff" stroke-linecap="round" stroke-miterlimit="10" stroke-width="6"/><polyline id="start2" points="3 9.96 24.68 31.64 3 53.32" fill="none" stroke="#fff" stroke-linecap="round" stroke-miterlimit="10" stroke-width="6"/></svg>
<svg id="tocReverse" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 47.95 63.28"><title>tocReverse</title><polyline id="end1" points="32.88 60.28 4.24 31.64 32.88 3" fill="none" stroke="#fff" stroke-linecap="round" stroke-miterlimit="10" stroke-width="6"/><polyline id="end2" points="44.95 53.32 23.27 31.64 44.95 9.96" fill="none" stroke="#fff" stroke-linecap="round" stroke-miterlimit="10" stroke-width="6"/></svg>
</div>
<div class="scrollPane" id="scrollIcon"> </div>
<ul id="toc">
<li class="tile" id="moduleOneTitle"> Module One
<ul>
<li class="subTile modOne" id="oneSlideOne"> Title Slide </li>
<li class="subTile modOne" id="oneSlideTwo"> References </li>
<li class="subTile modOne" id="oneSlideThree"> Introduction </li>
<li class="subTile modOne" id="oneSlideFour"> Next Slide </li>
</ul>
</li>
<li class=tile id="moduleTwoTitle"> Module Two
<ul>
<li class="subTile modTwo" id="twoSlideOne"> Title Slide </li>
<li class="subTile modTwo" id="twoSlideTwo"> Third Slide Long Name </li>
<li class="subTile modTwo" id="twoSlideThree"> Fourth Slide Long </li>
</ul>
</li>
</ul>
</div>

build semi circle progress bar with round corners and shadow in java script and CSS

I searched a lot and finding nothing on it. I want to make a progress bar with round corners.progress bar need to have shadow. All I did as of now is here :
$(".progress-bar").each(function(){
var bar = $(this).find(".bar");
var val = $(this).find("span");
var per = parseInt( val.text(), 10);
$({p:0}).animate({p:per}, {
duration: 3000,
easing: "swing",
step: function(p) {
bar.css({
transform: "rotate("+ (45+(p*1.8)) +"deg)"
});
val.text(p|0);
}
});
});
body{
background-color:#3F63D3;
}
.progress-bar{
position: relative;
margin: 4px;
float:left;
text-align: center;
}
.barOverflow{
position: relative;
overflow: hidden;
width: 150px; height: 70px;
margin-bottom: -14px;
}
.bar{
position: absolute;
top: 0; left: 0;
width: 150px; height: 150px;
border-radius: 50%;
box-sizing: border-box;
border: 15px solid gray;
border-bottom-color: white;
border-right-color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="progress-bar">
<div class="barOverflow">
<div class="bar"></div>
</div>
<span>100</span>%
</div>
I want to make corners round and having shadow. below given image represent what actually i want. Shadow is missing because i don't know to draw. :
I have tried Progressbar.js also, but I don't have much knowledge about SVG. Any answer would be appreciated.
#jaromanda for suggestion of learning SVG.
Yes is looks very hard to achieve from border-radius. So i looked into SVG and find it pretty handy. Here is my snippet:
// progressbar.js#1.0.0 version is used
// Docs: http://progressbarjs.readthedocs.org/en/1.0.0/
var bar = new ProgressBar.SemiCircle(container, {
strokeWidth: 10,
color: 'red',
trailColor: '#eee',
trailWidth: 10,
easing: 'easeInOut',
duration: 1400,
svgStyle: null,
text: {
value: '',
alignToBottom: false
},
// Set default step function for all animate calls
step: (state, bar) => {
bar.path.setAttribute('stroke', state.color);
var value = Math.round(bar.value() * 100);
if (value === 0) {
bar.setText('');
} else {
bar.setText(value+"%");
}
bar.text.style.color = state.color;
}
});
bar.text.style.fontFamily = '"Raleway", Helvetica, sans-serif';
bar.text.style.fontSize = '2rem';
bar.animate(0.45); // Number from 0.0 to 1.0
#container {
width: 200px;
height: 100px;
}
svg {
height: 120px;
width: 200px;
fill: none;
stroke: red;
stroke-width: 10;
stroke-linecap: round;
-webkit-filter: drop-shadow( -3px -2px 5px gray );
filter: drop-shadow( -3px -2px 5px gray );
}
<script src="https://rawgit.com/kimmobrunfeldt/progressbar.js/1.0.0/dist/progressbar.js"></script>
<link href="https://fonts.googleapis.com/css?family=Raleway:400,300,600,800,900" rel="stylesheet" type="text/css">
<div id="container"></div>
I want to suggest some stupid but quick solution since you're already using position: absolute. You can add background color to the circles when your animation starts.
html:
<div class="progress-bar">
<div class="left"></div>
<div class="right"><div class="back"></div></div>
<div class="barOverflow">
<div class="bar"></div>
</div>
<span>0</span>%
</div>
css:
/** all your css here **/
body{
background-color:#3F63D3;
}
.progress-bar{
position: relative;
margin: 4px;
float: left;
text-align: center;
}
.barOverflow{
position: relative;
overflow: hidden;
width: 150px; height: 70px;
margin-bottom: -14px;
}
.bar{
position: absolute;
top: 0; left: 0;
width: 150px; height: 150px;
border-radius: 50%;
box-sizing: border-box;
border: 15px solid gray;
border-bottom-color: white;
border-right-color: white;
transform: rotate(45deg);
}
.progress-bar > .left {
position: absolute;
background: white;
width: 15px;
height: 15px;
border-radius: 50%;
left: 0;
bottom: -4px;
overflow: hidden;
}
.progress-bar > .right {
position: absolute;
background: white;
width: 15px;
height: 15px;
border-radius: 50%;
right: 0;
bottom: -4px;
overflow: hidden;
}
.back {
width: 15px;
height: 15px;
background: gray;
position: absolute;
}
jquery:
$(".progress-bar").each(function(){
var bar = $(this).find(".bar");
var val = $(this).find("span");
var per = parseInt( val.text(), 10);
var $right = $('.right');
var $back = $('.back');
$({p:0}).animate({p:per}, {
duration: 3000,
step: function(p) {
bar.css({
transform: "rotate("+ (45+(p*1.8)) +"deg)"
});
val.text(p|0);
}
}).delay( 200 );
if (per == 100) {
$back.delay( 2600 ).animate({'top': '18px'}, 200 );
}
if (per == 0) {
$('.left').css('background', 'gray');
}
});
https://jsfiddle.net/y86qs0a9/7/
Same as the answers above, I found it much easier to implement using SVG instead of pure CSS.
However I couldn't find a single simplistic implementation using only HTML and CSS, or at least with no libraries, no external scripts or no dependencies. I found that given the math that needs to be calculated to make the SVG transformations to represent the percentage, JS needs to be included (if someone knows how to achieve this with only HTML and CSS I'd love to learn how). But what the JS script does is not long or complex enough to justify the overhead of adding yet another dependency to my codebase.
The JS calculations are pretty easy once you read through. You need to calculate the coordinate for the end point of the gauge in the coordinate system of the SVG. so basic trig.
Most of the CSS is not even needed and I added just to style it and to make it pretty. You can add shadow or gradients same as you could with any HTML pure shape.
Here is the codePen https://codepen.io/naticaceres/pen/QWQeyGX
You can easily tinker with this code to achieve any kind of shape of circular gauge (full circle, lower half of the semi-circle, or any variation including ellipsis).
Hope this is helpful.
// # Thanks to mxle for the first rounded corner CSS only solution https://stackoverflow.com/a/42478006/4709712
// # Thanks to Aniket Naik for the styling and the basic idea and implementation https://codepen.io/naikus/pen/BzZoLL
// - Aniket Naik has a library, linked to that codepen you should check out if you don't want to copy-paste or implement yourself
// the arc radius in the meter-value needs to stay the same, and must always be x=y, not lower than the possible circle that can connect the two points (otherwise the ratio is not preserved and the curvature doesn't match the background path).
// to style the gauge, make it bigger or smaller, play with its parent element and transform scale. don't edit width and height of SVG directly
function percentageInRadians(percentage) {
return percentage * (Math.PI / 100);
}
function setGaugeValue(gaugeElement, percentage, color) {
const gaugeRadius = 65;
const startingY = 70;
const startingX = 10;
const zeroBasedY = gaugeRadius * Math.sin(percentageInRadians(percentage));
const y = -zeroBasedY + startingY;
const zeroBasedX = gaugeRadius * Math.cos(percentageInRadians(percentage));
const x = -zeroBasedX + gaugeRadius + startingX;
// # uncomment this to log the calculations of the coordinates for the final point of the gauge value path.
//console.log(
// `percentage: ${percentage}, zeroBasedY: ${zeroBasedY}, y: ${y}, zeroBasedX: ${zeroBasedX}, x: ${x}`
//);
gaugeElement.innerHTML = `<path d="M ${startingX} ${startingY}
A ${gaugeRadius} ${gaugeRadius} 0 0 1 ${x} ${y}
" stroke="${color}" stroke-width="10" stroke-linecap="round" />`;
}
percentageChangedEvent = (gauge, newPercentage, color) => {
const percentage =
newPercentage > 100 ? 100 : newPercentage < 0 ? 0 : newPercentage;
setGaugeValue(gauge, percentage, color);
};
function initialGaugeSetup(gaugeElementId, inputId, meterColor, initialValue) {
const gaugeElement = document.getElementById(gaugeElementId);
setGaugeValue(gaugeElement, 0, meterColor);
const inputElement = document.getElementById(inputId);
inputElement.value = initialValue;
setGaugeValue(gaugeElement, initialValue, meterColor);
inputElement.addEventListener("change", (event) =>
percentageChangedEvent(gaugeElement, event.target.value, meterColor)
);
}
// Gauge Initial Config
initialGaugeSetup(
"svg-graph-meter-value",
"svg-gauge-percentage-2",
"rgb(227 127 215)",
40
);
body {
background-color: rgba(0, 0, 0, 0.8);
color: #999;
font-family: Hevletica, sans-serif;
}
/* SVG Path implementation */
.svg-container {
margin: 20px auto 10px;
height: 80px;
width: 150px;
}
svg {
fill: transparent;
}
.input-percent-container {
text-align: center;
}
.input-percent-container>* {
display: inline;
}
input {
text-align: right;
width: 40px;
margin: auto;
background-color: #5d5d5d;
color: white;
border-radius: 6px;
border: black;
}
<div class="svg-container">
<svg width="150" height="80" xmlns="http://www.w3.org/2000/svg">
<path d="M 10 70
A 65 65 0 1 1 140 70
" stroke="grey" stroke-width="3" stroke-linecap="round" />
<g id="svg-graph-meter-value">
</g>
</svg>
</div>
<div class="input-percent-container"><input id="svg-gauge-percentage-2" /><span>%<span/></div>

Javascript animation is not running

I'm trying to create an animated menu that slides up and down. Unfortunately it's not working. I've checked the error console and there are no syntax errors. Here's my Javascript:
function showLayer() {
var hiddenLayer = document.getElementById("mainmenu");
var layerPosition = parseInt(hiddenLayer.style.bottom);
if (layerPosition > 700) {
hiddenLayer.style.bottom = (layerPosition + 5) + "px";
setTimeout("showLayer()", 20);
}
}
function hideLayer() {
var hiddenLayer = document.getElementByID("mainmenu");
hiddenLayer.style.bottom = "700px";
}
Here's the whole context:
<script type="text/javascript">
function showLayer() {
var hiddenLayer = document.getElementById("mainmenu");
var layerPosition = parseInt(hiddenLayer.style.bottom);
if (layerPosition > 700) {
hiddenLayer.style.bottom = (layerPosition + 5) + "px";
setTimeout("showLayer()", 20);
}
}
function hideLayer() {
var hiddenLayer = document.getElementByID("mainmenu");
hiddenLayer.style.bottom = "700px";
}
</script>
<style type="text/css">
div#mainmenu { position: absolute; bottom: 700px; left: 9px; width: 600px;
height: 350px; border-style: solid; background-color: rgb(0, 0, 0) ; border-
width: 3px; border-top-right-radius: 7px; border-top-left-radius: 7px; }
div#mainbutton { position: absolute; top: 674px; left: 12px; width: 28px;
height: 28px; border-style: solid; border-color: rgb(255, 255, 255); border-width:
1px; border-radius: 4px; }
div#mainbuttontext { position: absolute; top: 679px; left: 22px; color: rgb(255, 255,
255); font-style: normal; font-size: 18px; font-family:"Arial"; }
</style>
<div id="mainbutton"></div>
<div id="mainmenu" onClick="showLayer('mainmenu')"> </div>
<div id="mainbuttontext">F</div>
</body>
I think I found your problem! It's something very strange and I can't explain it, but to get style in javascript, the css must be inline (to set a style it's not necessary).
So I modified your code by placing the css inline.
HTML :
<div id="mainmenu" style="position:absolute;bottom:100px;" onclick="showLayer('mainmenu');">Click me!</div>
<!--I wrote 100px just for the test, you can change it and modify the js-->
JS :
function showLayer()
{
var hiddenLayer=document.getElementById("mainmenu");
var layerPosition=parseInt(hiddenLayer.style.bottom);
if(layerPosition>50)
{
hiddenLayer.style.bottom=(layerPosition+5)+"px";
setTimeout("showLayer()",20);
}
}
function hideLayer()
{
var hiddenLayer=document.getElementById("mainmenu");
hiddenLayer.style.bottom="700px";
}
Fiddle : http://jsfiddle.net/8MWfV/
And here is a fiddle that shows that a not inline css doesn't works : http://jsfiddle.net/kfUrP/

Categories

Resources