<svg class="mainSVG" viewBox="0 0 800 600" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet">
<defs>
<filter id="glow" x="-100%" y="-100%" width="350%" height="350%" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="5" result="coloredBlur" />
<feOffset dx="0" dy="20" result="offsetblur"></feOffset>
<feFlood id="glowAlpha" flood-color="#000" flood-opacity="0.123"></feFlood>
<feComposite in2="offsetblur" operator="in"></feComposite>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
</defs>
<g id="horizontalLinesGroup" fill="none" stroke="#FFF" stroke-miterlimit="10">
<line id="bottomLine" x1="149.72" y1="467.16" x2="611.66" y2="467.16" fill="none" stroke="#aaa" stroke-miterlimit="10" stroke-width="3"/>
<line id="vert" x1="149.72" y1="467.16" x2="149.72" y2="120.11" fill="none" stroke="#aaa" stroke-miterlimit="10" stroke-width="3"/>
<polygon id="left_arr" stroke-width="0" points="609.86 461.68 609.86 471.93 620.29 467.16 609.86 461.68" fill="#aaa"/>
<polygon id="up_arr" stroke-width="0" points="144.6 121.58 154.85 121.58 150.08 111.15 144.6 121.58" fill="#aaa"/>
</g>
<g id="uiGroup">
<g id="v_dash">
<g>
<line x1="401.5" y1="466.8" x2="401.5" y2="460.8" fill="none" stroke="#aaa" stroke-miterlimit="10" stroke-width="1"/>
<line x1="401.5" y1="449.28" x2="401.5" y2="339.78" fill="none" stroke="#aaa" stroke-miterlimit="10" stroke-width="1" stroke-dasharray="11.53 11.53"/>
<line x1="401.5" y1="334.02" x2="401.5" y2="328.02" fill="none" stroke="#aaa" stroke-miterlimit="10" stroke-width="1"/>
</g>
</g>
<g id="h_dash">
<g>
<line x1="401.5" y1="327" x2="395.5" y2="327" fill="none" stroke="#aaa" stroke-miterlimit="10" stroke-width="1"/>
<line x1="384.08" y1="327" x2="161.43" y2="327" fill="none" stroke="#aaa" stroke-miterlimit="10" stroke-width="1" stroke-dasharray="11.42 11.42"/>
<line x1="155.72" y1="327" x2="149.72" y2="327" fill="none" stroke="#aaa" stroke-miterlimit="10" stroke-width="1"/>
</g>
</g>
<path id="graphLine" d="M187.06,168.76S278,321.66,400,326.52s210.16-135.1,210.16-135.1" fill="none" stroke-linecap="round" stroke="#2665BA" stroke-width="4" stroke-miterlimit="10"/>
<path id="train_err_line" d="M185.1,222.7c22.6,47.7,64.7,115.6,124.5,158.1c69,49,136.8,53.6,304.2,53.4" fill="none" stroke-linecap="round" stroke="#F79819" stroke-width="4" stroke-miterlimit="10"/>
<g id="connectorGroup">
<line id="connector" x1="1000" x2="1000" y1="0" y2="0" stroke="#5ea0fa" />
<line id="connector2" x1="1000" x2="1000" y1="0" y2="0" stroke="#5ea0fa" />
<line id="connector3" x1="1000" x2="1000" y1="0" y2="0" stroke="#5ea0fa" />
</g>
<text class="axes" transform="translate(75 120)">Error</tspan></text>
<text class="legend" transform="translate(620.09 190)">dev error</tspan></text>
<text class="legend" transform="translate(620.09 438)">train error</text>
<text class="axes" transform="translate(560 530)">Number of Epochs</text>
<g id="box">
<rect x="0" width="160" height="40" rx="20" ry="20" fill="#f0f0f0"/>
<text id="boxLabel" x="80" y="25"></text>
</g>
<g id="box2">
<rect x="0" width="170" height="40" rx="20" ry="20" fill="#f0f0f0"/>
<text id="boxLabel2" x="85" y="25"></text>
</g>
</g>
<circle id="nullDot" fill="red" cx="0" cy="0" r="0"/>
<circle id="graphDot" fill="#5ea0fa" cx="0" cy="0" r="10"/>
<circle id="interDot" fill="#F79819" cx="0" cy="0" r="8"/>
<circle id="dragger" fill="rgba(0, 137, 236, 0.2)" cx="0" cy="0" r="15" stroke="rgba(95, 190, 237, 0.05)" stroke-width="10"/>
<linearGradient id="boxGrad" gradientUnits="userSpaceOnUse" x1="65.7809" y1="25.7808" x2="14.2194" y2="25.7808" gradientTransform="matrix(-5.857245e-007 -1 1 -5.872379e-007 14.2192 65.7809)">
<stop offset="0.3858" style="stop-color:#E4386D"/>
<stop offset="0.7513" style="stop-color:#CF2156"/>
</linearGradient>
</svg>
body {
background-color:#ffffff;
overflow: hidden;
/* font-family: 'Alegreya Sans', sans-serif; */
}
body,
html {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
svg{
position:absolute;
width:100%;
height:100%;
visibility:hidden;
}
.mainSVG{
position:absolute;
width:100%;
height:100%;
visibility:hidden;
/* top:200px; */
left:50%;
transform:translate(-50%, 0%);
overflow:visible;
}
#boxLabel, #boxLabel2{
text-anchor:middle;
fill:#115F9A;
font-size:16px;
user-select:none;
user-select:none;
pointer-events:none;
font-family: 'Roboto', sans-serif;
font-weight:700;
}
.legend {
fill: #aaa;
font-size:14px;
font-family: 'Roboto', sans-serif;
}
.axes {
fill: #878181;
font-size:15px;
font-family: 'Roboto', sans-serif;
}
#box, #box2{
opacity:0;
}
circle{
tap-highlight-color: rgba(0,0,0,0);
}
var xmlns = "http://www.w3.org/2000/svg",
xlinkns = "http://www.w3.org/1999/xlink",
select = function(s) {
return document.querySelector(s);
},
selectAll = function(s) {
return document.querySelectorAll(ds);
},
mainSVG = select('.mainSVG'),
box = select('#box'),
box2 = select('#box2'),
connector = select('#connector'),
connector2 = select('#connector2'),
connector3 = select('#connector3'),
train_line = select('#train_err_line'),
// connectorGroup = select('#connectorGroup'),
dragger = select('#dragger'),
graphDot = select('#graphDot'),
interDot = select('#interDot'),
boxLabel = select('#boxLabel'),
nullDot = select('#nullDot'),
graphLine = select('#graphLine'),
graphBezier = MorphSVGPlugin.pathDataToBezier(graphLine.getAttribute('d')),
perc, boxPos = {x: 0, y: 0},
boxPos2 = {x: 0, y: 0},
isPressed = false,
bottomLine = select('#bottomLine'),
labelOffset = 8;
TweenMax.set('svg', {visibility: 'visible'})
TweenMax.set([dragger, graphDot, nullDot, interDot], {transformOrigin: '50% 50%'})
TweenMax.set([dragger, graphDot, nullDot], {x: graphBezier[0].x, y: graphBezier[0].y})
TweenMax.set([box, box2], {transformOrigin: '50% 100%'})
TweenMax.set(interDot, {alpha: 0, scale: 0})
//fix boxes at certain axes
TweenMax.set(box, {x: (bottomLine.getAttribute('x1') - labelOffset - (box.getBBox().width))})
TweenMax.set(box2, {y: 467.1610 + labelOffset})
let tl = new TimelineMax({paused: true});
init()
function init() {
tl.to([graphDot, dragger], 5, {
bezier: {
type: "cubic",
values: graphBezier,
autoRotate: false
},
ease: Linear.easeNone
})
Draggable.create(nullDot, {
// type: 'x', //horizontal movement only
trigger: dragger,
onPress: graphPress,
bounds: {
minX: graphBezier[0].x,
maxX: graphBezier[graphBezier.length-1].x
},
zIndexBoost:false,
onDrag: updateGraph,
onRelease: graphRelease,
})
}
function graphPress() {
isPressed = true;
TweenMax.to(dragger, 0.5, {
attr: {r: 30},
ease: Elastic.easeOut.config(1, 0.7)
})
appendGraphEle();
TweenMax.to([box, box2, interDot], 0.5, {
scale: 1,
alpha: 1,
ease: Elastic.easeOut.config(1.2, 0.7)
});
}
function graphRelease() {
appendGraphEle()
isPressed = false;
TweenMax.to(dragger, 0.2, {
attr: {r: 15},
ease: Elastic.easeOut.config(0.7, 0.7)
})
appendGraphEle()
TweenMax.to([box, box2, interDot], 0.2, {
scale: 0,
alpha: 0,
})
}
function updateGraph() {
perc = (nullDot._gsTransform.x - graphBezier[0].x) / (graphBezier[graphBezier.length-1].x - graphBezier[0].x);
TweenMax.to(tl, 0.0005, {
progress: perc
})
appendGraphEle()
}
function appendGraphEle() {
drawLines();
drawBoxes();
}
function drawLines() {
if (isPressed) {
TweenMax.set(connector, { //vertical line
attr: {
x1: graphDot._gsTransform.x,
y1: 467.1610 + labelOffset,
x2: graphDot._gsTransform.x,
y2: graphDot._gsTransform.y
}
})
TweenMax.set(connector2, { //horizontal line ok
attr: {
x1: bottomLine.getAttribute('x1') - labelOffset,
y1: graphDot._gsTransform.y,
x2: graphDot._gsTransform.x,
y2: graphDot._gsTransform.y
}
})
findAndDrawIntersectionDot();
} else {
TweenMax.to([connector, connector2, connector3], 0.1, {
attr: {
x1: graphDot._gsTransform.x,
y1: graphDot._gsTransform.y,
x2: graphDot._gsTransform.x,
y2: graphDot._gsTransform.y
}
})
}
}
function drawBoxes() {
// console.log("updating box pos " + boxPos.x)
// boxPos.x = connector2.getAttribute("x1") - (box.getBBox().width);
boxPos.y = connector2.getAttribute("y1") - (box.getBBox().height * 0.5);
boxPos2.x = connector.getAttribute("x1") - (box2.getBBox().width /2);
TweenMax.to(box, 0.01, {
y: boxPos.y,
})
TweenMax.to(box2, 0.01, {
x: boxPos2.x,
})
let curr_iter = mapTo6000(dragger._gsTransform.x, 59000);
if ((curr_iter < 31000) && (curr_iter > 29000)) {
curr_iter = 30000;
}
let err_msg = " ";
if (curr_iter == 30000) {
err_msg = "Optimal dev error"
} else if (curr_iter < 30000) {
err_msg = "Model underfitting"
} else {
err_msg = "Model overfitting"
}
boxLabel.textContent = err_msg //.toFixed(2);
boxLabel2.textContent = parseInt(curr_iter) + "th epoch" //.toFixed(2);
}
function mapTo6000(currX, range) {
let currRange = Math.floor(graphBezier[graphBezier.length - 1].x - graphBezier[0].x)
return Math.floor((currX - graphBezier[0].x) / currRange * range)
}
function findAndDrawIntersectionDot() {
let path_d = convertFromLineToPath(connector);
let pt = Snap.path.intersection(path_d, train_line.getAttribute('d'))
if(pt[0]) {
TweenMax.set(interDot, {
x: pt[0].x,
y: pt[0].y,
})
}
}
function convertFromLineToPath(line) {
let str = "M"+line.getAttribute('x2')+","+line.getAttribute('y2')+"V"+line.getAttribute('y1');
return str;
}
This Pen doesn't use any external CSS resources.