【321号】Grok~即席円・棒グラフ作成 令和070606
Grokのウェブアプリをひねり出してもらった
きょうはこれ。《表とグラフ生成ツール》。MS-Excelあるじゃんとツッコミ入れないでください。手軽に作りたい場合、先に行数指定して、構成比を自動計算、一発グラフ作成して画像生成。MS-Excelなら式を入れて、グラフを選んで作成。3Dやお好きな角度や色も使えるいいところもいっぱい。積極的に使って豊かな資料も作れます。
この《表とグラフ生成ツール》。ぱっと作ってぱっと表示。細かなレイアウトや装飾はいらない、イメージをつかみたい場合に有効です。
【使用例】
例えば売上グラフ。営業所の数と指定すると必要な行が出てきます。そこに営業所名と売上いれると、合計と構成比を自動計算するようになっています。円グラフか棒グラフを選び、画像出力かHTML文書出力できるようにしています。画像出力の例を見てみましょう。
【円グラフ】
【棒グラフ】
構成比は小数点以下第2位まで表示に調整しています。これで簡単にグラフが出来上がります。
データは1つのみ一時保存できるようにしています。保存を選んでいればブラウザを閉じてもあとから呼び出せませす。
【ソース(Grok生成)】
<!DOCTYPE html><html lang="ja"><head><meta charset="UTF-8"><title>表とグラフ生成ツール(改良版6)</title><style>body { font-family: 'Segoe UI', Arial, sans-serif; margin: 20px; background-color: #f4f4f4; }table { border-collapse: collapse; margin-bottom: 20px; width: 100%; max-width: 600px; }th, td { border: 1px solid #ccc; padding: 10px; text-align: right; }th { background-color: #e0e0e0; font-weight: 600; }#chartContainer { display: flex; justify-content: center; margin-top: 20px; }canvas { max-width: 500px; max-height: 400px; }.button { margin: 10px; padding: 12px 24px; cursor: pointer; background-color: #0078d4; color: white; border: none; border-radius: 5px; }.button:hover { background-color: #005ba1; }#titleInput, #rowsInput, #unitInput, #descriptionInput { width: 300px; padding: 8px; margin-bottom: 10px; border: 1px solid #ccc; border-radius: 4px; }#descriptionInput { width: 500px; height: 80px; resize: vertical; }#graphType { margin: 10px; padding: 8px; border-radius: 4px; }label { font-weight: 500; margin-right: 10px; }</style></head><body><h1>表とグラフ生成ツール</h1><input type="text" id="titleInput" placeholder="グラフのタイトルを入力"><br><label>行数:</label><input type="number" id="rowsInput" min="1" value="1"><label>単位:</label><input type="text" id="unitInput" placeholder="例: 万円"><br><label>説明:</label><textarea id="descriptionInput" placeholder="グラフの説明を入力"></textarea><br><button class="button" onclick="generateTable()">表を生成</button><br><table id="dataTable" style="display:none;"><thead><tr><th>項目</th><th>数値</th><th>構成比(%)</th></tr></thead><tbody id="tableBody"></tbody><tfoot><tr><td>合計</td><td id="totalValue">0</td><td id="totalRatio">100.00</td></tr></tfoot></table><select id="graphType"><option value="pie">円グラフ</option><option value="bar">棒グラフ</option></select><button class="button" onclick="generateChart()">グラフ生成</button><button class="button" onclick="clearTable()">クリア</button><button class="button" onclick="saveData()">データ保存</button><button class="button" onclick="loadData()">データ呼び出し</button><button class="button" onclick="exportGraph('jpg')">JPGでエクスポート</button><button class="button" onclick="exportGraph('html')">HTMLでエクスポート</button><div id="chartContainer"><canvas id="chart"></canvas></div><script src="https://cdn.jsdelivr.net/npm/chart.js"></script><script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2"></script><script>let chart;function generateTable() {const rows = Number(document.getElementById('rowsInput').value);if (rows < 1) {alert('行数は1以上を指定してください。');return;}const tbody = document.getElementById('tableBody');tbody.innerHTML = '';for (let i = 0; i < rows; i++) {const row = document.createElement('tr');row.innerHTML = `<td><input type="text" class="item" value="項目${i + 1}"></td><td><input type="number" class="value" placeholder="数値"></td><td><input type="number" class="ratio" placeholder="構成比" step="0.01" readonly></td>`;tbody.appendChild(row);}document.getElementById('dataTable').style.display = 'table';updateTotals();}function updateTotals() {const values = document.querySelectorAll('.value');const ratios = document.querySelectorAll('.ratio');const unit = document.getElementById('unitInput').value || '';let totalValue = 0;values.forEach(input => totalValue += Number(input.value) || 0);let totalRatio = 0;values.forEach((input, i) => {const value = Number(input.value) || 0;const ratio = totalValue > 0 ? (value / totalValue * 100) : 0;ratios[i].value = ratio.toFixed(2);totalRatio += ratio;});document.getElementById('totalValue').textContent = totalValue + (unit ? ` ${unit}` : '');document.getElementById('totalRatio').textContent = totalRatio.toFixed(2);}function generateChart() {const items = document.querySelectorAll('.item');const values = document.querySelectorAll('.value');const ratios = document.querySelectorAll('.ratio');const title = document.getElementById('titleInput').value || 'グラフ';const unit = document.getElementById('unitInput').value || '';const description = document.getElementById('descriptionInput').value || '';const graphType = document.getElementById('graphType').value;const labels = [], data = [];let isValid = true;items.forEach((item, i) => {if (item.value && values[i].value) {labels.push(`${item.value} (${values[i].value}${unit ? ` ${unit}` : ''}, ${ratios[i].value}%)`);data.push(Number(values[i].value));} else {isValid = false;}});if (!isValid || labels.length === 0) {alert('すべての項目名と数値を入力してください。');return;}if (chart) chart.destroy();const ctx = document.getElementById('chart').getContext('2d');chart = new Chart(ctx, {type: graphType,data: {labels: labels,datasets: [{label: graphType === 'bar' ? `数値 (${unit || '単位なし'})` : '',data: data,backgroundColor: graphType === 'pie' ? ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF'] : '#0078d4'}]},options: {plugins: {title: {display: true,text: [title, description],font: { size: 18, weight: 'bold' },padding: { top: 10, bottom: 10 }},legend: {position: graphType === 'pie' ? 'right' : 'top',labels: { font: { size: 12 } }},datalabels: {color: graphType === 'pie' ? '#fff' : '#000',backgroundColor: graphType === 'pie' ? 'rgba(0,0,0,0.5)' : null,borderRadius: graphType === 'pie' ? 3 : 0,anchor: graphType === 'pie' ? 'center' : 'end',align: graphType === 'pie' ? 'center' : 'top',formatter: (value, context) => {const label = context.chart.data.labels[context.dataIndex];const ratio = (context.chart.data.datasets[0].data[context.dataIndex] / context.chart.data.datasets[0].data.reduce((a, b) => a + b, 0) * 100).toFixed(2);return graphType === 'pie' ? label.split(' ')[0] + `\n${value}${unit ? ` ${unit}` : ''} (${ratio}%)` : `${value}${unit ? ` ${unit}` : ''}`;},font: { size: 12 }}},scales: graphType === 'bar' ? {y: {beginAtZero: true,title: { display: true, text: unit || '数値' }},x: { ticks: { font: { size: 10 } } }} : {}},plugins: [ChartDataLabels]});}function clearTable() {document.getElementById('tableBody').innerHTML = '';document.getElementById('titleInput').value = '';document.getElementById('rowsInput').value = '1';document.getElementById('unitInput').value = '';document.getElementById('descriptionInput').value = '';document.getElementById('totalValue').textContent = '0';document.getElementById('totalRatio').textContent = '100.00';document.getElementById('dataTable').style.display = 'none';if (chart) chart.destroy();}function saveData() {const items = document.querySelectorAll('.item');const values = document.querySelectorAll('.value');const ratios = document.querySelectorAll('.ratio');const title = document.getElementById('titleInput').value;const unit = document.getElementById('unitInput').value;const description = document.getElementById('descriptionInput').value;const data = {title,unit,description,rows: Array.from(items).map((item, i) => ({item: item.value,value: values[i].value,ratio: ratios[i].value}))};localStorage.setItem('graphData', JSON.stringify(data));alert('データが保存されました。');}function loadData() {const data = JSON.parse(localStorage.getItem('graphData'));if (!data) {alert('保存されたデータがありません。');return;}document.getElementById('titleInput').value = data.title;document.getElementById('rowsInput').value = data.rows.length;document.getElementById('unitInput').value = data.unit;document.getElementById('descriptionInput').value = data.description;generateTable();const items = document.querySelectorAll('.item');const values = document.querySelectorAll('.value');const ratios = document.querySelectorAll('.ratio');data.rows.forEach((row, i) => {items[i].value = row.item;values[i].value = row.value;ratios[i].value = row.ratio;});updateTotals();}function exportGraph(format) {const title = document.getElementById('titleInput').value || 'グラフ';const description = document.getElementById('descriptionInput').value || '';const canvas = document.getElementById('chart');if (!chart) {alert('先にグラフを生成してください。');return;}const tempCanvas = document.createElement('canvas');tempCanvas.width = 800;tempCanvas.height = 600;const ctx = tempCanvas.getContext('2d');// PowerPoint風デザインctx.fillStyle = '#ffffff';ctx.fillRect(0, 0, tempCanvas.width, tempCanvas.height);ctx.fillStyle = '#0078d4';ctx.fillRect(0, 0, tempCanvas.width, 80);ctx.font = 'bold 28px Segoe UI';ctx.fillStyle = '#ffffff';ctx.textAlign = 'left';ctx.fillText(title, 20, 50);ctx.font = '16px Segoe UI';ctx.fillStyle = '#333333';ctx.textAlign = 'left';// 説明文を複数行で描画const maxWidth = tempCanvas.width - 40;const lineHeight = 20;let words = description.split(' ');let line = '';let y = 120;for (let i = 0; i < words.length; i++) {const testLine = line + words[i] + ' ';const metrics = ctx.measureText(testLine);if (metrics.width > maxWidth && i > 0) {ctx.fillText(line, 20, y);line = words[i] + ' ';y += lineHeight;} else {line = testLine;}}ctx.fillText(line, 20, y);// グラフの描画位置とサイズを調整const graphWidth = 700;const graphHeight = 350;const scaleX = graphWidth / canvas.width;const scaleY = graphHeight / canvas.height;const scale = Math.min(scaleX, scaleY);const scaledWidth = canvas.width * scale;const scaledHeight = canvas.height * scale;const xPos = (tempCanvas.width - scaledWidth) / 2;const yPos = 150 + (y - 120);ctx.drawImage(canvas, xPos, yPos, scaledWidth, scaledHeight);if (format === 'jpg') {const link = document.createElement('a');link.href = tempCanvas.toDataURL('image/jpeg', 0.9);link.download = `${title}.jpg`;link.click();} else if (format === 'html') {const imgData = tempCanvas.toDataURL('image/jpeg', 0.9);const htmlContent = `<!DOCTYPE html><html><head><title>${title}</title><style>body { font-family: 'Segoe UI', Arial, sans-serif; margin: 0; background-color: #f4f4f4; }.slide { width: 800px; height: 600px; background-color: #ffffff; margin: 20px auto; box-shadow: 0 4px 8px rgba(0,0,0,0.2); }.header { background-color: #0078d4; height: 80px; padding: 20px; }.header h1 { color: #ffffff; margin: 0; font-size: 28px; }.description { padding: 10px 20px; color: #333333; font-size: 16px; }.graph { display: block; margin: 20px auto; max-width: 700px; max-height: 350px; }</style></head><body><div class="slide"><div class="header"><h1>${title}</h1></div><div class="description">${description}</div><img src="${imgData}" alt="${title}" class="graph"></div></body></html>`;const blob = new Blob([htmlContent], { type: 'text/html' });const link = document.createElement('a');link.href = URL.createObjectURL(blob);link.download = `${title}.html`;link.click();}}document.addEventListener('input', (e) => {if (e.target.classList.contains('value') || e.target.classList.contains('unitInput')) {updateTotals();}});</script></body></html>
このソースをテキストエディタかメモ帳に貼り付けて、適当なファイル名.htmlにて保存してください。オフラインでも使えます。