<script>
import { LayerCake, Svg } from 'layercake';
import { scaleLinear } from 'd3-scale';
import Radar from './_components/Radar.svelte';
import AxisRadial from './_components/AxisRadial.svelte';
import data from './_data/radarScores.csv';
const seriesKey = 'name';
const xKey = ['fastball', 'change', 'slider', 'cutter', 'curve'];
const seriesNames = Object.keys(data[0]).filter(d => d !== seriesKey);
data.forEach(d => {
seriesNames.forEach(name => {
d[name] = +d[name];
});
});
</script>
<style>
.chart-container {
width: 100%;
height: 250px;
}
</style>
<div class="chart-container">
<LayerCake
padding={{ top: 30, right: 0, bottom: 7, left: 0 }}
x={xKey}
xDomain={[0, 10]}
xRange={({ height }) => [0, height / 2]}
data={data}
>
<Svg>
<AxisRadial/>
<Radar/>
</Svg>
</LayerCake>
</div>
<script>
import { getContext } from 'svelte';
import { line, curveCardinalClosed } from 'd3-shape';
const { data, width, height, xGet, config } = getContext('LayerCake');
export let fill = '#f0c'
export let stroke = '#f0c'
export let strokeWidth = 2
export let fillOpacity = 0.5
export let r = 4.5;
export let circleFill = "#f0c";
export let circleStroke = "#fff";
export let circleStrokeWidth = 1;
$: angleSlice = (Math.PI * 2) / $config.x.length;
$: path = line()
.curve(curveCardinalClosed)
.x((d, i) => d * Math.cos(angleSlice * i - Math.PI / 2))
.y((d, i) => d * Math.sin(angleSlice * i - Math.PI / 2));
</script>
<g
transform="translate({ $width / 2 }, { $height / 2 })"
>
{#each $data as row}
{@const xVals = $xGet(row)}
<path
class='path-line'
d='{path(xVals)}'
stroke="{stroke}"
stroke-width="{strokeWidth}"
fill="{fill}"
fill-opacity="{fillOpacity}"
></path>
{#each xVals as circleR, i}
{@const thisAngleSlice = angleSlice * i - Math.PI / 2}
<circle
cx={circleR * Math.cos(thisAngleSlice)}
cy={circleR * Math.sin(thisAngleSlice)}
r="{r}"
fill="{circleFill}"
stroke="{circleStroke}"
stroke-width="{circleStrokeWidth}"
></circle>
{/each}
{/each}
</g>
<style>
.path-line {
stroke-linejoin: round;
stroke-linecap: round;
}
</style>
<script>
import { getContext } from 'svelte';
const { width, height, xScale, extents, config } = getContext('LayerCake');
export let lineLengthFactor = 1.1;
export let labelPlacementFactor = 1.25;
$: max = $xScale(Math.max(...$extents.x));
$: lineLength = max * lineLengthFactor;
$: labelPlacement = max * labelPlacementFactor;
$: angleSlice = (Math.PI * 2) / $config.x.length;
function anchor (total, i) {
if (i === 0 || i === total / 2) {
return 'middle';
} else if (i < total / 2) {
return 'start';
}
return 'end';
}
</script>
<g
transform="translate({ $width / 2 }, { $height / 2 })"
>
<circle
cx="0"
cy="0"
r="{max}"
stroke="#ccc"
stroke-width="1"
fill="#CDCDCD"
fill-opacity="0.1"
></circle>
<circle
cx="0"
cy="0"
r="{max / 2}"
stroke="#ccc"
stroke-width="1"
fill="none"
></circle>
{#each $config.x as label, i}
{@const thisAngleSlice = angleSlice * i - Math.PI / 2}
<line
x1="0"
y1="0"
x2="{lineLength * Math.cos(thisAngleSlice)}"
y2="{lineLength * Math.sin(thisAngleSlice)}"
stroke="#ccc"
stroke-width="1"
fill="none"
>
</line>
<text
text-anchor="{anchor($config.x.length, i)}"
dy="0.35em"
font-size="12px"
text-outline="#fff"
transform="translate({(labelPlacement) * Math.cos(thisAngleSlice)}, {labelPlacement * Math.sin(thisAngleSlice)})">{label}</text>
{/each}
</g>
name,fastball,change,slider,cutter,curve
Allison,10,0,4,8,5