Skip to content

Instantly share code, notes, and snippets.

@nawawishkid-old
Forked from anonymous/index.html
Created April 20, 2017 08:48
Show Gist options
  • Save nawawishkid-old/aab67d0ae4905ebd4fa799867cc290c7 to your computer and use it in GitHub Desktop.
Save nawawishkid-old/aab67d0ae4905ebd4fa799867cc290c7 to your computer and use it in GitHub Desktop.
COLUMN CHART BY NAWAWISH SAMERPARK // source https://jsbin.com/puwevilahe
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>COLUMN CHART BY NAWAWISH SAMERPARK</title>
<style id="jsbin-css">
html, body {
width: 100%;
height: 100%;
margin: 0;
}
.svg-chart {
width: 100%;
height: 70%;
margin: auto;
display: flex;
justify-content: center;
align-items: center;
background-color: white;
border: 1px solid black;
}
svg {
width: 95%;
height: 95%;
}
g#axis {
/*stroke: black;*/
stroke-width: .5;
}
g#tblineHorizontal {
stroke: black;
stroke-width: 0.125;
opacity: 0.5;
}
g#bar {
cursor: pointer;
}
g#bar > path:hover {
opacity: 0.7;
transition: opacity 0.2s;
}
g#label text {
font-size: 0.2em;
}
svg text {
font-size: .2em;
}
g#yInfo text {
opacity: 0;
transition: opacity 0.4s ease-out;
}
g#xInfo text {
font-weight: bold;
opacity: 0;
transition: opacity 0.8s ease-out;
}
</style>
</head>
<body>
<div class="svg-chart">
<svg id="svg" viewBox="0 0 100 100">
<g id="chartBody"
transform="translate(15,10)">
<g id="axis">
<!--line class="x-axis" x1="0" y1="80"
x2="80" y2="80"/>
<line class="y-axis" x1="0" y1="80.25"
x2="0" y2="0"/-->
</g>
<g id="value-axis-y">
</g>
<g id="tblineHorizontal"
transform="translate(-1,80)">
<!--line x1="0" y1="10" x2="80" y2="10"/>
<line x1="0" y1="20" x2="80" y2="20"/>
<line x1="0" y1="30" x2="80" y2="30"/>
<line x1="0" y1="40" x2="80" y2="40"/>
<line x1="0" y1="50" x2="80" y2="50"/>
<line x1="0" y1="60" x2="80" y2="60"/>
<line x1="0" y1="70" x2="80" y2="70"/-->
</g>
<g id="bar" transform="translate(0,80)">
<!--path d="m17.5 0 l0 -5.63 l5 0
l0 5.63 z" fill="green"/>
<path d="m57.5 0 l0 -61.05 l5 0
l0 61.05 z" fill="red"/-->
</g>
<g id="label"
transform="translate(0,80)">
<!--text x="17.5" y="-7.63">100</text>
<text x="57.5" y="-63.05">1,221</text-->
</g>
<g id="yInfo"
transform="translate(-2,80)">
<!--text class="txt" x="0" y="8">Total amount</text>
<text x="5" y="91.5">
<tspan>0</tspan>
<tspan x="0" y="81.5">200</tspan>
<tspan x="0" y="71.5">400</tspan>
<tspan x="0" y="61.5">600</tspan>
<tspan x="0" y="51.5">800</tspan>
<tspan x="0" y="41.5">1000</tspan>
<tspan x="0" y="31.5">1200</tspan>
<tspan x="0" y="21.5">1400</tspan>
</text-->
</g>
<g id="xInfo"
transform="translate(0,80)">
<!--text x="17.5" y="5">Necessary</text>
<text x="57.5" y="5">Unnecessary</text-->
</g>
</g>
<!--g id="map">
<line x1="0" y1="0" x2="0" y2="100"
stroke="blue"/>
<line x1="0" y1="0" x2="100" y2="0"
stroke="blue"/>
<line x1="100" y1="100" x2="0" y2="100"
stroke="blue"/>
<line x1="100" y1="0" x2="100" y2="100"
stroke="blue"/>
<line x1="50" y1="0" x2="50" y2="100"
stroke="red"/>
<line x1="0" y1="50" x2="100" y2="50"
stroke="red"/>
</g-->
</svg>
</div>
<div class="user-div">
<button id="btn">Run</button>
<fieldset class="data-sec">
<legend>Data</legend>
<span>Dataset name: </span>
<input data-obj="xInfo" type="text" id="dataKey"
placeholder="e.g. data1, data2, ..."/>
<br/>
<span>Dataset value: </span>
<input data-obj="value" type="text" id="dataValue"
placeholder="e.g. value1, value2, ..."/>
<br/>
<span>Name of dataset value: </span>
<input data-obj="headY" type="text" id="valueName"
placeholder="Use for Y axis"/>
<br/>
</fieldset>
<fieldset class="appearance">
<legend>Appearance</legend>
<span>Bar color: </span>
<input data-obj="colBar" type="text" id="barColor"
placeholder="name, rgb, hex, or hsl"/>
<br/>
<span>Axis color: </span>
<input data-obj="colAxis" type="text" id="axisColor"
placeholder="name, rgb, hex, or hsl"/>
<br/>
<span>Horizontal line color: </span>
<input data-obj="colLine" type="text" id="lineColor"
placeholder="name, rgb, hex, or hsl"/>
<br/>
<span>Text color: </span>
<input data-obj="colTxt" type="text" id="txtColor"
placeholder="name, rgb, hex, or hsl"/>
<br/>
<span>Background color: </span>
<input data-obj="colBg" type="text" id="bgColor"
placeholder="name, rgb, hex, or hsl"/>
<br/>
</fieldset>
</div>
<script id="jsbin-javascript">
// create default object
var chartObj = {
xInfo : ["data1",
"data2",
"data3",
"data4"],
value : [600, 60, 1006, 446, 100],
headY : "Total amount",
/*chartWidth : 80,
chartHeight : 75,*/
barWidth : 5,
chartWidth : 80,
chartHeight : 75.25,
colBar : "white",
colAxis : "white",
colLine : "wheat",
colTxt : "white",
colBg : "black"
}
// find SVG:DOM ratio for centering text
/*var svg = document.getElementById("svg"),
svgW = svg.clientWidth,
svgH = svg.clientHeight,
xNum = (svgW > svgH ? svgH : svgW),
ratio = xNum / svg.getAttributeNS(null, "viewBox").split(" ")[2];
chartObj.ratio = ratio;*/
// 1. find max value of the chart
function findMax1() {
console.log("--- findMax1() ---");
var dvsr = 1,
num = Math.max.apply(null, chartObj.value),
max1,
evenNum;
// set divisor
if(num > 9999) {
dvsr = 1000;
} else if(num > 999) {
dvsr = 100;
} else if(num > 99) {
dvsr = 10;
}
// round
var num2 = num / dvsr;
evenNum = (num2 % 2 === 0 ?
num2 : num2 + 1);
max1 = Math.ceil(evenNum) * dvsr;
max1 = (max1 === num ?
max1 += dvsr : max1);
chartObj.max1 = max1;
chartObj.dvsr = dvsr;
}
// 2. find position of x axis value (bars)
// and bars height
function findBarsPosAndHeight() {
console.log("--- findBarsPosAndHeight() ---");
var secWidth = chartObj.chartWidth / chartObj.barsNum,
barsPosX = [],
barsPosY = [];
for(var i = 0; i < chartObj.barsNum; i++) {
barsPosX[i] = ((secWidth * i) + (secWidth / 2)) - (chartObj.barWidth / 2);
barsPosY[i] = Math.floor((chartObj.value[i] / chartObj.max1) * chartObj.chartHeight);
barsPosY[i] = (barsPosY[i] === 0 ? barsPosY[i] += 1 : barsPosY[i]);
}
chartObj.barsPosX = barsPosX;
chartObj.barsPosY = barsPosY;
}
// find number of horizontal lines
function findLineNum() {
console.log("--- findLineNum() ---");
var max1 = chartObj.max1,
dvsr = chartObj.dvsr,
mtplr = (max1 === (2 * dvsr) ||
(max1 - 1) === (2 * dvsr) ?
1 : 2 * dvsr),
lineNum = max1 / mtplr;
while(lineNum < 2 ||
lineNum > 10) {
mtplr += 2 * dvsr;
lineNum = max1 / mtplr;
//console.log("mtplr = " + mtplr);
//console.log("lineNum = " + lineNum);
}
lineNum = Math.ceil(lineNum);
// set max again to match line number
var max = lineNum * mtplr;
chartObj.lineNum = lineNum;
chartObj.max = max;
chartObj.range = mtplr;
//console.log("mtplr = " + mtplr);
//console.log("lineNum = " + lineNum);
}
// find position of y axis value
function findYPos() {
console.log("--- findYPos() ---");
var linePos = (chartObj.chartHeight) / chartObj.lineNum,
linesPosY = [],
yInfo = [];
for(var i = 0; i < chartObj.lineNum; i++) {
linesPosY[i] = -Math.abs(linePos * (i + 1));
yInfo[i] = chartObj.range * (i + 1);
}
chartObj.linesPosY = linesPosY;
chartObj.yInfo = yInfo;
//console.log(chartObj);
}
// create axis
function createAxis() {
console.log("--- createAxis() ---");
var g = document.getElementById("axis");
var x = document.createElementNS(
"http://www.w3.org/2000/svg", "line");
var y = document.createElementNS(
"http://www.w3.org/2000/svg", "line");
x.setAttributeNS(null, "x1", 0);
x.setAttributeNS(null, "y1", 80);
x.setAttributeNS(null, "x2", chartObj.chartWidth);
x.setAttributeNS(null, "y2", 80);
x.setAttributeNS(null, "stroke", chartObj.colAxis);
y.setAttributeNS(null, "x1", 0);
y.setAttributeNS(null, "y1", chartObj.chartHeight + 5);
y.setAttributeNS(null, "x2", 0);
y.setAttributeNS(null, "y2", 0);
y.setAttributeNS(null, "stroke", chartObj.colAxis);
g.appendChild(x);
g.appendChild(y);
}
// create horizontal lines
function createLines() {
console.log("--- createLines() ---");
for(var i = 0; i < chartObj.lineNum; i++) {
var g = document.getElementById("tblineHorizontal");
var line = document.createElementNS(
"http://www.w3.org/2000/svg", "line");
var yVal = chartObj.linesPosY[i];
line.setAttributeNS(null, "x1", 0);
line.setAttributeNS(null, "y1", yVal);
line.setAttributeNS(null, "x2", 0);
line.setAttributeNS(null, "y2", yVal);
line.setAttributeNS(null, "stroke", chartObj.colLine);
g.appendChild(line);
}
}
// create paths (bars) and labels
function createBarsAndLabels() {
console.log("--- createBarsAndLabels() ---");
for(var i = 0; i < chartObj.barsNum; i++) {
var x = chartObj.barsPosX[i],
gBar = document.getElementById("bar"),
gLabel = document.getElementById("label"),
bar = document.createElementNS(
"http://www.w3.org/2000/svg", "path"),
label = document.createElementNS(
"http://www.w3.org/2000/svg", "text"),
d = "M" + x + " 0 l5 0";
bar.setAttributeNS(null, "d", d);
bar.setAttributeNS(null, "fill", chartObj.colBar);
label.setAttributeNS(
null, "text-anchor", "start");
label.setAttributeNS(null, "x", x);
label.setAttributeNS(null, "y", -2);
label.setAttributeNS(null, "fill", chartObj.colTxt);
label.innerHTML = 0;
gBar.appendChild(bar);
gLabel.appendChild(label);
// center label
/*var labelWidth = label.getBoundingClientRect().width,
pos = (x + (chartObj.barWidth / 2)) -
((labelWidth / chartObj.ratio) / 2);
label.setAttributeNS(null, "x", pos);
*/
}
}
// create y-axis info
function createYInfo() {
console.log("--- createYInfo() ---");
for(var i = 0; i < (chartObj.lineNum + 1); i++) {
var g = document.getElementById("yInfo"),
txt = document.createElementNS(
"http://www.w3.org/2000/svg", "text"),
posY = chartObj.linesPosY[i],
yInfo = chartObj.yInfo[i];
if((i + 1) === (chartObj.lineNum + 1)) {
txt.setAttributeNS(
null, "text-anchor", "middle");
txt.setAttributeNS(null, "x", 0);
txt.setAttributeNS(null, "y", "-" + (chartObj.chartHeight + 8));
txt.setAttributeNS(null, "fill", chartObj.colTxt);
txt.innerHTML = chartObj.headY;
g.appendChild(txt);
break;
}
txt.setAttributeNS(
null, "alignment-baseline", "central");
txt.setAttributeNS(
null, "text-anchor", "end");
txt.setAttributeNS(null, "x", 0);
txt.setAttributeNS(null, "y", posY);
txt.setAttributeNS(null, "fill", chartObj.colTxt);
txt.innerHTML = yInfo;
g.appendChild(txt);
// center text
/*var txtHeight = (txt.getBoundingClientRect().height / ratio),
pos = posY + txtHeight / 2;
txt.setAttributeNS(null, "y", pos);
console.log("txtHeight = " + txtHeight);
console.log("pos = " + pos);*/
}
}
// create x info
function createXInfo() {
console.log("--- createXInfo() ---");
var totalW = 0;
for(var i = 0; i < chartObj.barsNum; i++) {
var g = document.getElementById("xInfo"),
txt = document.createElementNS(
"http://www.w3.org/2000/svg", "text"),
posX = chartObj.barsPosX[i] +
(chartObj.barWidth / 2),
xInfo = chartObj.xInfo,
widthRatio = chartObj.chartWidth / chartObj.barsNum,
y = 3;
txt.innerHTML = xInfo[i];
txt.setAttributeNS(
null, "alignment-baseline", "central");
txt.setAttributeNS(
null, "text-anchor", "middle");
txt.setAttributeNS(null, "x", posX);
txt.setAttributeNS(null, "fill", chartObj.colTxt);
g.appendChild(txt);
// spacing the text properly
if(i > 0) {
var prev = txt.previousElementSibling;
if((prev.getBBox().width > widthRatio ||
txt.getBBox().width > widthRatio) &&
prev.getAttributeNS(null, "y") == 3) {
y = 7;
} else {
y = 3;
}
}
txt.setAttributeNS(null, "y", y);
//console.log("txtW = " + txt.getBBox().width);
totalW += txt.getBBox().width;
// find string width to center the string
/*var txtWidth = txt.getBoundingClientRect().width / chartObj.ratio,
pos = posX - (txtWidth / 2);
//console.log("boundingWidth = " + txt.getBoundingClientRect().width);
//console.log("pos = " + pos);
txt.setAttributeNS(null, "x", pos);*/
}
}
// path animation function
function animateBarsAndLabels(bar, label, barEnd, labelValue) {
console.log("--- animateBarsAndLabels() ---");
var y = 0,
num = 0,
add = labelValue / barEnd,
t = 100 / barEnd,
d = bar.getAttributeNS(null, "d"),
myPath,
int = setInterval(function() {
if(y >= barEnd) {
clearInterval(int);
} else {
y++;
num += add;
myPath = " l0 -" + y + " l-" +
chartObj.barWidth + " 0";
bar.setAttributeNS(
null, "d", d +
myPath + " Z");
label.setAttributeNS(null, "y",
"-" + (3 + y));
label.innerHTML = num.toFixed();
}
}, t);
}
// line animation function
function animateLine(elem) {
console.log("--- animateLine() ---");
var x = 0,
end = chartObj.chartWidth,
t = 100 / end,
int = setInterval(function() {
if(x >= end) {
clearInterval(int);
} else {
x++;
elem.setAttributeNS(null, "x2", x);
elem.setAttributeNS(null, "y2",
elem.getAttributeNS(null, "y1"));
}
}, t);
}
// display yInfo and lines
function displayYInfoAndLines() {
console.log("--- displayYInfoAndLines() ---");
var y = document.querySelectorAll("g#yInfo text");
var line = document.querySelectorAll("#tblineHorizontal line");
var i = 0;
var int = setInterval(function() {
if(!y[i]) {
clearInterval(int);
} else {
y[i].style.opacity = 1;
if(line[i]) {
animateLine(line[i]);
}
i++;
}
}, 50);
}
// display xInfo and bars
function displayXInfoAndBars() {
console.log("--- displayXInfoAndBars() ---");
var x = document.querySelectorAll("#xInfo text");
var bars = document.querySelectorAll("#bar path");
var labels = document.querySelectorAll("#label text");
var i = 0;
var int = setInterval(function() {
if(!x[i]) {
clearInterval(int);
} else {
x[i].style.opacity = 1;
animateBarsAndLabels(bars[i], labels[i],
chartObj.barsPosY[i],
chartObj.value[i]);
i++;
}
}, 100);
}
// remove all svg elements in each svg 'g' element
function clearSVG() {
console.log("--- clearSVG() ---");
var g = document.querySelectorAll("svg g#chartBody g");
for(var i = 0; g[i]; i++) {
g[i].innerHTML = null;
}
}
var btn = document.getElementById("btn");
btn.onclick = function() {
console.log("--- btn.onclick() ---");
// get data from user
var inputs = document.getElementsByTagName("input");
for(var i = 0; inputs[i]; i++) {
var ip = inputs[i];
if(ip.value === "") {continue;}
var val;
if(ip.value.includes(", ")) {
val = ip.value.split(", ");
} else {val = ip.value;}
chartObj[ip.dataset.obj] = val;
}
clearSVG();
findMax1();
// find number of bars
chartObj.barsNum = chartObj.xInfo.length;
findBarsPosAndHeight();
findLineNum();
findYPos();
var svg = document.getElementById("svg");
svg.style.backgroundColor = chartObj.colBg;
var axis = document.querySelectorAll("#axis line");
for(i = 0; axis[i]; i++) {
axis[i].setAttributeNS(
null, "stroke", chartObj.colAxis);
}
console.log(chartObj);
createAxis();
createYInfo();
createXInfo();
createLines();
createBarsAndLabels();
displayYInfoAndLines();
displayXInfoAndBars();
}
</script>
<script id="jsbin-source-css" type="text/css">html, body {
width: 100%;
height: 100%;
margin: 0;
}
.svg-chart {
width: 100%;
height: 70%;
margin: auto;
display: flex;
justify-content: center;
align-items: center;
background-color: white;
border: 1px solid black;
}
svg {
width: 95%;
height: 95%;
}
g#axis {
/*stroke: black;*/
stroke-width: .5;
}
g#tblineHorizontal {
stroke: black;
stroke-width: 0.125;
opacity: 0.5;
}
g#bar {
cursor: pointer;
}
g#bar > path:hover {
opacity: 0.7;
transition: opacity 0.2s;
}
g#label text {
font-size: 0.2em;
}
svg text {
font-size: .2em;
}
g#yInfo text {
opacity: 0;
transition: opacity 0.4s ease-out;
}
g#xInfo text {
font-weight: bold;
opacity: 0;
transition: opacity 0.8s ease-out;
}</script>
<script id="jsbin-source-javascript" type="text/javascript">
// create default object
var chartObj = {
xInfo : ["data1",
"data2",
"data3",
"data4"],
value : [600, 60, 1006, 446, 100],
headY : "Total amount",
/*chartWidth : 80,
chartHeight : 75,*/
barWidth : 5,
chartWidth : 80,
chartHeight : 75.25,
colBar : "white",
colAxis : "white",
colLine : "wheat",
colTxt : "white",
colBg : "black"
}
// find SVG:DOM ratio for centering text
/*var svg = document.getElementById("svg"),
svgW = svg.clientWidth,
svgH = svg.clientHeight,
xNum = (svgW > svgH ? svgH : svgW),
ratio = xNum / svg.getAttributeNS(null, "viewBox").split(" ")[2];
chartObj.ratio = ratio;*/
// 1. find max value of the chart
function findMax1() {
console.log("--- findMax1() ---");
var dvsr = 1,
num = Math.max.apply(null, chartObj.value),
max1,
evenNum;
// set divisor
if(num > 9999) {
dvsr = 1000;
} else if(num > 999) {
dvsr = 100;
} else if(num > 99) {
dvsr = 10;
}
// round
var num2 = num / dvsr;
evenNum = (num2 % 2 === 0 ?
num2 : num2 + 1);
max1 = Math.ceil(evenNum) * dvsr;
max1 = (max1 === num ?
max1 += dvsr : max1);
chartObj.max1 = max1;
chartObj.dvsr = dvsr;
}
// 2. find position of x axis value (bars)
// and bars height
function findBarsPosAndHeight() {
console.log("--- findBarsPosAndHeight() ---");
var secWidth = chartObj.chartWidth / chartObj.barsNum,
barsPosX = [],
barsPosY = [];
for(var i = 0; i < chartObj.barsNum; i++) {
barsPosX[i] = ((secWidth * i) + (secWidth / 2)) - (chartObj.barWidth / 2);
barsPosY[i] = Math.floor((chartObj.value[i] / chartObj.max1) * chartObj.chartHeight);
barsPosY[i] = (barsPosY[i] === 0 ? barsPosY[i] += 1 : barsPosY[i]);
}
chartObj.barsPosX = barsPosX;
chartObj.barsPosY = barsPosY;
}
// find number of horizontal lines
function findLineNum() {
console.log("--- findLineNum() ---");
var max1 = chartObj.max1,
dvsr = chartObj.dvsr,
mtplr = (max1 === (2 * dvsr) ||
(max1 - 1) === (2 * dvsr) ?
1 : 2 * dvsr),
lineNum = max1 / mtplr;
while(lineNum < 2 ||
lineNum > 10) {
mtplr += 2 * dvsr;
lineNum = max1 / mtplr;
//console.log("mtplr = " + mtplr);
//console.log("lineNum = " + lineNum);
}
lineNum = Math.ceil(lineNum);
// set max again to match line number
var max = lineNum * mtplr;
chartObj.lineNum = lineNum;
chartObj.max = max;
chartObj.range = mtplr;
//console.log("mtplr = " + mtplr);
//console.log("lineNum = " + lineNum);
}
// find position of y axis value
function findYPos() {
console.log("--- findYPos() ---");
var linePos = (chartObj.chartHeight) / chartObj.lineNum,
linesPosY = [],
yInfo = [];
for(var i = 0; i < chartObj.lineNum; i++) {
linesPosY[i] = -Math.abs(linePos * (i + 1));
yInfo[i] = chartObj.range * (i + 1);
}
chartObj.linesPosY = linesPosY;
chartObj.yInfo = yInfo;
//console.log(chartObj);
}
// create axis
function createAxis() {
console.log("--- createAxis() ---");
var g = document.getElementById("axis");
var x = document.createElementNS(
"http://www.w3.org/2000/svg", "line");
var y = document.createElementNS(
"http://www.w3.org/2000/svg", "line");
x.setAttributeNS(null, "x1", 0);
x.setAttributeNS(null, "y1", 80);
x.setAttributeNS(null, "x2", chartObj.chartWidth);
x.setAttributeNS(null, "y2", 80);
x.setAttributeNS(null, "stroke", chartObj.colAxis);
y.setAttributeNS(null, "x1", 0);
y.setAttributeNS(null, "y1", chartObj.chartHeight + 5);
y.setAttributeNS(null, "x2", 0);
y.setAttributeNS(null, "y2", 0);
y.setAttributeNS(null, "stroke", chartObj.colAxis);
g.appendChild(x);
g.appendChild(y);
}
// create horizontal lines
function createLines() {
console.log("--- createLines() ---");
for(var i = 0; i < chartObj.lineNum; i++) {
var g = document.getElementById("tblineHorizontal");
var line = document.createElementNS(
"http://www.w3.org/2000/svg", "line");
var yVal = chartObj.linesPosY[i];
line.setAttributeNS(null, "x1", 0);
line.setAttributeNS(null, "y1", yVal);
line.setAttributeNS(null, "x2", 0);
line.setAttributeNS(null, "y2", yVal);
line.setAttributeNS(null, "stroke", chartObj.colLine);
g.appendChild(line);
}
}
// create paths (bars) and labels
function createBarsAndLabels() {
console.log("--- createBarsAndLabels() ---");
for(var i = 0; i < chartObj.barsNum; i++) {
var x = chartObj.barsPosX[i],
gBar = document.getElementById("bar"),
gLabel = document.getElementById("label"),
bar = document.createElementNS(
"http://www.w3.org/2000/svg", "path"),
label = document.createElementNS(
"http://www.w3.org/2000/svg", "text"),
d = "M" + x + " 0 l5 0";
bar.setAttributeNS(null, "d", d);
bar.setAttributeNS(null, "fill", chartObj.colBar);
label.setAttributeNS(
null, "text-anchor", "start");
label.setAttributeNS(null, "x", x);
label.setAttributeNS(null, "y", -2);
label.setAttributeNS(null, "fill", chartObj.colTxt);
label.innerHTML = 0;
gBar.appendChild(bar);
gLabel.appendChild(label);
// center label
/*var labelWidth = label.getBoundingClientRect().width,
pos = (x + (chartObj.barWidth / 2)) -
((labelWidth / chartObj.ratio) / 2);
label.setAttributeNS(null, "x", pos);
*/
}
}
// create y-axis info
function createYInfo() {
console.log("--- createYInfo() ---");
for(var i = 0; i < (chartObj.lineNum + 1); i++) {
var g = document.getElementById("yInfo"),
txt = document.createElementNS(
"http://www.w3.org/2000/svg", "text"),
posY = chartObj.linesPosY[i],
yInfo = chartObj.yInfo[i];
if((i + 1) === (chartObj.lineNum + 1)) {
txt.setAttributeNS(
null, "text-anchor", "middle");
txt.setAttributeNS(null, "x", 0);
txt.setAttributeNS(null, "y", "-" + (chartObj.chartHeight + 8));
txt.setAttributeNS(null, "fill", chartObj.colTxt);
txt.innerHTML = chartObj.headY;
g.appendChild(txt);
break;
}
txt.setAttributeNS(
null, "alignment-baseline", "central");
txt.setAttributeNS(
null, "text-anchor", "end");
txt.setAttributeNS(null, "x", 0);
txt.setAttributeNS(null, "y", posY);
txt.setAttributeNS(null, "fill", chartObj.colTxt);
txt.innerHTML = yInfo;
g.appendChild(txt);
// center text
/*var txtHeight = (txt.getBoundingClientRect().height / ratio),
pos = posY + txtHeight / 2;
txt.setAttributeNS(null, "y", pos);
console.log("txtHeight = " + txtHeight);
console.log("pos = " + pos);*/
}
}
// create x info
function createXInfo() {
console.log("--- createXInfo() ---");
var totalW = 0;
for(var i = 0; i < chartObj.barsNum; i++) {
var g = document.getElementById("xInfo"),
txt = document.createElementNS(
"http://www.w3.org/2000/svg", "text"),
posX = chartObj.barsPosX[i] +
(chartObj.barWidth / 2),
xInfo = chartObj.xInfo,
widthRatio = chartObj.chartWidth / chartObj.barsNum,
y = 3;
txt.innerHTML = xInfo[i];
txt.setAttributeNS(
null, "alignment-baseline", "central");
txt.setAttributeNS(
null, "text-anchor", "middle");
txt.setAttributeNS(null, "x", posX);
txt.setAttributeNS(null, "fill", chartObj.colTxt);
g.appendChild(txt);
// spacing the text properly
if(i > 0) {
var prev = txt.previousElementSibling;
if((prev.getBBox().width > widthRatio ||
txt.getBBox().width > widthRatio) &&
prev.getAttributeNS(null, "y") == 3) {
y = 7;
} else {
y = 3;
}
}
txt.setAttributeNS(null, "y", y);
//console.log("txtW = " + txt.getBBox().width);
totalW += txt.getBBox().width;
// find string width to center the string
/*var txtWidth = txt.getBoundingClientRect().width / chartObj.ratio,
pos = posX - (txtWidth / 2);
//console.log("boundingWidth = " + txt.getBoundingClientRect().width);
//console.log("pos = " + pos);
txt.setAttributeNS(null, "x", pos);*/
}
}
// path animation function
function animateBarsAndLabels(bar, label, barEnd, labelValue) {
console.log("--- animateBarsAndLabels() ---");
var y = 0,
num = 0,
add = labelValue / barEnd,
t = 100 / barEnd,
d = bar.getAttributeNS(null, "d"),
myPath,
int = setInterval(function() {
if(y >= barEnd) {
clearInterval(int);
} else {
y++;
num += add;
myPath = " l0 -" + y + " l-" +
chartObj.barWidth + " 0";
bar.setAttributeNS(
null, "d", d +
myPath + " Z");
label.setAttributeNS(null, "y",
"-" + (3 + y));
label.innerHTML = num.toFixed();
}
}, t);
}
// line animation function
function animateLine(elem) {
console.log("--- animateLine() ---");
var x = 0,
end = chartObj.chartWidth,
t = 100 / end,
int = setInterval(function() {
if(x >= end) {
clearInterval(int);
} else {
x++;
elem.setAttributeNS(null, "x2", x);
elem.setAttributeNS(null, "y2",
elem.getAttributeNS(null, "y1"));
}
}, t);
}
// display yInfo and lines
function displayYInfoAndLines() {
console.log("--- displayYInfoAndLines() ---");
var y = document.querySelectorAll("g#yInfo text");
var line = document.querySelectorAll("#tblineHorizontal line");
var i = 0;
var int = setInterval(function() {
if(!y[i]) {
clearInterval(int);
} else {
y[i].style.opacity = 1;
if(line[i]) {
animateLine(line[i]);
}
i++;
}
}, 50);
}
// display xInfo and bars
function displayXInfoAndBars() {
console.log("--- displayXInfoAndBars() ---");
var x = document.querySelectorAll("#xInfo text");
var bars = document.querySelectorAll("#bar path");
var labels = document.querySelectorAll("#label text");
var i = 0;
var int = setInterval(function() {
if(!x[i]) {
clearInterval(int);
} else {
x[i].style.opacity = 1;
animateBarsAndLabels(bars[i], labels[i],
chartObj.barsPosY[i],
chartObj.value[i]);
i++;
}
}, 100);
}
// remove all svg elements in each svg 'g' element
function clearSVG() {
console.log("--- clearSVG() ---");
var g = document.querySelectorAll("svg g#chartBody g");
for(var i = 0; g[i]; i++) {
g[i].innerHTML = null;
}
}
var btn = document.getElementById("btn");
btn.onclick = function() {
console.log("--- btn.onclick() ---");
// get data from user
var inputs = document.getElementsByTagName("input");
for(var i = 0; inputs[i]; i++) {
var ip = inputs[i];
if(ip.value === "") {continue;}
var val;
if(ip.value.includes(", ")) {
val = ip.value.split(", ");
} else {val = ip.value;}
chartObj[ip.dataset.obj] = val;
}
clearSVG();
findMax1();
// find number of bars
chartObj.barsNum = chartObj.xInfo.length;
findBarsPosAndHeight();
findLineNum();
findYPos();
var svg = document.getElementById("svg");
svg.style.backgroundColor = chartObj.colBg;
var axis = document.querySelectorAll("#axis line");
for(i = 0; axis[i]; i++) {
axis[i].setAttributeNS(
null, "stroke", chartObj.colAxis);
}
console.log(chartObj);
createAxis();
createYInfo();
createXInfo();
createLines();
createBarsAndLabels();
displayYInfoAndLines();
displayXInfoAndBars();
}
</script></body>
</html>
html, body {
width: 100%;
height: 100%;
margin: 0;
}
.svg-chart {
width: 100%;
height: 70%;
margin: auto;
display: flex;
justify-content: center;
align-items: center;
background-color: white;
border: 1px solid black;
}
svg {
width: 95%;
height: 95%;
}
g#axis {
/*stroke: black;*/
stroke-width: .5;
}
g#tblineHorizontal {
stroke: black;
stroke-width: 0.125;
opacity: 0.5;
}
g#bar {
cursor: pointer;
}
g#bar > path:hover {
opacity: 0.7;
transition: opacity 0.2s;
}
g#label text {
font-size: 0.2em;
}
svg text {
font-size: .2em;
}
g#yInfo text {
opacity: 0;
transition: opacity 0.4s ease-out;
}
g#xInfo text {
font-weight: bold;
opacity: 0;
transition: opacity 0.8s ease-out;
}
// create default object
var chartObj = {
xInfo : ["data1",
"data2",
"data3",
"data4"],
value : [600, 60, 1006, 446, 100],
headY : "Total amount",
/*chartWidth : 80,
chartHeight : 75,*/
barWidth : 5,
chartWidth : 80,
chartHeight : 75.25,
colBar : "white",
colAxis : "white",
colLine : "wheat",
colTxt : "white",
colBg : "black"
}
// find SVG:DOM ratio for centering text
/*var svg = document.getElementById("svg"),
svgW = svg.clientWidth,
svgH = svg.clientHeight,
xNum = (svgW > svgH ? svgH : svgW),
ratio = xNum / svg.getAttributeNS(null, "viewBox").split(" ")[2];
chartObj.ratio = ratio;*/
// 1. find max value of the chart
function findMax1() {
console.log("--- findMax1() ---");
var dvsr = 1,
num = Math.max.apply(null, chartObj.value),
max1,
evenNum;
// set divisor
if(num > 9999) {
dvsr = 1000;
} else if(num > 999) {
dvsr = 100;
} else if(num > 99) {
dvsr = 10;
}
// round
var num2 = num / dvsr;
evenNum = (num2 % 2 === 0 ?
num2 : num2 + 1);
max1 = Math.ceil(evenNum) * dvsr;
max1 = (max1 === num ?
max1 += dvsr : max1);
chartObj.max1 = max1;
chartObj.dvsr = dvsr;
}
// 2. find position of x axis value (bars)
// and bars height
function findBarsPosAndHeight() {
console.log("--- findBarsPosAndHeight() ---");
var secWidth = chartObj.chartWidth / chartObj.barsNum,
barsPosX = [],
barsPosY = [];
for(var i = 0; i < chartObj.barsNum; i++) {
barsPosX[i] = ((secWidth * i) + (secWidth / 2)) - (chartObj.barWidth / 2);
barsPosY[i] = Math.floor((chartObj.value[i] / chartObj.max1) * chartObj.chartHeight);
barsPosY[i] = (barsPosY[i] === 0 ? barsPosY[i] += 1 : barsPosY[i]);
}
chartObj.barsPosX = barsPosX;
chartObj.barsPosY = barsPosY;
}
// find number of horizontal lines
function findLineNum() {
console.log("--- findLineNum() ---");
var max1 = chartObj.max1,
dvsr = chartObj.dvsr,
mtplr = (max1 === (2 * dvsr) ||
(max1 - 1) === (2 * dvsr) ?
1 : 2 * dvsr),
lineNum = max1 / mtplr;
while(lineNum < 2 ||
lineNum > 10) {
mtplr += 2 * dvsr;
lineNum = max1 / mtplr;
//console.log("mtplr = " + mtplr);
//console.log("lineNum = " + lineNum);
}
lineNum = Math.ceil(lineNum);
// set max again to match line number
var max = lineNum * mtplr;
chartObj.lineNum = lineNum;
chartObj.max = max;
chartObj.range = mtplr;
//console.log("mtplr = " + mtplr);
//console.log("lineNum = " + lineNum);
}
// find position of y axis value
function findYPos() {
console.log("--- findYPos() ---");
var linePos = (chartObj.chartHeight) / chartObj.lineNum,
linesPosY = [],
yInfo = [];
for(var i = 0; i < chartObj.lineNum; i++) {
linesPosY[i] = -Math.abs(linePos * (i + 1));
yInfo[i] = chartObj.range * (i + 1);
}
chartObj.linesPosY = linesPosY;
chartObj.yInfo = yInfo;
//console.log(chartObj);
}
// create axis
function createAxis() {
console.log("--- createAxis() ---");
var g = document.getElementById("axis");
var x = document.createElementNS(
"http://www.w3.org/2000/svg", "line");
var y = document.createElementNS(
"http://www.w3.org/2000/svg", "line");
x.setAttributeNS(null, "x1", 0);
x.setAttributeNS(null, "y1", 80);
x.setAttributeNS(null, "x2", chartObj.chartWidth);
x.setAttributeNS(null, "y2", 80);
x.setAttributeNS(null, "stroke", chartObj.colAxis);
y.setAttributeNS(null, "x1", 0);
y.setAttributeNS(null, "y1", chartObj.chartHeight + 5);
y.setAttributeNS(null, "x2", 0);
y.setAttributeNS(null, "y2", 0);
y.setAttributeNS(null, "stroke", chartObj.colAxis);
g.appendChild(x);
g.appendChild(y);
}
// create horizontal lines
function createLines() {
console.log("--- createLines() ---");
for(var i = 0; i < chartObj.lineNum; i++) {
var g = document.getElementById("tblineHorizontal");
var line = document.createElementNS(
"http://www.w3.org/2000/svg", "line");
var yVal = chartObj.linesPosY[i];
line.setAttributeNS(null, "x1", 0);
line.setAttributeNS(null, "y1", yVal);
line.setAttributeNS(null, "x2", 0);
line.setAttributeNS(null, "y2", yVal);
line.setAttributeNS(null, "stroke", chartObj.colLine);
g.appendChild(line);
}
}
// create paths (bars) and labels
function createBarsAndLabels() {
console.log("--- createBarsAndLabels() ---");
for(var i = 0; i < chartObj.barsNum; i++) {
var x = chartObj.barsPosX[i],
gBar = document.getElementById("bar"),
gLabel = document.getElementById("label"),
bar = document.createElementNS(
"http://www.w3.org/2000/svg", "path"),
label = document.createElementNS(
"http://www.w3.org/2000/svg", "text"),
d = "M" + x + " 0 l5 0";
bar.setAttributeNS(null, "d", d);
bar.setAttributeNS(null, "fill", chartObj.colBar);
label.setAttributeNS(
null, "text-anchor", "start");
label.setAttributeNS(null, "x", x);
label.setAttributeNS(null, "y", -2);
label.setAttributeNS(null, "fill", chartObj.colTxt);
label.innerHTML = 0;
gBar.appendChild(bar);
gLabel.appendChild(label);
// center label
/*var labelWidth = label.getBoundingClientRect().width,
pos = (x + (chartObj.barWidth / 2)) -
((labelWidth / chartObj.ratio) / 2);
label.setAttributeNS(null, "x", pos);
*/
}
}
// create y-axis info
function createYInfo() {
console.log("--- createYInfo() ---");
for(var i = 0; i < (chartObj.lineNum + 1); i++) {
var g = document.getElementById("yInfo"),
txt = document.createElementNS(
"http://www.w3.org/2000/svg", "text"),
posY = chartObj.linesPosY[i],
yInfo = chartObj.yInfo[i];
if((i + 1) === (chartObj.lineNum + 1)) {
txt.setAttributeNS(
null, "text-anchor", "middle");
txt.setAttributeNS(null, "x", 0);
txt.setAttributeNS(null, "y", "-" + (chartObj.chartHeight + 8));
txt.setAttributeNS(null, "fill", chartObj.colTxt);
txt.innerHTML = chartObj.headY;
g.appendChild(txt);
break;
}
txt.setAttributeNS(
null, "alignment-baseline", "central");
txt.setAttributeNS(
null, "text-anchor", "end");
txt.setAttributeNS(null, "x", 0);
txt.setAttributeNS(null, "y", posY);
txt.setAttributeNS(null, "fill", chartObj.colTxt);
txt.innerHTML = yInfo;
g.appendChild(txt);
// center text
/*var txtHeight = (txt.getBoundingClientRect().height / ratio),
pos = posY + txtHeight / 2;
txt.setAttributeNS(null, "y", pos);
console.log("txtHeight = " + txtHeight);
console.log("pos = " + pos);*/
}
}
// create x info
function createXInfo() {
console.log("--- createXInfo() ---");
var totalW = 0;
for(var i = 0; i < chartObj.barsNum; i++) {
var g = document.getElementById("xInfo"),
txt = document.createElementNS(
"http://www.w3.org/2000/svg", "text"),
posX = chartObj.barsPosX[i] +
(chartObj.barWidth / 2),
xInfo = chartObj.xInfo,
widthRatio = chartObj.chartWidth / chartObj.barsNum,
y = 3;
txt.innerHTML = xInfo[i];
txt.setAttributeNS(
null, "alignment-baseline", "central");
txt.setAttributeNS(
null, "text-anchor", "middle");
txt.setAttributeNS(null, "x", posX);
txt.setAttributeNS(null, "fill", chartObj.colTxt);
g.appendChild(txt);
// spacing the text properly
if(i > 0) {
var prev = txt.previousElementSibling;
if((prev.getBBox().width > widthRatio ||
txt.getBBox().width > widthRatio) &&
prev.getAttributeNS(null, "y") == 3) {
y = 7;
} else {
y = 3;
}
}
txt.setAttributeNS(null, "y", y);
//console.log("txtW = " + txt.getBBox().width);
totalW += txt.getBBox().width;
// find string width to center the string
/*var txtWidth = txt.getBoundingClientRect().width / chartObj.ratio,
pos = posX - (txtWidth / 2);
//console.log("boundingWidth = " + txt.getBoundingClientRect().width);
//console.log("pos = " + pos);
txt.setAttributeNS(null, "x", pos);*/
}
}
// path animation function
function animateBarsAndLabels(bar, label, barEnd, labelValue) {
console.log("--- animateBarsAndLabels() ---");
var y = 0,
num = 0,
add = labelValue / barEnd,
t = 100 / barEnd,
d = bar.getAttributeNS(null, "d"),
myPath,
int = setInterval(function() {
if(y >= barEnd) {
clearInterval(int);
} else {
y++;
num += add;
myPath = " l0 -" + y + " l-" +
chartObj.barWidth + " 0";
bar.setAttributeNS(
null, "d", d +
myPath + " Z");
label.setAttributeNS(null, "y",
"-" + (3 + y));
label.innerHTML = num.toFixed();
}
}, t);
}
// line animation function
function animateLine(elem) {
console.log("--- animateLine() ---");
var x = 0,
end = chartObj.chartWidth,
t = 100 / end,
int = setInterval(function() {
if(x >= end) {
clearInterval(int);
} else {
x++;
elem.setAttributeNS(null, "x2", x);
elem.setAttributeNS(null, "y2",
elem.getAttributeNS(null, "y1"));
}
}, t);
}
// display yInfo and lines
function displayYInfoAndLines() {
console.log("--- displayYInfoAndLines() ---");
var y = document.querySelectorAll("g#yInfo text");
var line = document.querySelectorAll("#tblineHorizontal line");
var i = 0;
var int = setInterval(function() {
if(!y[i]) {
clearInterval(int);
} else {
y[i].style.opacity = 1;
if(line[i]) {
animateLine(line[i]);
}
i++;
}
}, 50);
}
// display xInfo and bars
function displayXInfoAndBars() {
console.log("--- displayXInfoAndBars() ---");
var x = document.querySelectorAll("#xInfo text");
var bars = document.querySelectorAll("#bar path");
var labels = document.querySelectorAll("#label text");
var i = 0;
var int = setInterval(function() {
if(!x[i]) {
clearInterval(int);
} else {
x[i].style.opacity = 1;
animateBarsAndLabels(bars[i], labels[i],
chartObj.barsPosY[i],
chartObj.value[i]);
i++;
}
}, 100);
}
// remove all svg elements in each svg 'g' element
function clearSVG() {
console.log("--- clearSVG() ---");
var g = document.querySelectorAll("svg g#chartBody g");
for(var i = 0; g[i]; i++) {
g[i].innerHTML = null;
}
}
var btn = document.getElementById("btn");
btn.onclick = function() {
console.log("--- btn.onclick() ---");
// get data from user
var inputs = document.getElementsByTagName("input");
for(var i = 0; inputs[i]; i++) {
var ip = inputs[i];
if(ip.value === "") {continue;}
var val;
if(ip.value.includes(", ")) {
val = ip.value.split(", ");
} else {val = ip.value;}
chartObj[ip.dataset.obj] = val;
}
clearSVG();
findMax1();
// find number of bars
chartObj.barsNum = chartObj.xInfo.length;
findBarsPosAndHeight();
findLineNum();
findYPos();
var svg = document.getElementById("svg");
svg.style.backgroundColor = chartObj.colBg;
var axis = document.querySelectorAll("#axis line");
for(i = 0; axis[i]; i++) {
axis[i].setAttributeNS(
null, "stroke", chartObj.colAxis);
}
console.log(chartObj);
createAxis();
createYInfo();
createXInfo();
createLines();
createBarsAndLabels();
displayYInfoAndLines();
displayXInfoAndBars();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment