mirror of
https://github.com/seigler/neat-charts
synced 2025-07-26 17:06:09 +00:00
feat: add x-axis labels
This commit is contained in:
parent
d59dd86f14
commit
d0f39f04ea
5 changed files with 86 additions and 42 deletions
|
@ -3,7 +3,7 @@ require_once 'vendor/autoload.php';
|
||||||
|
|
||||||
function randomData($count = 20, $offsetMax = 100) {
|
function randomData($count = 20, $offsetMax = 100) {
|
||||||
$randomData = [];
|
$randomData = [];
|
||||||
$duration = 60 * 5 + rand() * 60 * 60 * 24;
|
$duration = 60 * 5 + rand()/getRandMax() * 60 * 60 * 24;
|
||||||
$begin = time() - $duration;
|
$begin = time() - $duration;
|
||||||
$offset = $offsetMax * (rand()/getRandMax())**2;
|
$offset = $offsetMax * (rand()/getRandMax())**2;
|
||||||
$scale = max(0.25 * $offset, 100 * rand() / getRandMax());
|
$scale = max(0.25 * $offset, 100 * rand() / getRandMax());
|
||||||
|
|
3
demo.php
3
demo.php
File diff suppressed because one or more lines are too long
|
@ -12,9 +12,10 @@ namespace NeatCharts {
|
||||||
'labelColor' => '#000',
|
'labelColor' => '#000',
|
||||||
'fontSize' => 15,
|
'fontSize' => 15,
|
||||||
'yAxisEnabled' => true,
|
'yAxisEnabled' => true,
|
||||||
'xAxisEnabled' => false,
|
'xAxisEnabled' => true,
|
||||||
'yAxisZero' => false,
|
'yAxisZero' => false,
|
||||||
'background' => 'none'
|
'background' => 'none',
|
||||||
|
'shadow' => 'none'
|
||||||
];
|
];
|
||||||
parent::setOptions($options);
|
parent::setOptions($options);
|
||||||
}
|
}
|
||||||
|
@ -71,8 +72,20 @@ namespace NeatCharts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->output = '<svg viewBox="-'.( $this->padding['left'] ).' -'.( $this->padding['top'] ).' '.( $this->options['width'] ).' '.( $this->options['height'] ).'" width="'.( $this->options['width'] ).'" height="'.( $this->options['height'] ).'" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
$this->output = '<svg viewBox="-'.( $this->padding['left'] ).' -'.( $this->padding['top'] ).' '.( $this->options['width'] ).' '.( $this->options['height'] ).'" width="'.( $this->options['width'] ).'" height="'.( $this->options['height'] ).'" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">'.
|
||||||
<g class="neatchart">'.
|
( $this->options['shadow'] == 'none' ? '' : ' <defs>
|
||||||
|
<filter id="shadow" x="-5000%" y="-5000%" width="10000%" height="10000%">
|
||||||
|
<feFlood result="flood" flood-color="'.$this->options['shadow'].'" flood-opacity="0.75"></feFlood>
|
||||||
|
<feComposite in="flood" result="mask" in2="SourceAlpha" operator="in"></feComposite>
|
||||||
|
<feMorphology in="mask" result="dilated" operator="dilate" radius="2"></feMorphology>
|
||||||
|
<feGaussianBlur in="dilated" result="blurred" stdDeviation="6"></feGaussianBlur>
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode in="blurred"></feMergeNode>
|
||||||
|
<feMergeNode in="SourceGraphic"></feMergeNode>
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
</defs>').'
|
||||||
|
<g class="neatchart"'.( $this->options['shadow'] == 'none' ? '' : 'filter="url(#shadow)"').'>'.
|
||||||
$gridLabelXML.'
|
$gridLabelXML.'
|
||||||
<g
|
<g
|
||||||
class="chart__bars"
|
class="chart__bars"
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace NeatCharts {
|
||||||
'smoothed' => false,
|
'smoothed' => false,
|
||||||
'fontSize' => 15,
|
'fontSize' => 15,
|
||||||
'yAxisEnabled' => true,
|
'yAxisEnabled' => true,
|
||||||
'xAxisEnabled' => false,
|
'xAxisEnabled' => true,
|
||||||
'yAxisZero' => false,
|
'yAxisZero' => false,
|
||||||
'filled' => false,
|
'filled' => false,
|
||||||
'background' => 'none'
|
'background' => 'none'
|
||||||
|
|
|
@ -12,7 +12,8 @@ namespace NeatCharts {
|
||||||
'yAxisEnabled' => true,
|
'yAxisEnabled' => true,
|
||||||
'xAxisEnabled' => true,
|
'xAxisEnabled' => true,
|
||||||
'yAxisZero' => false,
|
'yAxisZero' => false,
|
||||||
'background' => 'none'
|
'background' => 'none',
|
||||||
|
'shadow' => 'none'
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $width;
|
protected $width;
|
||||||
|
@ -25,7 +26,6 @@ namespace NeatCharts {
|
||||||
protected $yMax;
|
protected $yMax;
|
||||||
protected $yRange;
|
protected $yRange;
|
||||||
protected $padding = ['top'=>10, 'right'=>10, 'bottom'=>10, 'left'=>10];
|
protected $padding = ['top'=>10, 'right'=>10, 'bottom'=>10, 'left'=>10];
|
||||||
protected $timeIntervals = [ 5 * 60, 60 * 60, 24 * 60 * 60, 28 * 60 * 60 ]; // 5 min, 1 hr, 24 hr, 1 month
|
|
||||||
|
|
||||||
protected function labelFormat($float, $places, $minPlaces = 0) {
|
protected function labelFormat($float, $places, $minPlaces = 0) {
|
||||||
$value = number_format($float, max($minPlaces, $places));
|
$value = number_format($float, max($minPlaces, $places));
|
||||||
|
@ -79,10 +79,12 @@ namespace NeatCharts {
|
||||||
protected function buildGridLabelXML() {
|
protected function buildGridLabelXML() {
|
||||||
$this->width = $this->options['width'] - $this->padding['left'] - $this->padding['right'];
|
$this->width = $this->options['width'] - $this->padding['left'] - $this->padding['right'];
|
||||||
$this->height = $this->options['height'] - $this->padding['top'] - $this->padding['bottom'];
|
$this->height = $this->options['height'] - $this->padding['top'] - $this->padding['bottom'];
|
||||||
|
$gridText = '';
|
||||||
|
$gridLines = '';
|
||||||
if ($this->options['yAxisEnabled']) {
|
if ($this->options['yAxisEnabled']) {
|
||||||
$numLabels = 4 + ceil($this->height / $this->options['fontSize'] / 4);
|
$numYLabels = 4 + ceil($this->height / $this->options['fontSize'] / 4);
|
||||||
$labelInterval = $this->yRange / $numLabels;
|
$labelInterval = $this->yRange / $numYLabels;
|
||||||
$labelModulation = 10 ** (1 + floor(-log($this->yRange / $numLabels, 10)));
|
$labelModulation = 10 ** (1 + floor(-log($this->yRange / $numYLabels, 10)));
|
||||||
// 1 here is a fudge factor so we get multiples of 2.5 more often
|
// 1 here is a fudge factor so we get multiples of 2.5 more often
|
||||||
if (fmod($labelInterval * $labelModulation, 2.5) < fmod($labelInterval * $labelModulation, 2) + 1) {
|
if (fmod($labelInterval * $labelModulation, 2.5) < fmod($labelInterval * $labelModulation, 2) + 1) {
|
||||||
$labelModulation /= 2.5;
|
$labelModulation /= 2.5;
|
||||||
|
@ -100,12 +102,12 @@ namespace NeatCharts {
|
||||||
$this->width = $this->options['width'] - $this->padding['left'] - $this->padding['right'];
|
$this->width = $this->options['width'] - $this->padding['left'] - $this->padding['right'];
|
||||||
|
|
||||||
// Top and bottom grid lines
|
// Top and bottom grid lines
|
||||||
$gridLines =
|
$gridLines .=
|
||||||
'M0,0 '.$this->width.',0 '.
|
'M0 0 '.$this->width.' 0 '.
|
||||||
' M0,'.$this->height.','.$this->width.','.$this->height;
|
' M0 '.$this->height.' L'.$this->width.' '.$this->height.' ';
|
||||||
|
|
||||||
// Top and bottom grid labels
|
// Top and bottom grid labels
|
||||||
$gridText =
|
$gridText .=
|
||||||
'<text text-anchor="end" x="'.(-0.4 * $this->options['fontSize']).'" y="'.($this->options['fontSize'] * 0.4).'">'.($this->labelFormat($this->yMax, $labelPrecision + 1)).'</text>' .
|
'<text text-anchor="end" x="'.(-0.4 * $this->options['fontSize']).'" y="'.($this->options['fontSize'] * 0.4).'">'.($this->labelFormat($this->yMax, $labelPrecision + 1)).'</text>' .
|
||||||
'<text text-anchor="end" x="'.(-0.4 * $this->options['fontSize']).'" y="'.($this->options['fontSize'] * 0.4 + $this->height).'">'.($this->labelFormat($this->yMin, $labelPrecision + 1)).'</text>';
|
'<text text-anchor="end" x="'.(-0.4 * $this->options['fontSize']).'" y="'.($this->options['fontSize'] * 0.4 + $this->height).'">'.($this->labelFormat($this->yMin, $labelPrecision + 1)).'</text>';
|
||||||
|
|
||||||
|
@ -131,32 +133,59 @@ namespace NeatCharts {
|
||||||
).','.$labelHeight.' '.$this->width.','.$labelHeight;
|
).','.$labelHeight.' '.$this->width.','.$labelHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return '
|
|
||||||
<rect class="chart__background"
|
|
||||||
fill="'.( $this->options['background'] ).'"
|
|
||||||
x="-'.( $this->padding['left'] ).'"
|
|
||||||
y="-'.( $this->padding['top'] ).'"
|
|
||||||
width="'.( $this->options['width'] ).'"
|
|
||||||
height="'.( $this->options['height'] ).'"
|
|
||||||
/>
|
|
||||||
<g class="chart__gridLines"
|
|
||||||
stroke="'.( $this->options['labelColor'] ).'"
|
|
||||||
stroke-opacity="0.4"
|
|
||||||
stroke-width="1"
|
|
||||||
vector-effect="non-scaling-stroke"
|
|
||||||
shape-rendering="crispEdges">
|
|
||||||
<path class="chart__gridLinePaths" d="'.( $gridLines ).'" />
|
|
||||||
</g>
|
|
||||||
<g class="chart__gridLabels"
|
|
||||||
fill="'.( $this->options['labelColor'] ).'"
|
|
||||||
font-family="monospace"
|
|
||||||
font-size="'.( $this->options['fontSize'] ).'px">
|
|
||||||
'.( $gridText ).'
|
|
||||||
</g>';
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
|
if ($this->options['xAxisEnabled']) {
|
||||||
|
$timeIntervals = [
|
||||||
|
'minutes' => [60, 'g:ia'],
|
||||||
|
'hours' => [60 * 60, 'ga'],
|
||||||
|
'days' => [24 * 60 * 60, 'M j'],
|
||||||
|
'years' => [365 * 24 * 60 * 60, 'Y']
|
||||||
|
];
|
||||||
|
$numXLabels = 1 + round($this->width / $this->options['fontSize'] / 10);
|
||||||
|
$scale = 'years';
|
||||||
|
$xLabelFormat = $timeIntervals[$scale][1];
|
||||||
|
foreach ($timeIntervals as $period => $duration) {
|
||||||
|
if ($this->xRange / $numXLabels < $duration[0]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$scale = $period;
|
||||||
|
$xLabelFormat = $duration[1];
|
||||||
|
}
|
||||||
|
$xLabelInterval = $this->xRange / $numXLabels;
|
||||||
|
$xLabelInterval -= fmod($xLabelInterval, $timeIntervals[$scale][0]);
|
||||||
|
for (
|
||||||
|
$labelX = $this->xMin - fmod($this->xMin, $timeIntervals[$scale][0]) + $xLabelInterval;
|
||||||
|
$labelX < $this->xMax;
|
||||||
|
$labelX += $xLabelInterval
|
||||||
|
) {
|
||||||
|
$labelXCoord = $this->transformX($labelX);
|
||||||
|
$gridLines .= 'M'.$labelXCoord.' 0 '.$labelXCoord.' '.$this->height.' ';
|
||||||
|
$xLabelAlignment = ($this->width - $labelXCoord > $this->options['fontSize'] * 2 ? ($labelXCoord > $this->options['fontSize'] * 2 ? 'middle' : 'start') : 'end');
|
||||||
|
$gridText .= '<text text-anchor="'.$xLabelAlignment.'" y="'.($this->height + $this->options['fontSize']).'" x="'.$labelXCoord.'">'.date($xLabelFormat, $labelX).'</text><!--'.$labelX.'-->';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '<rect class="chart__background"
|
||||||
|
fill="'.( $this->options['background'] ).'"
|
||||||
|
x="-'.( $this->padding['left'] ).'"
|
||||||
|
y="-'.( $this->padding['top'] ).'"
|
||||||
|
width="'.( $this->options['width'] ).'"
|
||||||
|
height="'.( $this->options['height'] ).'"
|
||||||
|
/>
|
||||||
|
<g class="chart__gridLines"
|
||||||
|
stroke="'.( $this->options['labelColor'] ).'"
|
||||||
|
stroke-opacity="0.4"
|
||||||
|
stroke-width="1"
|
||||||
|
vector-effect="non-scaling-stroke"
|
||||||
|
shape-rendering="crispEdges">
|
||||||
|
<path class="chart__gridLinePaths" d="'.( $gridLines ).'" />
|
||||||
|
</g>
|
||||||
|
<g class="chart__gridLabels"
|
||||||
|
fill="'.( $this->options['labelColor'] ).'"
|
||||||
|
font-family="monospace"
|
||||||
|
font-size="'.( $this->options['fontSize'] ).'px">
|
||||||
|
'.( $gridText ).'
|
||||||
|
</g>';
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function __construct($chartData, $options = []) {
|
final public function __construct($chartData, $options = []) {
|
||||||
|
@ -167,7 +196,8 @@ namespace NeatCharts {
|
||||||
public function setOptions($options) {
|
public function setOptions($options) {
|
||||||
$this->options = array_replace($this->options, $options);
|
$this->options = array_replace($this->options, $options);
|
||||||
$this->padding['left'] = $this->padding['right'] = $this->options['fontSize'] / 2;
|
$this->padding['left'] = $this->padding['right'] = $this->options['fontSize'] / 2;
|
||||||
$this->padding['top'] = $this->padding['bottom'] = $this->options['fontSize'];
|
$this->padding['top'] = $this->options['fontSize'];
|
||||||
|
$this->padding['bottom'] = $this->options['fontSize'] * 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract function setData($chartData);
|
public abstract function setData($chartData);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue