import LayerCake from 'layercake';
import points from './data/points.js';
import ScatterWebgl from './components/ScatterWebgl.html';
import AxisX from './components/AxisX.html';
import AxisY from './components/AxisY.html';
import QuadTree from './components/QuadTree.html';
const diameter = 6;
const padding = 6;
const myCake = new LayerCake({
padding: { top: 0, right: 5, bottom: 20, left: 25 },
x: 'myX',
y: 'myY',
xPadding: [padding, padding],
yPadding: [padding, padding],
data: points,
target: document.getElementById('my-chart')
})
.svgLayers([
{ component: AxisX, opts: {} },
{ component: AxisY, opts: {} }
])
.webglLayers([
{ component: ScatterWebgl, opts: { diameter } }
])
.htmlLayers([
{ component: QuadTree, opts: { color: '#fff' } }
]);
myCake.render();
<script>
import reglWrapper from 'regl';
function resize (gl) {
const canvas = gl.canvas;
var displayWidth = canvas.clientWidth;
var displayHeight = canvas.clientHeight;
if (canvas.width !== displayWidth || canvas.height !== displayHeight) {
canvas.width = displayWidth;
canvas.height = displayHeight;
}
gl.viewport(0, 0, canvas.width, canvas.height);
}
export default {
onupdate () {
const { gl } = this.get();
resize(gl);
const regl = reglWrapper({
gl,
extensions: ['oes_standard_derivatives']
});
this.render(regl);
},
methods: {
render (regl) {
const { opts } = this.get();
const { xGet, yGet, data } = this.store.get();
regl.clear({
color: [0, 0, 0, 0],
depth: 1
});
const draw = regl({
frag: `
#extension GL_OES_standard_derivatives : enable
precision mediump float;
uniform vec3 fill_color;
uniform vec3 stroke_color;
varying float s_s;
void main () {
vec2 cxy = 2.0 * gl_PointCoord - 1.0;
float dist = dot(cxy, cxy);
float delta = fwidth(dist);
float alpha = 1.0 - smoothstep(1.0 - delta, 1.0 + delta, dist);
float outer_edge_center = 1.0 - s_s;
float stroke = 1.0 - smoothstep(outer_edge_center - delta, outer_edge_center + delta, dist);
// gl_FragColor = vec4(fill_color,1.0) * alpha;
gl_FragColor = vec4( mix(stroke_color, fill_color, stroke), 1.0 ) * alpha;
gl_FragColor.rgb *= gl_FragColor.a;
}`,
vert: `
precision mediump float;
attribute vec2 position;
attribute float r;
attribute float stroke_size;
varying float s_s;
uniform float stage_width;
uniform float stage_height;
// http://peterbeshai.com/beautifully-animate-points-with-webgl-and-regl.html
vec2 normalizeCoords(vec2 position) {
// read in the positions into x and y vars
float x = position[0];
float y = position[1];
return vec2(
2.0 * ((x / stage_width) - 0.5),
// invert y to treat [0,0] as bottom left in pixel space
-(2.0 * ((y / stage_height) - 0.5))
);
}
void main () {
s_s = stroke_size;
gl_PointSize = r;
gl_Position = vec4(normalizeCoords(position), 0.0, 1.0);
}`,
attributes: {
position: (context, props) => {
return props.points.map(point => {
return [xGet(point), yGet(point)];
});
},
r: (context, props) => {
return props.points.map(point => props.pointWidth);
},
stroke_size: (context, props) => {
return props.points.map(point => 0);
}
},
uniforms: {
fill_color: [0.6705882352941176, 0, 0.8392156862745098],
stroke_color: [0, 0, 0],
stage_width: regl.context('drawingBufferWidth'),
stage_height: regl.context('drawingBufferHeight')
},
count: (context, props) => {
return props.points.length;
},
primitive: 'points',
blend: {
enable: true,
func: {
srcRGB: 'src alpha',
srcAlpha: 'src alpha',
dstRGB: 'one minus src alpha',
dstAlpha: 'one minus src alpha'
}
},
depth: { enable: false }
});
draw({
pointWidth: opts.diameter,
points: data
});
}
}
};
</script>
<g class='axis x-axis'>
{#each ticks as tick, i}
<g class='tick tick-{ tick }' transform='translate({$xScale(tick)},{$yScale.range()[0]})'>
{#if opts.gridlines !== false}
<line y1='{$height * -1}' y2='0' x1='0' x2='0'></line>
{/if}
<text y='16' text-anchor='{textAnchor(i)}'>{opts.formatTick ? opts.formatTick(tick) : tick}</text>
</g>
{/each}
{#if opts.baseline === true}
<line class="baseline" y1='{$height + 0.5}' y2='{$height + 0.5}' x1='0' x2='{$width}'></line>
{/if}
</g>
<style>
.tick {
font-size: .725em;
font-weight: 200;
}
line,
.tick line {
stroke: #aaa;
stroke-dasharray: 2;
}
.tick text {
fill: #666;
}
.baseline {
stroke-dasharray: 0;
}
</style>
<script>
export default {
namespace: 'svg',
computed: {
ticks: ({ $xScale, opts }) => {
return opts.ticks || $xScale.ticks(opts.tickNumber);
},
textAnchor: ({ ticks, opts }) => {
return function (i) {
if (opts.snapTicks === true) {
if (i === 0) {
return 'start';
}
if (i === ticks.length - 1) {
return 'end';
}
}
return 'middle';
};
}
}
};
</script>
<g class='axis y-axis' transform='translate(-{$padding.left}, 0)'>
{#each $yScale.ticks(opts.ticks || opts.tickNumber || 5) as tick, i}
<g class='tick tick-{tick}' transform='translate(0, {$yScale(tick)})'>
{#if opts.gridlines !== false}
<line x2='100%'></line>
{/if}
<text y='-4'>{opts.formatTick ? opts.formatTick(tick) : tick}</text>
</g>
{/each}
</g>
<style>
.tick {
font-size: .725em;
font-weight: 200;
}
.tick line {
stroke: #aaa;
stroke-dasharray: 2;
}
.tick text {
fill: #666;
text-anchor: start;
}
.tick.tick-0 line {
stroke-dasharray: 0;
}
</style>
<script>
export default {
namespace: 'svg'
};
</script>
<div ref:bg on:mousemove="mousemove(event)" on:mouseout="mouseout()"></div>
<div ref:circle style="top:{$yGet(highlight)}px;left:{$xGet(highlight)}px;display: { visible === true ? 'block' : 'none' };background-color:{opts.color};"></div>
<style>
ref:bg,
ref:circle {
position: absolute;
}
ref:bg {
top: 0;
right: 0;
bottom: 0;
left: 0;
}
ref:circle {
border-radius: 50%;
width: 8px;
height: 8px;
border: 1px solid #000;
transform: translate(-50%, -50%);
pointer-events: none;
}
</style>
<script>
import { quadtree } from 'd3-quadtree';
export default {
data () {
return {
highlight: {},
visible: false
};
},
computed: {
finder: ({ $data, $xGet, $yGet, $width, $height }) => {
return quadtree()
.extent([[-1, -1], [$width + 1, $height + 1]])
.x($xGet)
.y($yGet)
.addAll($data);
}
},
methods: {
mousemove (e) {
const { finder } = this.get();
const found = finder.find(e.layerX, e.layerY, 300);
if (found) {
this.set({ visible: true, highlight: found });
} else {
this.set({ visible: false });
}
},
mouseout () {
this.set({ visible: false });
}
}
};
</script>
export default [
{
myX: 1979,
myY: 7.19
},
{
myX: 1980,
myY: 7.83
},
{
myX: 1981,
myY: 7.24
},
{
myX: 1982,
myY: 7.44
},
{
myX: 1983,
myY: 7.51
},
{
myX: 1984,
myY: 7.1
},
{
myX: 1985,
myY: 6.91
},
{
myX: 1986,
myY: 7.53
},
{
myX: 1987,
myY: 7.47
},
{
myX: 1988,
myY: 7.48
},
{
myX: 1989,
myY: 7.03
},
{
myX: 1990,
myY: 6.23
},
{
myX: 1991,
myY: 6.54
},
{
myX: 1992,
myY: 7.54
},
{
myX: 1993,
myY: 6.5
},
{
myX: 1994,
myY: 7.18
},
{
myX: 1995,
myY: 6.12
},
{
myX: 1996,
myY: 7.87
},
{
myX: 1997,
myY: 6.73
},
{
myX: 1998,
myY: 6.55
},
{
myX: 1999,
myY: 6.23
},
{
myX: 2000,
myY: 6.31
},
{
myX: 2001,
myY: 6.74
},
{
myX: 2002,
myY: 5.95
},
{
myX: 2003,
myY: 6.13
},
{
myX: 2004,
myY: 6.04
},
{
myX: 2005,
myY: 5.56
},
{
myX: 2006,
myY: 5.91
},
{
myX: 2007,
myY: 4.29
},
{
myX: 2008,
myY: 4.72
},
{
myX: 2009,
myY: 5.38
},
{
myX: 2010,
myY: 4.92
},
{
myX: 2011,
myY: 4.61
},
{
myX: 2012,
myY: 3.62
},
{
myX: 2013,
myY: 5.35
},
{
myX: 2014,
myY: 5.28
},
{
myX: 2015,
myY: 4.63
},
{
myX: 2016,
myY: 4.72
}
];