Skip to content

Instantly share code, notes, and snippets.

@hankei6km
Last active September 18, 2020 16:11
Show Gist options
  • Select an option

  • Save hankei6km/92cc2b4ef0805cf8201e06a5af6c7cb9 to your computer and use it in GitHub Desktop.

Select an option

Save hankei6km/92cc2b4ef0805cf8201e06a5af6c7cb9 to your computer and use it in GitHub Desktop.
Hugo の shortcode でプロッター表示 https://hankei6km.github.io/posts/2020-08-hugo_shortcode_plotter/
<!--
button icons
https://material.io/resources/icons/ Apache license version 2.0.
-->
{{- /* unique_id https://discourse.gohugo.io/t/generating-a-unique-number-or-string/7930 */ -}}
{{- $unique_id := md5 now.UnixNano -}}
{{- $data := index (index $.Site.Data.plotter.a1 (.Get "data")) "series" -}}
{{- $series := "" -}}
{{- $xaxis_series := "" -}}
{{- $point_annotaion_series := "" -}}
{{- $window_size := "200" -}}
{{- with .Parent -}}
{{- $series = .Get "series" -}}
{{- $xaxis_series = .Get "xaxis_series" -}}
{{- $point_annotaion_series = .Get "point_annotaion_series" -}}
{{- if .Get "window_size" -}}
{{- $window_size = .Get "window_size" -}}
{{- end -}}
{{- else -}}
{{- $series = .Get "series" -}}
{{- $xaxis_series = .Get "xaxis_series" -}}
{{- $point_annotaion_series = .Get "point_annotaion_series" -}}
{{- if .Get "window_size" -}}
{{- $window_size = .Get "window_size" -}}
{{- end -}}
{{- end -}}
<div style=' width: 100%; padding-top: 20px; padding-bottom: 40px;'>
<div id='chart{{$unique_id}}' style='width:100%; height: 200'> </div>
<div style='display:flex; flex-direction: row; padding-left: 30px; padding-right: 0px;'>
<div style='flex: initial; display:flex; flex-direction: row;'>
<div style='flex:auto;'>
<button id='playback{{$unique_id}}' style='display: none; border: none;'>
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='#606060' width='32px'
height='32px'>
<path d='M0 0h24v24H0z' fill='none' />
<path
d='M10 16.5l6-4.5-6-4.5v9zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z' />
</svg>
</button>
</div>
<div style='flex:auto;'>
<button id='pause{{$unique_id}}' style='display: none; border: none;'>
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='#606060' width='32px'
height='32px'>
<path d='M0 0h24v24H0z' fill='none' />
<path
d='M9 16h2V8H9v8zm3-14C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm1-4h2V8h-2v8z' />
</svg>
</button>
</div>
<div style='flex:auto;'>
<button id='replay{{$unique_id}}' style='border: none;'>
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" viewBox="0 0 24 24"
fill="#606060" width="32px" height="32px">
<g>
<rect fill="none" height="24" width="24" />
</g>
<g>
<g>
<path
d="M12,5V1L7,6l5,5V7c3.31,0,6,2.69,6,6s-2.69,6-6,6s-6-2.69-6-6H4c0,4.42,3.58,8,8,8s8-3.58,8-8S16.42,5,12,5z" />
<g>
<path
d="M9.56,13.49h0.45c0.21,0,0.37-0.05,0.48-0.16s0.16-0.25,0.16-0.43c0-0.08-0.01-0.15-0.04-0.22s-0.06-0.12-0.11-0.17 s-0.11-0.09-0.18-0.11s-0.16-0.04-0.25-0.04c-0.08,0-0.15,0.01-0.22,0.03s-0.13,0.05-0.18,0.1s-0.09,0.09-0.12,0.15 s-0.05,0.13-0.05,0.2H8.65c0-0.18,0.04-0.34,0.11-0.48s0.17-0.27,0.3-0.37s0.27-0.18,0.44-0.23s0.35-0.08,0.54-0.08 c0.21,0,0.41,0.03,0.59,0.08s0.33,0.13,0.46,0.23s0.23,0.23,0.3,0.38s0.11,0.33,0.11,0.53c0,0.09-0.01,0.18-0.04,0.27 s-0.07,0.17-0.13,0.25s-0.12,0.15-0.2,0.22s-0.17,0.12-0.28,0.17c0.24,0.09,0.42,0.21,0.54,0.39s0.18,0.38,0.18,0.61 c0,0.2-0.04,0.38-0.12,0.53s-0.18,0.29-0.32,0.39s-0.29,0.19-0.48,0.24s-0.38,0.08-0.6,0.08c-0.18,0-0.36-0.02-0.53-0.07 s-0.33-0.12-0.46-0.23s-0.25-0.23-0.33-0.38s-0.12-0.34-0.12-0.55h0.85c0,0.08,0.02,0.15,0.05,0.22s0.07,0.12,0.13,0.17 s0.12,0.09,0.2,0.11s0.16,0.04,0.25,0.04c0.1,0,0.19-0.01,0.27-0.04s0.15-0.07,0.2-0.12s0.1-0.11,0.13-0.18s0.04-0.15,0.04-0.24 c0-0.11-0.02-0.21-0.05-0.29s-0.08-0.15-0.14-0.2s-0.13-0.09-0.22-0.11s-0.18-0.04-0.29-0.04H9.56V13.49z" />
<path
d="M15.3,14.24c0,0.32-0.03,0.6-0.1,0.82s-0.17,0.42-0.29,0.57s-0.28,0.26-0.45,0.33s-0.37,0.1-0.59,0.1 s-0.41-0.03-0.59-0.1s-0.33-0.18-0.46-0.33s-0.23-0.34-0.3-0.57s-0.11-0.5-0.11-0.82V13.5c0-0.32,0.03-0.6,0.1-0.82 s0.17-0.42,0.29-0.57s0.28-0.26,0.45-0.33s0.37-0.1,0.59-0.1s0.41,0.03,0.59,0.1s0.33,0.18,0.46,0.33s0.23,0.34,0.3,0.57 s0.11,0.5,0.11,0.82V14.24z M14.45,13.38c0-0.19-0.01-0.35-0.04-0.48c-0.03-0.13-0.07-0.23-0.12-0.31s-0.11-0.14-0.19-0.17 s-0.16-0.05-0.25-0.05s-0.18,0.02-0.25,0.05s-0.14,0.09-0.19,0.17s-0.09,0.18-0.12,0.31s-0.04,0.29-0.04,0.48v0.97 c0,0.19,0.01,0.35,0.04,0.48s0.07,0.24,0.12,0.32s0.11,0.14,0.19,0.17s0.16,0.05,0.25,0.05s0.18-0.02,0.25-0.05 s0.14-0.09,0.19-0.17s0.09-0.19,0.11-0.32c0.03-0.13,0.04-0.29,0.04-0.48V13.38z" />
</g>
</g>
</g>
</svg>
</button>
</div>
<div style='flex:auto;'>
<button id='forward{{$unique_id}}' style='border: none;'>
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" viewBox="0 0 24 24"
fill="#606060" width="32px" height="32px">
<g>
<rect fill="none" height="24" width="24" />
</g>
<g>
<g>
<path
d="M18,13c0,3.31-2.69,6-6,6s-6-2.69-6-6s2.69-6,6-6v4l5-5l-5-5v4c-4.42,0-8,3.58-8,8c0,4.42,3.58,8,8,8s8-3.58,8-8H18z" />
<path
d="M10.06,15.38c-0.29,0-0.62-0.17-0.62-0.54H8.59c0,0.97,0.9,1.23,1.45,1.23c0.87,0,1.51-0.46,1.51-1.25 c0-0.66-0.45-0.9-0.71-1c0.11-0.05,0.65-0.32,0.65-0.92c0-0.21-0.05-1.22-1.44-1.22c-0.62,0-1.4,0.35-1.4,1.16h0.85 c0-0.34,0.31-0.48,0.57-0.48c0.59,0,0.58,0.5,0.58,0.54c0,0.52-0.41,0.59-0.63,0.59H9.56v0.66h0.45c0.65,0,0.7,0.42,0.7,0.64 C10.71,15.11,10.5,15.38,10.06,15.38z" />
<path
d="M13.85,11.68c-0.14,0-1.44-0.08-1.44,1.82v0.74c0,1.9,1.31,1.82,1.44,1.82c0.14,0,1.44,0.09,1.44-1.82V13.5 C15.3,11.59,13.99,11.68,13.85,11.68z M14.45,14.35c0,0.77-0.21,1.03-0.59,1.03c-0.38,0-0.6-0.26-0.6-1.03v-0.97 c0-0.75,0.22-1.01,0.59-1.01c0.38,0,0.6,0.26,0.6,1.01V14.35z" />
</g>
</g>
</svg>
</button>
</div>
</div>
<input type="range" id="pos{{$unique_id}}" min="0" max="1" style="flex:auto; display:block;">
</div>
</div>
{{- with .Parent -}}
{{- else -}}
<script src="https://cdn.jsdelivr.net/npm/apexcharts@3.20.0"></script>
{{- end -}}
<script type="text/javascript">
(function () {
"use strict"
const palette = [ // palette1,palette2 https://apexcharts.com/docs/options/theme/
'#008FFB', '#00E396', '#FEB019', '#FF4560', '#775DD0',
'#3F51B5', '#03A9F4', '#4CAF50', '#F9CE1D', '#FF9800',
];
const uniqueId = '{{$unique_id}}';
const titleText = '{{.Get "title"}}' ? '{{.Get "title"}}' : '';
const enabledPalyback = '{{.Get "playback"}}' ? true : false;
const serriesParams = JSON.parse('{{$series}}');
const rawSeries = JSON.parse('{{ jsonify $data }}')
const xaxisSeriesName = '{{$xaxis_series}}' ? '{{$xaxis_series}}' : '';
const pointAnnotationSeriesName = '{{$point_annotaion_series}}' ? '{{$point_annotaion_series}}' : '';
let valWindowSize = parseInt('{{ $window_size }}', 10);
let valLen = 0;
const fullSeries = serriesParams.filter(v => rawSeries.findIndex(r => r.name === v.name) >= 0).map(v => {
const si = rawSeries.find(r => r.name === v.name);
if (valLen < si.data.length) {
valLen = si.data.length;
}
return {
name: v.name,
type: v.type,
showYaxis: v.showYaxis,
color: v.color,
hidden: v.hidden,
data: si.data,
}
})
if (valWindowSize <= 0) {
valWindowSize = valLen - 1;
}
const fullPointSeries = rawSeries.find(r => r.name === pointAnnotationSeriesName);
const pointSeries = fullPointSeries === undefined ? [] : fullPointSeries.data.map((v, i) => ({
x: i,
y: v,
marker: { size: 2 },
borderColor: '#775DD0',
label: {
style: {
color: '#808080',
},
text: fullPointSeries.name,
}
})).filter(v => v.y !== '');
const fullTimeSeries = rawSeries.find(r => r.name === xaxisSeriesName);
const timeSeries = fullTimeSeries === undefined ? [] : fullTimeSeries.data.map((v, i) => v);
const intervals = () => {
const m = rawSeries.filter(v => v.name === 'millis');
if (m.length > 0) {
const ret = new Array(valLen);
for (let i = 0; i + 1 < valLen; i++) {
ret[i] = (m[0].data[i + 1] - m[0].data[i]) * 10;
}
return ret;
}
return new Array(valLen).fill(100);
}
// const getNewSeries = pos =>
// fullSeries.map(v => ({ name: v.name, type: v.type, data: v.data.slice(pos, pos + valWindowSize) }));
const playbackBtn = document.querySelector('#playback' + uniqueId);
const pauseBtn = document.querySelector('#pause' + uniqueId);
const replayBtn = document.querySelector('#replay' + uniqueId);
const forwardBtn = document.querySelector('#forward' + uniqueId);
const { setPlayback, isPlayback } = (() => {
if (enabledPalyback) {
let isPlayback = false;
return {
setPlayback: (p) => {
isPlayback = p;
if (p) {
playbackBtn.style.display = 'none';
pauseBtn.style.display = '';
} else {
playbackBtn.style.display = '';
pauseBtn.style.display = 'none';
}
},
isPlayback: () => isPlayback,
}
}
return {
setPlayback: (p) => { },
isPlayback: () => false,
}
})();
setPlayback(false);
const handlePlaybackClick = (event) => {
setPlayback(true);
updateSeries(curSliderPos());
}
const handlePauseClick = (event) => {
setPlayback(false);
}
const handleReplayClick = (event) => {
const pos = curSliderPos();
if (pos - 30 > 0) {
setSliderPos(pos - 30);
updateSeries(curSliderPos());
}
}
const handleForwardClick = (event) => {
const pos = curSliderPos();
if (pos + 30 < valLen) {
setSliderPos(pos + 30);
} else {
setSliderPos(valLen - 1);
}
updateSeries(curSliderPos());
}
playbackBtn.addEventListener('click', handlePlaybackClick);
pauseBtn.addEventListener('click', handlePauseClick);
replayBtn.addEventListener('click', handleReplayClick);
forwardBtn.addEventListener('click', handleForwardClick);
const posSlider = document.querySelector('#pos' + uniqueId);
const curSliderPos = () => parseInt(posSlider.value, 10) + 1;
const setSliderPos = (pos) => {
const value = (pos === undefined) ? 1 : pos;
if (value - 1 !== curSliderPos()) {
posSlider.value = value - 1;
}
}
const handleChange = (event) => {
updateSeries(parseInt(event.target.value, 10));
}
posSlider.setAttribute('max', valLen);
posSlider.addEventListener('change', handleChange);
let timerId = 0;
const updateSeries = (pos) => {
setSliderPos(pos);
chart.zoomX(pos, pos + valWindowSize);
if (isPlayback()) {
if (timerId !== 0) {
clearTimeout(timerId);
}
if (pos < valLen - valWindowSize) {
timerId = setTimeout(() => updateSeries(pos + 1), intervals[pos]);
} else {
setPlayback(true);
}
}
}
const options = {
chart: {
type: 'line',
animations: {
enabled: false,
},
events: {
zoomed: (chartContext, { xaxis, yaxis }) => {
if (xaxis.min !== undefined && xaxis.max !== undefined) {
valWindowSize = xaxis.max - xaxis.min;
} else {
valWindowSize = valLen - 1;
}
setSliderPos(xaxis.min);
},
scrolled: (chartContext, { xaxis }) => {
setSliderPos(xaxis.min);
},
},
},
series: fullSeries.map(v => ({ name: v.name, type: v.type, data: v.data })),
colors: fullSeries.map((v, i) => v.color ? v.color : palette[i]),
yaxis: fullSeries.map((v, i) => {
const opposite = v.opposite === undefined ? false : v.opposite;
const show = v.showYaxis === undefined ? false : v.showYaxis;
return {
opposite: opposite,
show: show,
min: min => {
const pos = curSliderPos() - 1;
// for の方が速いかな。
// max と共通化できる。
const r = v.data.slice(pos, pos + valWindowSize).reduce((a, b) => {
return Math.min(a, b);
})
return r - 10;
},
max: max => {
const pos = curSliderPos() - 1;
const r = v.data.slice(pos, pos + valWindowSize).reduce((a, b) => {
return Math.max(a, b);
})
return r + 10;
},
}
}),
xaxis: {
// type: 'categories',
categories: timeSeries,
labels: {
show: false,
},
},
annotations: {
points: pointSeries,
},
title: {
text: titleText,
},
}
const chart = new ApexCharts(document.querySelector('#chart' + uniqueId), options);
chart.render();
fullSeries.forEach((v, i) => {
if (v.hidden) {
chart.hideSeries(v.name);
}
});
updateSeries(1);
})()
</script>
<script src='https://cdn.jsdelivr.net/npm/apexcharts'></script>
{{.Inner}}
## テスト表示
{{< replayPlotterOuter
series="[{\"name\": \"val\", \"showYaxis\":true, \"color\": \"#FF4560\"}, {\"name\": \"gyroX\", \"hidden\": true, \"opposite\": true, \"color\": \"#775DD0\"}, {\"name\": \"gyroY\", \"hidden\": true, \"opposite\": true, \"color\": \"#00E396\"}, {\"name\": \"gyroZ\", \"opposite\": true, \"color\": \"#008FFB\"}]"
xaxis_series="millis"
point_annotaion_series="peakP"
>}}
{{< replayPlotter_a1
data="2020_09_02_walk_grip_backhand_ble"
title="2020-09-02 ウォーク、日中、曇り、ケース逆手持ち BLE"
>}}
{{< replayPlotter_a1
data="2020_09_02_walk_fit_under_fingertips_ble"
title="2020-09-02 ウォーク、日中、曇り、ケース指先に沿わせる BLE"
>}}
{{< /replayPlotterOuter >}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment