Home | Math Display Experiments | JSXGraph axes, ticks and grids
Page by Murray Bourne, IntMath.com. Last updated: 21 Nov 2019JSXGraph axes, ticks and grids
Axes, ticks and grids in JSXGraph are somewhat mysterious. I sometimes get it, only to find a feature I was using disappears in later versions of JSXGraph, or the documentation is not very clear.
So below are some things I couldn't find easily in the JSXGraph documentation, or are recent changes to how things work, or things I keep forgetting how to do.
I wrote it as a summary for myself, but I hope it's useful for anyone else trying to figure out JSXGraph axes, ticks and grids.
Any questions or comments, please add them here.
Update (May 2017)
JSXGraph has improved many of the default settings since I first wrote this page, so I have updated portions (mostly by removing my earlier gripes).
Later, on this page:
(1) Default axis
Here is a simple JSXGraph board where I have turned "axis" and "zooming" on, so you can see default axis appearance.
In this default view, (which is −5 to 5 for both axes), we cannot actually see those extremities (the graph finishes there, but the axis markers don't show).
You can zoom in and out (hold down <Shift> on the keyboard while using the mouse scroll wheel to zoom).
var board = JXG.JSXGraph.initBoard('jxgbox1', { axis:true, zoom: { factorX:1.25, factorY:1.25, wheel:true, needshift:true, eps: 0.1 } } );
(2) grid:true
The default when using grid:true
doesn't appear to be all that different, but the zoom possibilities change.
var board = JXG.JSXGraph.initBoard('jxgbox2', { axis:true, grid:true, zoom: { factorX:1.25, factorY:1.25, wheel:true, needshift:true, eps: 0.1 } } );
You can zoom in and out (press <Shift> on the keyboard and use the mouse wheel to zoom), and see that sometimes the grids make sense, but a lot of the time they don't.
(3) Large scale difference between axes
When there's a big difference between the x- and y-scales, the grids end up being a dense mass.
var board = JXG.JSXGraph.initBoard('jxgbox3', { boundingbox:[-80,2.4, 80,-2.4], axis:true, grid:true,zoom: { factorX: 1.25,factorY: 1.25,wheel: true,needshift: true,eps: 0.1 } } );
(4) Large scale difference between axes and grid:false
Here's the same one with no grids.
var board = JXG.JSXGraph.initBoard('jxgbox4', { boundingbox:[-80,2.4, 80,-2.4], axis:true });
(5) Using JXG.Options to produce desired ticks
JXG.Options.axis.ticks.majorHeight = 60
means the major ticks are 60 px long. (As mentioned above, the default is -1
, and this would give ticks which extend the whole width of the board. Of course, 60 is pretty ugly - it's just for demonstration purposes.)
The documentation says:
insertTicks
inserts ticks whenever the distance between two ticks is too big.
So we set it to JXG.Options.axis.ticks.insertTicks = false;
, so we can get the ticks we want.
If we set it to JXG.Options.axis.ticks.insertTicks = true;
, we would have the default behavior like in (4), where we see no indication of the scale.
JXG.Options.axis.ticks.ticksDistance = 50;
means the ticks will be 50 units apart on both axes.
But that's a problem, because my y-axis is −2.4 to 2.4, so no scale indication will appear.
JXG.Options.axis.ticks.majorHeight = 60; JXG.Options.axis.ticks.insertTicks = false; JXG.Options.axis.ticks.ticksDistance = 50;
Here are the board properties:
var board = JXG.JSXGraph.initBoard('jxgbox5', { boundingbox:[-200,2.4, 240,-2.4], axis:true, grid:false });
(6) grid:true
Using grid:true
gives a thick mass of vertical grids, but at least now we can see horizontal grids at −2, −1, 1 and 2.
JXG.Options.axis.ticks.majorHeight = 60; JXG.Options.axis.ticks.insertTicks = false; JXG.Options.axis.ticks.ticksDistance = 100; var board = JXG.JSXGraph.initBoard('jxgbox6', { boundingbox:[-200,2.4, 240,-2.4], axis:true, grid:true, zoom: { factorX: 1.25,factorY: 1.25,wheel: true,needshift: true,eps: 0.1 } } );
(7) Create your own axes
I now turn off the default axes (with axis:false
), and create my own axes.
I remove all the ticks with xaxis.removeAllTicks();
I then create equidistant ticks on the 2 axes (NOTE: The number itself is not in its own separate array. It should be like this: board.create('ticks', [xaxis, 50]);
).
Here's the code:
var board = JXG.JSXGraph.initBoard('jxgbox7', { boundingbox:[-200,2.4, 240,-2.4], axis:false, grid:false, zoom: { factorX: 1.25,factorY: 1.25,wheel: true,needshift: true,eps: 0.1 } } ); var xaxis = board.create('axis', [ [0,0],[1,0] ], { label: {offset: [7, -10]}, // Doesn't do anything here. drawZero:false // Doesn't do anything here. } ); xaxis.removeAllTicks(); board.create('ticks', [xaxis, 50], { // The number here is the distance between Major ticks strokeColor:'#ccc', majorHeight:-1, // Need this because the JXG.Options one doesn't apply drawLabels:true, // Needed, and only works for equidistant ticks label: {offset: [-12, -10]}, minorTicks:5, // The NUMBER of small ticks between each Major tick drawZero:true } ); var yaxis = board.create('axis', [ [0,0],[0,1] ]); yaxis.removeAllTicks(); board.create('ticks', [yaxis, 1], { strokeColor:'#ccc', majorHeight:-1, // Need this because the JXG.Options one doesn't apply drawLabels:true, // Only works for equidistant ticks label: {offset: [7, -2]}, minorTicks:1, // The NUMBER of small ticks between each Major tick drawZero:false } );
Note: I have only one drawZero:true
so the origin zeros don't overlap.
(8) Label x- and y-axes
To put the x and y labels on our axes, we proceed as follows:
var board = JXG.JSXGraph.initBoard('jxgbox8', { boundingbox:[-3,13.5, 5.5,-10], axis:false, showCopyright:false, zoom: { factorX:1.25, factorY:1.25, wheel:true, needshift:true, eps: 0.1 } } ); xaxis = board.create('axis', [[0, 0], [1,0]], {name:'x', withLabel: true, label: {position: 'rt', // possible values are 'lft', 'rt', 'top', 'bot' offset: [-15, 20] // (in pixels) } }); yaxis = board.create('axis', [[0, 0], [0, 1]], {name:'y', withLabel: true, label: { position: 'rt', // possible values are 'lft', 'rt', 'top', 'bot' offset: [-20, 0] // (in pixels) } });
(9) Fix zoom issues
The problem with (7) is that when you zoom in you lose the scale, and when you zoom out, you get too many ticks and labels.
Here, I'm using the function setTicks
to change the ticks distance as you zoom in and out. It is triggered by board.on('boundingbox', function() { ... });
(which fires any time the bounding box is changed).
This time, I'm handling the ticks within the "create axis" construction. It doesn't make a lot of difference whether you do this, or create the ticks separately, like I did in (7).
Here's the code (this needed to be in a closure as it was being messed up by an earlier setting):
(function() { JXG.Options.axis.ticks.majorHeight = -1; // To prevent default tick labels: JXG.Options.axis.ticks.insertTicks = false; var board = JXG.JSXGraph.initBoard('jxgbox9', { boundingbox: [-220,2.4,220,-2.4], axis: false, grid: false, zoom: { factorX: 1.25,factorY: 1.25,wheel: true,needshift: true,eps: 0.1 } } ); var xaxis = board.create('axis', [ [0,0],[1,0] ], { ticks: { ticksDistance: 2, label: { offset: [-10, -12] // Must be within 'ticks:' definition }, minorTicks:1, // The NUMBER of small ticks between each Major tick drawZero:true // Must be within 'ticks:' definition } } ); var xTicks, yTicks, bb; xaxis.defaultTicks.ticksFunction = function () { return xTicks; }; board.fullUpdate(); // full update is required yaxis = board.create('axis', [[0, 0], [0, 1]], { ticks: { ticksDistance: 2, label: { offset: [7, -2] }, minorTicks:4 // The NUMBER of small ticks between each Major tick } } ); yaxis.defaultTicks.ticksFunction = function () { return yTicks; }; board.fullUpdate(); // full update is required var setTicks = function() { bb = board.getBoundingBox(); xTicksVal = Math.pow(10, Math.floor((Math.log(0.6*(bb[2]-bb[0])))/Math.LN10)); if( (bb[2]-bb[0])/xTicksVal > 6) { xTicks = xTicksVal; } else { xTicks = 0.5* xTicksVal; } yTicksVal = Math.pow(10, Math.floor((Math.log(0.6*(bb[1]-bb[3])))/Math.LN10)); if( (bb[1]-bb[3])/yTicksVal > 6) { yTicks = yTicksVal; } else { yTicks = 0.5* yTicksVal; } board.fullUpdate(); // full update is required } setTicks(); board.on('boundingbox', function () { setTicks(); }); })();
(10) Fix disappearing axes
The problem with (9) is that a lot of the time, depending on where you have zoomed to, you can't see the axes. So even if you have ticks where you want them, you can't see what their values are.
Here, I introduce "frozen" axes. Actually, the points making the axes are frozen when they get near the edge of the graph, using xaxis.point1.setAttribute({frozen:true});
.
Also, the axes change color to indicate they are not in their "normal" position. Sometimes the axis labels overlap, but I don't believe it's a big issue.
Here's the code:
(function() { JXG.Options.slider.ticks.majorHeight = 0; var board = JXG.JSXGraph.initBoard('jxgbox10',{ boundingbox: [-220,2.4,220,-2.4], axis:false, showCopyright:false, showNavigation:false, zoom: {factorX:1.25,factorY: 1.25,wheel:true,needshift:true,eps:0.1} } ); var grf = board.create('functiongraph', function(x){return 0.01*x*Math.sin(0.03*x); },{ strokeColor:'#066',highlight:false } ); var xAxPt0 = board.create('point', [0,0], { needsRegularUpdate: false, visible: false}); var xAxPt1 = board.create('point', [1,0], { needsRegularUpdate: false, visible: false}); var xaxis = board.create('axis', [xAxPt0,xAxPt1], { needsRegularUpdate: false, ticks:{ label:{offset:[-10,-10]}, precision:5} } ); var xTicks, yTicks, bb; xaxis.defaultTicks.ticksFunction = function () { return xTicks; }; board.fullUpdate(); // full update is required var coords=[]; var xPt0 = function(offset) { coords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [0, offset], board); return coords.usrCoords; } var xPt1 = function(offset) { coords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [board.canvasWidth, offset], board); return coords.usrCoords; } var yAxPt0 = board.create('point', [0,0], { needsRegularUpdate: false, visible: false}); var yAxPt1 = board.create('point', [0,1], { needsRegularUpdate: false, visible: false}); var yaxis = board.create('axis', [yAxPt0,yAxPt1], { needsRegularUpdate: false, ticks:{ label:{offset:[10,0],precision:8} } } ); yaxis.defaultTicks.ticksFunction = function () { return yTicks; }; board.fullUpdate(); var yPt0 = function(offset) { coords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [offset,board.canvasHeight], board); return coords.usrCoords; } var yPt1 = function(offset) { coords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [offset,0], board); return coords.usrCoords; } var setTicks = function() { bb = board.getBoundingBox(); xTicksVal = Math.pow(10, Math.floor((Math.log(0.6*(bb[2]-bb[0])))/Math.LN10)); if( (bb[2]-bb[0])/xTicksVal > 5) { xTicks = xTicksVal; } else { xTicks = 0.5* xTicksVal; } yTicksVal = Math.pow(10, Math.floor((Math.log(0.6*(bb[1]-bb[3])))/Math.LN10)); if( (bb[1]-bb[3])/yTicksVal > 5) { yTicks = yTicksVal; } else { yTicks = 0.5* yTicksVal; } board.fullUpdate(); // full update is required } setTicks(); var origPt = board.create('point', [0,0],{visible:false}); board.on('boundingbox', function() { bb = board.getBoundingBox(); mycoordsY = new JXG.Coords(JXG.COORDS_BY_USER, [0,origPt.Y()], board); yPixels = mycoordsY.scrCoords[2]; mycoordsX = new JXG.Coords(JXG.COORDS_BY_USER, [0,origPt.X()], board); xPixels = mycoordsX.scrCoords[1]; if( 30 > yPixels) { xAxPt0.moveTo (xPt0(4),0); xAxPt1.moveTo (xPt1(4),0); xaxis.point1.setAttribute({frozen: true}); xaxis.point2.setAttribute({frozen: true}); xaxis.setAttribute({strokeColor: '#a00'}); xaxis.defaultTicks.visProp.label.offset = [-10,-10]; } else if( yPixels > board.canvasHeight - 30) { xAxPt0.moveTo (xPt0(board.canvasHeight - 10),0); xAxPt1.moveTo (xPt1(board.canvasHeight - 10),0); xaxis.point1.setAttribute({frozen: true}); xaxis.point2.setAttribute({frozen: true}); xaxis.setAttribute({color: '#a00'}); xaxis.setAttribute({strokeColor: '#a00'}); xaxis.defaultTicks.visProp.label.offset = [-10,9]; } else { xaxis.point1.setAttribute({frozen: false}); xaxis.point2.setAttribute({frozen: false}); xaxis.setAttribute({color:'#666'}); xAxPt0.moveTo ([0,0],0); xAxPt1.moveTo ([1,0],0); } if( 30 > xPixels) { yAxPt0.moveTo (yPt0(5),0); yAxPt1.moveTo (yPt1(5),0); yaxis.point1.setAttribute({frozen: true}); yaxis.point2.setAttribute({frozen: true}); yaxis.setAttribute({strokeColor: '#a00'}); yaxis.defaultTicks.visProp.label.offset = [7,0]; } else if( xPixels > board.canvasWidth-30) { yAxPt0.moveTo (yPt0(board.canvasWidth-5),0); yAxPt1.moveTo (yPt1(board.canvasWidth-5),0); yaxis.point1.setAttribute({frozen: true}); yaxis.point2.setAttribute({frozen: true}); yaxis.setAttribute({strokeColor: '#a00'}); yaxis.defaultTicks.visProp.label.offset = [-28,0]; yaxis.defaultTicks.visProp.label.align = 'right'; } else { yaxis.point1.setAttribute({frozen: false}); yaxis.point2.setAttribute({frozen: false}); yaxis.setAttribute({strokeColor: '#666'}); yAxPt0.moveTo ([0,0],0); yAxPt1.moveTo ([0,1],0); } setTicks(); }); })();
Credit: Some of the above is based on this Fiddle: http://jsfiddle.net/migerh/DWpAw/
(11) Axes-dependent zoom
The default zoom function moves the same amount in each direction, (a 25% move) as given by this default code: zoom: {factorX:1.25, factorY:1.25...}
.
But often we want to only zoom in (or out) along one axis only.
Say your graph has a lot of interesting features near the x-axis (like the one shown), but the y-range is too high. That is, we only want to zoom the y-axis, and leave the domain (the x- values) as is.
Here's the relevant snippet from the setZoom
function: board.attr.zoom.factorx = factX;
.
When zooming this one, hold your mouse near the origin for best results.
var board = JXG.JSXGraph.initBoard('jxgbox11',{ boundingbox: [-220,22,250,-22], axis:false, showCopyright:false, showNavigation:false, zoom: {wheel: true,needshift: true,eps: 0.1} } ); var factX = 1.25, factY = 1.0; var setZoom = function() { board.attr.zoom.factorx = factX; board.attr.zoom.factory = factY; } setZoom(); var grf = board.create('functiongraph', function(x){return 0.01*x*Math.abs(Math.sin(0.03*x)); },{ strokeColor:'#066', highlight:false } ); var xAxPt0 = board.create('point', [0,0], {needsRegularUpdate: false, visible: false}); var xAxPt1 = board.create('point', [1,0], {needsRegularUpdate: false, visible: false}); var xaxis = board.create('axis', [xAxPt0,xAxPt1], { needsRegularUpdate: false, ticks:{ label:{offset:[-10,-10]}} } ); var xTicks,yTicks,bb; //xaxis.defaultTicks.ticksFunction = function () { return xTicks; }; board.fullUpdate(); // full update is required var coords=[]; var xPt0 = function(offset) { coords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [0, offset], board); return coords.usrCoords; } var xPt1 = function(offset) { coords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [board.canvasWidth, offset], board); return coords.usrCoords; } var yAxPt0 = board.create('point', [0,0], {needsRegularUpdate: false, visible: false}); var yAxPt1 = board.create('point', [0,1], {needsRegularUpdate: false, visible: false}); var yaxis = board.create('axis', [yAxPt0,yAxPt1], { needsRegularUpdate: false, ticks:{ label:{offset:[10,0]} } } ); //yaxis.defaultTicks.ticksFunction = function () { return yTicks; }; board.fullUpdate(); // full update is required var yPt0 = function(offset) { coords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [offset,board.canvasHeight], board); return coords.usrCoords; } var yPt1 = function(offset) { coords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [offset,0], board); return coords.usrCoords; } var origPt = board.create('point', [0,0],{visible:false}); var setTicks = function() { bb = board.getBoundingBox(); xTicksVal = Math.pow(10, Math.floor((Math.log(0.6*(bb[2]-bb[0])))/Math.LN10)); if( (bb[2]-bb[0])/xTicksVal > 6) { xTicks = xTicksVal; } else { xTicks = 0.5* xTicksVal; } yTicksVal = Math.pow(10, Math.floor((Math.log(0.6*(bb[1]-bb[3])))/Math.LN10)); if( (bb[1]-bb[3])/yTicksVal > 6) { yTicks = yTicksVal; } else { yTicks = 0.5* yTicksVal; } board.fullUpdate(); // full update is required } setTicks(); board.on('boundingbox', function() { bb = board.getBoundingBox(); mycoordsY = new JXG.Coords(JXG.COORDS_BY_USER, [0,origPt.Y()], board); yPixels = mycoordsY.scrCoords[2]; mycoordsX = new JXG.Coords(JXG.COORDS_BY_USER, [0,origPt.X()], board); xPixels = mycoordsX.scrCoords[1]; if( 30 > yPixels) { xAxPt0.moveTo (xPt0(4),0); xAxPt1.moveTo (xPt1(4),0); xaxis.point1.setAttribute({frozen: true}); xaxis.point2.setAttribute({frozen: true}); xaxis.setAttribute({strokeColor: '#a00'}); xaxis.defaultTicks.visProp.label.offset = [-10,-10]; } else if( yPixels > board.canvasHeight - 30) { xAxPt0.moveTo (xPt0(board.canvasHeight - 10),0); xAxPt1.moveTo (xPt1(board.canvasHeight - 10),0); xaxis.point1.setAttribute({frozen: true}); xaxis.point2.setAttribute({frozen: true}); xaxis.setAttribute({color: '#a00'}); xaxis.setAttribute({strokeColor: '#a00'}); xaxis.defaultTicks.visProp.label.offset = [-10,9]; } else { xaxis.point1.setAttribute({frozen: false}); xaxis.point2.setAttribute({frozen: false}); xaxis.setAttribute({color:'#666'}); xAxPt0.moveTo ([0,0],0); xAxPt1.moveTo ([1,0],0); } if( 30 > xPixels) { yAxPt0.moveTo (yPt0(5),0); yAxPt1.moveTo (yPt1(5),0); yaxis.point1.setAttribute({frozen: true}); yaxis.point2.setAttribute({frozen: true}); yaxis.setAttribute({strokeColor: '#a00'}); yaxis.defaultTicks.visProp.label.offset = [7,0]; } else if( xPixels > board.canvasWidth-30) { yAxPt0.moveTo (yPt0(board.canvasWidth-5),0); yAxPt1.moveTo (yPt1(board.canvasWidth-5),0); yaxis.point1.setAttribute({frozen: true}); yaxis.point2.setAttribute({frozen: true}); yaxis.setAttribute({strokeColor: '#a00'}); yaxis.defaultTicks.visProp.label.offset = [-23,0]; yaxis.defaultTicks.visProp.label.align = 'right'; } else { yaxis.point1.setAttribute({frozen: false}); yaxis.point2.setAttribute({frozen: false}); yaxis.setAttribute({strokeColor: '#666'}); yAxPt0.moveTo ([0,0],0); yAxPt1.moveTo ([0,1],0); } setTicks(); }); var options = document.getElementsByName("chooseDir"); if (options) { for (var i = 0; options.length > i; i++) { options[i].addEventListener("change", function() { if(this.value == 'zoomInX') { factX = 1.25; factY = 1.0; } else if(this.value == 'zoomInY') { factX = 1.0; factY = 1.25; } else { factX = 1.25; factY = 1.25; } setZoom(); }); } }
(12) Special ticks (e.g. multiples of π)
Often we have multiples of π or other constants for one (or more) of our axes. You can produce such special ticks using the following.
Changes from (11) include:
The "scale" and "scaleSymbol" definitions in the xaxis
definition: scale: Math.PI, scaleSymbol: 'π'
(the normal HTML way of entering lower case pi (π), does not work as of current writing).
The multiple 0.2
in this line: xTicksVal = Math.pow(10, Math.floor((Math.log(0.2*(bb[2]-bb[0])))/Math.LN10));
Here's the code:
JXG.Options.slider.ticks.majorHeight = 0; var board = JXG.JSXGraph.initBoard('jxgbox12',{ boundingbox: [-7,2.4,20,-2.4], axis:false, showCopyright:false, showNavigation:false, zoom: {factorX: 1.25,factorY: 1.25,wheel: true,needshift: true,eps: 0.1} } ); var grf = board.create('functiongraph', function(x){return 2*Math.sin(x); },{ strokeColor:'#066', highlight:false } ); var xAxPt0 = board.create('point', [0,0], {needsRegularUpdate: false, visible: false}); var xAxPt1 = board.create('point', [1,0], {needsRegularUpdate: false, visible: false}); var xaxis = board.create('axis', [xAxPt0,xAxPt1], { needsRegularUpdate: false, ticks:{ label:{offset:[-10,-10]}, scale: Math.PI, scaleSymbol: 'π' } } ); var xTicks, yTicks, bb; xaxis.defaultTicks.ticksFunction = function () { return xTicks; }; board.fullUpdate(); // full update is required var coords=[]; var xPt0 = function(offset) { coords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [0, offset], board); return coords.usrCoords; } var xPt1 = function(offset) { coords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [board.canvasWidth, offset], board); return coords.usrCoords; } var yAxPt0 = board.create('point', [0,0], {needsRegularUpdate: false, visible: false}); var yAxPt1 = board.create('point', [0,1], {needsRegularUpdate: false, visible: false}); var yaxis = board.create('axis', [yAxPt0,yAxPt1], { needsRegularUpdate: false, ticks:{ label:{offset:[10,0]} } } ); yaxis.defaultTicks.ticksFunction = function () { return yTicks; }; board.fullUpdate(); var yPt0 = function(offset) { coords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [offset,board.canvasHeight], board); return coords.usrCoords; } var yPt1 = function(offset) { coords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [offset,0], board); return coords.usrCoords; } var setTicks = function() { bb = board.getBoundingBox(); xTicksVal = Math.pow(10, Math.floor((Math.log(0.2*(bb[2]-bb[0])))/Math.LN10)); if( (bb[2]-bb[0])/xTicksVal > 7) { xTicks = xTicksVal; } else { xTicks = 0.5* xTicksVal; } yTicksVal = Math.pow(10, Math.floor((Math.log(0.6*(bb[1]-bb[3])))/Math.LN10)); if( (bb[1]-bb[3])/yTicksVal > 6) { yTicks = yTicksVal; } else { yTicks = 0.5* yTicksVal; } board.fullUpdate(); // full update is required } setTicks(); var origPt = board.create('point', [0,0],{visible:false}); board.on('boundingbox', function() { bb = board.getBoundingBox(); mycoordsY = new JXG.Coords(JXG.COORDS_BY_USER, [0,origPt.Y()], board); yPixels = mycoordsY.scrCoords[2]; mycoordsX = new JXG.Coords(JXG.COORDS_BY_USER, [0,origPt.X()], board); xPixels = mycoordsX.scrCoords[1]; if( 10 > yPixels) { xAxPt0.moveTo (xPt0(10),0); xAxPt1.moveTo (xPt1(10),0); xaxis.point1.setAttribute({frozen: true}); xaxis.point2.setAttribute({frozen: true}); xaxis.setAttribute({strokeColor: '#a00'}); xaxis.defaultTicks.visProp.label.offset = [-10,-10]; } else if( yPixels > board.canvasHeight - 10) { xAxPt0.moveTo (xPt0(board.canvasHeight - 10),0); xAxPt1.moveTo (xPt1(board.canvasHeight - 10),0); xaxis.point1.setAttribute({frozen: true}); xaxis.point2.setAttribute({frozen: true}); //xaxis.setAttribute({color: '#a00'}); xaxis.setAttribute({strokeColor: '#a00'}); xaxis.defaultTicks.visProp.label.offset = [-10,9]; } else { xaxis.point1.setAttribute({frozen: false}); xaxis.point2.setAttribute({frozen: false}); xaxis.setAttribute({color: '#666'}); xAxPt0.moveTo ([0,0],0); xAxPt1.moveTo ([1,0],0); } if( 5 > xPixels) { yAxPt0.moveTo (yPt0(5),0); yAxPt1.moveTo (yPt1(5),0); yaxis.point1.setAttribute({frozen: true}); yaxis.point2.setAttribute({frozen: true}); yaxis.setAttribute({strokeColor: '#a00'}); yaxis.defaultTicks.visProp.label.offset = [7,0]; } else if( xPixels > board.canvasWidth-5) { yAxPt0.moveTo (yPt0(board.canvasWidth-5),0); yAxPt1.moveTo (yPt1(board.canvasWidth-5),0); yaxis.point1.setAttribute({frozen: true}); yaxis.point2.setAttribute({frozen: true}); yaxis.setAttribute({strokeColor: '#a00'}); yaxis.defaultTicks.visProp.label.offset = [-20,0]; yaxis.defaultTicks.visProp.label.align = 'right'; } else { yaxis.point1.setAttribute({frozen: false}); yaxis.point2.setAttribute({frozen: false}); yaxis.setAttribute({strokeColor: '#666'}); yAxPt0.moveTo ([0,0],0); yAxPt1.moveTo ([0,1],0); } setTicks(); });
(13) Units on axis labels
This one is similar to (12), except this time we have labelled the axes with different units.
We have scaleSymbol:' s'
on the horizontal axis and scaleSymbol:' m'
on the vertical axis.
Also, the axes are labelled with t and h.
Here's the code:
JXG.Options.slider.ticks.majorHeight = 0; var board = JXG.JSXGraph.initBoard('jxgbox13',{ boundingbox: [-1.2,5,6,-1.4], axis:false, showCopyright:false, showNavigation:false, zoom: {factorX:1.25,factorY:1.25, wheel:true,needshift:true,eps:0.1} } ); var grf = board.create('functiongraph', [ function(x){return 4-(x-2)*(x-2); },0,4], { strokeColor:'#066',highlight:false } ); var xAxPt0 = board.create('point', [0,0], {needsRegularUpdate: false, visible: false}); var xAxPt1 = board.create('point', [1,0], {needsRegularUpdate: false, visible: false}); var xaxis = board.create('axis', [xAxPt0,xAxPt1], { needsRegularUpdate: false, name:'t', withLabel: true, label: {position: 'rt', // possible values are 'lft', 'rt', 'top', 'bot' offset: [-15, 20] // (in pixels) }, ticks:{ label:{offset:[-10,-10]}, scaleSymbol: ' s' } } ); var xTicks, yTicks, bb; xaxis.defaultTicks.ticksFunction = function () { return xTicks; }; board.fullUpdate(); // full update is required var coords=[]; var xPt0 = function(offset) { coords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [0, offset], board); return coords.usrCoords; } var xPt1 = function(offset) { coords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [board.canvasWidth, offset], board); return coords.usrCoords; } var yAxPt0 = board.create('point', [0,0], {needsRegularUpdate: false, visible: false}); var yAxPt1 = board.create('point', [0,1], {needsRegularUpdate: false, visible: false}); var yaxis = board.create('axis', [yAxPt0,yAxPt1], { needsRegularUpdate: false, name:'h', withLabel: true, label: {position: 'rt', // possible values are 'lft', 'rt', 'top', 'bot' offset: [-15, 20] // (in pixels) }, ticks:{ label:{offset:[10,0]}, scaleSymbol: ' m' } } ); yaxis.defaultTicks.ticksFunction = function () { return yTicks; }; board.fullUpdate(); var yPt0 = function(offset) { coords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [offset,board.canvasHeight], board); return coords.usrCoords; } var yPt1 = function(offset) { coords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [offset,0], board); return coords.usrCoords; } var setTicks = function() { bb = board.getBoundingBox(); xTicksVal = Math.pow(10, Math.floor((Math.log(0.2*(bb[2]-bb[0])))/Math.LN10)); if( (bb[2]-bb[0])/xTicksVal > 7) { xTicks = xTicksVal; } else { xTicks = 0.5* xTicksVal; } yTicksVal = Math.pow(10, Math.floor((Math.log(0.6*(bb[1]-bb[3])))/Math.LN10)); if( (bb[1]-bb[3])/yTicksVal > 6) { yTicks = yTicksVal; } else { yTicks = 0.5* yTicksVal; } board.fullUpdate(); // full update is required } setTicks(); var origPt = board.create('point', [0,0],{visible:false}); board.on('boundingbox', function() { bb = board.getBoundingBox(); mycoordsY = new JXG.Coords(JXG.COORDS_BY_USER, [0,origPt.Y()], board); yPixels = mycoordsY.scrCoords[2]; mycoordsX = new JXG.Coords(JXG.COORDS_BY_USER, [0,origPt.X()], board); xPixels = mycoordsX.scrCoords[1]; if( 10 > yPixels) { xAxPt0.moveTo (xPt0(10),0); xAxPt1.moveTo (xPt1(10),0); xaxis.point1.setAttribute({frozen: true}); xaxis.point2.setAttribute({frozen: true}); xaxis.setAttribute({strokeColor: '#a00'}); xaxis.defaultTicks.visProp.label.offset = [-10,-10]; } else if( yPixels > board.canvasHeight - 10) { xAxPt0.moveTo (xPt0(board.canvasHeight - 10),0); xAxPt1.moveTo (xPt1(board.canvasHeight - 10),0); xaxis.point1.setAttribute({frozen: true}); xaxis.point2.setAttribute({frozen: true}); xaxis.setAttribute({color: '#a00'}); xaxis.setAttribute({strokeColor: '#a00'}); xaxis.defaultTicks.visProp.label.offset = [-10,9]; } else { xaxis.point1.setAttribute({frozen: false}); xaxis.point2.setAttribute({frozen: false}); xaxis.setAttribute({color:'#666'}); xAxPt0.moveTo ([0,0],0); xAxPt1.moveTo ([1,0],0); } if( 5 > xPixels) { yAxPt0.moveTo (yPt0(5),0); yAxPt1.moveTo (yPt1(5),0); yaxis.point1.setAttribute({frozen: true}); yaxis.point2.setAttribute({frozen: true}); yaxis.setAttribute({strokeColor: '#a00'}); yaxis.defaultTicks.visProp.label.offset = [7,0]; } else if( xPixels > board.canvasWidth-5) { yAxPt0.moveTo (yPt0(board.canvasWidth-5),0); yAxPt1.moveTo (yPt1(board.canvasWidth-5),0); yaxis.point1.setAttribute({frozen: true}); yaxis.point2.setAttribute({frozen: true}); yaxis.setAttribute({strokeColor: '#a00'}); yaxis.defaultTicks.visProp.label.offset = [-20,0]; yaxis.defaultTicks.visProp.label.align = 'right'; } else { yaxis.point1.setAttribute({frozen: false}); yaxis.point2.setAttribute({frozen: false}); yaxis.setAttribute({strokeColor: '#666'}); yAxPt0.moveTo ([0,0],0); yAxPt1.moveTo ([0,1],0); } setTicks(); });
Any questions or comments on these examples, please add them here.