<script src="https://unpkg.com/konva@^3/konva.min.js"></script>
<div id="container"></div>
body {
margin: 10;
padding: 10;
overflow: hidden;
background-color: #ffffff;
}
#container {
border: 1px solid silver;
}
/** Developing a graphing component based on Konva.
*/
// Set up a stage
let stage = new Konva.Stage({
container: "container",
width: window.innerWidth,
height: 400,
draggable: true
}),
// add a layer to draw on
layer = new Konva.Layer();
stage.add(layer);
stage.draw();
// Instantiate a vwGraph object and pass it the initial options to draw the grid.
let d = new vwGraph({
stage: stage,
origin: {x: 50, y: 250},
range: {posX: 500, posY: 1000, negX: -50, negY: -300},
direction: 'y-up',
xPoints: ['', 1975, 1980, 1985, 1990, 1995, 2000, 2005, 2010, 2015, 2020, 2025],
title: {x: 50, y: 240, text: 'Avg Tasmanian Wombat IQ'}
})
d.drawGrid(50);
d.plotData({
tp: 'path',
clr: 'magenta',
pts: [
{x: 50, y: 10},
{x: 100, y: 30},
{x: 150, y: 100},
{x: 200, y: 180},
{x: 250, y: 160},
{x: 300, y: 140},
{x: 350, y: 90},
{x: 400, y: 40},
{x: 450, y: 20},
]
})
function vwGraph(opts){
let origin = opts.origin,
range = opts.range, stage = opts.stage,
direction = opts.direction === 'y-up' ? -1 : 1,
xPoints = opts.xPoints,
title = opts.title,
gridLayer = null;
// calculate the width and height of the range
range.width = range.posX + Math.abs(range.negX);
range.height = range.posY + Math.abs(range.negY);
// Get a reliable top Y position regardless of direction
range.minY = direction === 1 ? range.negY : range.posY;
// turn a logical x into a physical x
function getX(x){
return x + origin.x;
}
// turn a logical x into a physical x
function getY(y){
return origin.y + (direction * y);
}
// turn a logical x into a physical x but without the offset !
function getRelativeY(y){
return (direction * y);
}
// draw the grid
this.drawGrid = function drawGrid(step){
gridLayer = new Konva.Layer({listening: false}),
ln = new Konva.Line({x: 0, y: 0, points: [], stroke: 'black', strokeWidth: 0.5, listening: false, perfectDrawEnabled: false, transformsEnabled: 'position', shadowEnabled: false, shadowForStrokeEnabled: false }),
txt = new Konva.Text({x:0, y: 0, width: 40, height: 10, fontSize: 10, fontFamily: 'Calibri', align: 'left', fill: 'black', listening: false, perfectDrawEnabled: false, transformsEnabled: 'position', shadowEnabled: false, shadowForStrokeEnabled: false}),
ln1 = null,
t1 = null;
// output the x-axis
ln1 = ln.clone({x: getX(range.negX), y: getY(0), points: [0, 0, range.width, 0], strokeWidth: 1.5});
ln1.cache();
gridLayer.add(ln1)
// output the Y axis
ln1 = ln.clone({x: getX(0), y: getY(range.minY), points: [0, 0, 0, range.height], strokeWidth: 1.5});
ln1.cache();
gridLayer.add(ln1)
// X axis lines & text
let ptCnt = 0;
for (let i = 0; i <= range.width; i = i + step ){
ln1 = ln.clone({ x: getX(range.negX) + i, y: getY(range.minY), points: [0, 0, 0, range.height]});
t1 = txt.clone({x: getX(range.negX) + i - 20, y: getY(0) + 5, text: range.negX + i, align: 'center'});
t1.text('xxx')
if (xPoints && xPoints.length >= ptCnt){
t1.text(xPoints[ptCnt])
}
ln1.cache();
t1.cache();
gridLayer.add(ln1, t1);
ptCnt = ptCnt + 1;
}
// Y axis lines & text
for (let i = 0; i <= range.height; i = i + step ){
ln1 = ln.clone({x: getX(range.negX), y: getY(range.minY) + i, points: [0, 0, range.width, 0]});
t1 = txt.clone({x: getX(10), y: getY(range.minY) + i - 5, text: range.minY + (direction * i)});
ln1.cache();
t1.cache();
gridLayer.add(ln1, t1);
}
if (title){
t1 = txt.clone({x: getX(title.x), y: 0, text: title.text, fontSize: 32, width: 2000, height: 40});
t1.cache();
gridLayer.add(t1);
}
stage.add(gridLayer);
gridLayer.batchDraw();
}
this.plotData = function(opts){
switch (opts.tp){
case 'path' : // a Konva path.
// A path has a set of data points. We process these to flip them if required, and make the path commands.
let data = '', sep = '';
for (let i = 0; i < opts.pts.length; i++){
if (i === 0){ // first path command is M
data = 'M' + opts.pts[i].x + ',' + getRelativeY(opts.pts[i].y)
}
else { // following path commands all L
data = data + sep + 'L' + opts.pts[i].x + ',' + getRelativeY(opts.pts[i].y)
}
sep = ','
}
console.log(data)
let p = new Konva.Path({
x: getX(0),
y: getY(0),
stroke: opts.clr,
strokeWidth: 2,
data: data
})
gridLayer.add(p);
gridLayer.batchDraw();
break;
// case '??' : // extend as required....
}
}
return this;
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.