<div class="container">
<h2><span>Stonks</span>, the sparkline renderer</h2>
<p>文章内の任意の場所に<span class="stonk"></span>このように折れ線グラフを挿入する事が出来ます。折れ線グラフのデータは配列で管理され、<span class="stonk"></span>指定のclassを付与した要素の順に割り当てられます。</p>
<section>
<p>例えばこちらのグラフ<span class="stonk"></span>は3つ目のデータを元に作られたグラフです</p>
</section>
</div>
body, html {
height: 100%;
font-size: 18px;
line-height: 1.5;
}
* {
box-sizing: border-box;
}
h2 span {
color: #E3342F;
}
h2 {
margin-bottom: 2rem;
}
body {
padding: 2rem;
background: #f7fafc;
font-family: system-ui, sans-serif;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #4a5568;
}
.container {
text-align: center;
max-width: 640px;
}
section {
margin-top: 2rem;
font-size: 0.8rem;
}
/**
* Data
*/
let stonks = document.querySelectorAll(".stonk");
data = [
[
420,
700,
500,
480,
460,
490,
390,
420,
550,
340,
320,
240,
180,
160,
200,
40,
0
],
[1, 3, 2, 3, 1, 2, 3],
[13, 30, 12, 3, 51, 20, 31]
];
const options = {
lineWidth: 2,
color: "#E3342F"
};
/**
* Class
*/
class Stonk {
constructor(wrapper, data, settings = {}) {
const defaults = {
width: 80,
color: "#000000",
lineWidth: 1
};
this.settings = Object.assign(defaults, settings);
this.wrapper = wrapper;
this.data = data;
this.normalData = [];
this.canvas = null;
this.ctx = null;
this.dimes = { w: 0, h: 0 };
}
draw() {
this._buildCanvas();
this._normalizeData(this.data);
this._drawData();
}
_drawData() {
let ctx = this.ctx;
let { w, h } = this.dimes;
w = w - this.settings.lineWidth / 2;
h = h - this.settings.lineWidth / 2;
let nd = this.normalData;
let step = w / (this.data.length - 1);
let tick = this.settings.lineWidth / 2;
ctx.strokeStyle = this.settings.color;
ctx.lineWidth = this.settings.lineWidth;
ctx.beginPath();
ctx.moveTo(tick, h - nd[0]);
for (var i = 1; i <= nd.length - 1; i++) {
tick += step;
ctx.lineTo(tick, h - nd[i]);
}
ctx.stroke();
}
_buildCanvas() {
let canvas = document.createElement("canvas");
let absoluteDimes;
this.wrapper.style.cssText += `
position: relative;
display: inline-block;
line-height: 1;
height: 0.9em;
`;
// Temporarily set the height so we can calculate the bounding
canvas.style.height = "100%";
canvas.style.width = `${this.settings.width}px`;
// Add it to the document to return the bounding
this.canvas = canvas;
this.ctx = this.canvas.getContext("2d");
this.wrapper.appendChild(this.canvas);
absoluteDimes = this.canvas.getBoundingClientRect();
const { width: w, height: h } = absoluteDimes;
this.dimes = { w, h: Math.floor(h) };
// Set absolute attributes
this.canvas.setAttribute("height", this.dimes.h);
this.canvas.setAttribute("width", this.dimes.w);
// Clean up
this.canvas.removeAttribute("style");
}
// Utils
_normalizeData(array) {
let min = Math.min(...array);
let max = Math.max(...array);
this.data.forEach((v) => {
let percentage = this._scaleBetween(v, 0, 100, min, max);
let h = (this.dimes.h - this.settings.lineWidth) * (percentage / 100);
this.normalData.push(h);
});
}
_scaleBetween(unscaledNum, minAllowed, maxAllowed, min, max) {
return (
((maxAllowed - minAllowed) * (unscaledNum - min)) / (max - min) +
minAllowed
);
}
}
/**
* Mount
*/
stonks.forEach((s, idx) => {
let stonk = new Stonk(s, data[idx], options).draw();
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.