Quantcast
Channel: CSSタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9004

CSSのradial-gradient、conic-gradientで描いた円グラフをJavaScriptで可変

$
0
0
今回もこちらの続きのような感じですが、単項目の進捗度ではなく複数アイテムの円グラフを描いてみました。 JavaScript circle_graph.js 'use strict'; function drawGraph(obj) { const baseElement = document.getElementById(obj.targetId); if(baseElement === null) return; const bgColor = obj.backgroundColor ?? 'transparent', size = obj.size ?? 200, weight = obj.weight ?? 0.5, sort = obj.sort ?? 1, items = (obj.items ?? []).slice(); // ソート除外アイテム退避 const fixed = []; for(let i = 0; i < items.length; i++) { if(items[i].fixed !== undefined && 'fr'.indexOf(items[i].fixed[0]) >= 0) { fixed.push(items.splice(i--, 1)[0]); } } // ソート if(sort !== 0) items.sort(function(a, b) { return (b.v - a.v) * sort;}); // ソート除外アイテムを戻し if(fixed.length) { fixed.forEach(i => { if(i.fixed === 'f') items.unshift(i); else if(i.fixed === 'r') items.push(i); }); } // トータル算出 let total = 0; items.forEach(i => { total += +i.v;}); // 百分率追加 items.forEach(i => { i.p = total > 0 ? 100 * i.v / total : 0;}); // conic-gradientへ渡すパラメータ生成 const degree = []; let t = 0; items.forEach(i => { if(i.p > 0) degree.push(`${i.c} ${t}deg ${t = t + i.p * 3.6}deg`); }); if(total === 0) degree.push('#ddd 0 360deg'); // グラフ描画 baseElement.innerHTML = "<div class='circle'><div class='graph'></div></div>"; const circle = baseElement.querySelector('.circle'), graph = circle.querySelector('.graph'), outerRadius = size / 2, innerRadius = outerRadius * (1 - weight); circle.style.position = 'relative'; graph.style.position = 'absolute'; graph.style.mixBlendMode = 'multiply'; baseElement.style.width = baseElement.style.height = circle.style.width = circle.style.height = graph.style.width = graph.style.height = `${size}px`; circle.style.background = `radial-gradient(${bgColor} ${innerRadius - 1}px, #fff ${innerRadius}px, #fff ${outerRadius - 1}px, ${bgColor} ${outerRadius}px)`; graph.style.background = `radial-gradient(#fff ${innerRadius - 1}px, #0000 ${innerRadius}px, #0000 ${outerRadius - 1}px, #fff ${outerRadius}px), conic-gradient(${degree.join(', ')})`; return { total: total, items: items, }; } サンプルHTML <!DOCTYPE html> <html lang='ja'> <head> <meta name='viewport' content='width=device-width,initial-scale=1'> <script src='./circle_graph.js'></script> </head> <body style='background-color: #ccc'> <div id='id1'></div> <script> 'use strict'; const obj = { // 描画先要素ID targetId: 'id1', // 背景色 backgroundColor: 'transparent', // グラフ直径 size: 250, // 太さ 0~1 weight: 0.5, // ソート 1:降順 -1:昇順 0:配置順 sort: 1, // アイテム配列 items: [ // c:色, v:数, n:名前 [,fixed:ソート除外('f'前方固定|'r'後方固定)] {c:'red', v:239, n:'name1'}, {c:'orange', v:110, n:'name2'}, {c:'blue', v:75, n:'name3'}, {c:'cyan', v:33, n:'name4'}, {c:'yellow', v:428, n:'name5'}, {c:'magenta', v:183, n:'name6'}, {c:'#fff', v:31, n:'other1', fixed:'f'}, {c:'#444', v:230, n:'other2', fixed:'r'}, ], }; window.addEventListener('DOMContentLoaded', function(){ const res = drawGraph(obj); console.log(res); }); </script> </body> </html> こんな感じになります。 動作デモ スライダーでの入力反映サンプル <!DOCTYPE html> <html lang='ja'> <head> <meta charset='utf-8'> <meta name='viewport' content='width=device-width,initial-scale=1'> <script src='./circle_graph.js'></script> <style> .panel { background-color: #fff8; position: fixed; bottom: 0px; left: 0px; padding: 8px; width: 100%; } .colorcode, .name { width: 80px; } .picker { width: 28px; height: 20px; padding: 0px; } .slider { width: 50%; } .view { display: flex; padding: 10px; } div[id=id1] { display: inline-table; } div[id=id2] { margin: 8px; } #icon { position: absolute; top: 0px; right: 20px; width: 20px; height: 20px; cursor: pointer; } </style> </head> <body style='background-color: #ccc'> <div class='view'> <div id='id1'></div> <div id='id2'></div> </div> <div class='panel'> <span id='icon'>▼</span> <input type='range' min='10' max='500' id='size' oninput='update()'>直径&emsp; <input type='range' min='0' max='1' step='0.02' id='weight' oninput='update()'>太さ&emsp; <select id='sort' onchange='update()'> <option value='0'>配置順</option> <option value='1'>降順</option> <option value='-1'>昇順</option> </select> <div class='items'> <br> <span class='p'> <input type='checkbox' class='check' checked onclick='chgActive()'> <input type='text' class='colorcode' value='#000000' oninput='update()'> <input type='color' class='picker' oninput='update(1)'> <input type='text' class='name' value='name1' oninput='update()'> <input type='range' class='slider' min='0' max='1000' value='0' oninput='update()'> <select class='fixed' onchange='update()'> <option value=''>---</option> <option value='f'>前方固定</option> <option value='r'>後方固定</option> </select> </span> </div> </div> <script> 'use strict'; const obj = { targetId: 'id1', size: 250, weight: 1, sort: -1, items: [ {c:'#ff0000', v:50, n:'name1'}, {c:'#00ff00', v:100, n:'name2'}, {c:'#ffff00', v:150, n:'name3'}, {c:'#0000ff', v:200, n:'name4'}, {c:'#ff00ff', v:250, n:'name5'}, {c:'#00ffff', v:300, n:'name6'}, {c:'#ffffff', v:150, n:'other1'}, {c:'#000000', v:150, n:'other2'}, ], }; window.addEventListener('DOMContentLoaded', function() { const panelItems = document.querySelector('.panel .items'); let s1 = panelItems.innerHTML, s2 = s1.repeat(obj.items.length); panelItems.innerHTML = s2; const colorcodes = document.querySelectorAll('input[class=colorcode]'), sliders = document.querySelectorAll('input[class=slider]'), pickers = document.querySelectorAll('input[class=picker]'), names = document.querySelectorAll('input[class=name]'), fixeds = document.querySelectorAll('select[class=fixed]'); for(let i = 0; i < obj.items.length; i++) { pickers[i].value = colorcodes[i].value = obj.items[i].c; sliders[i].value = obj.items[i].v; names[i].value = obj.items[i].n; if(obj.items[i].fixed !== undefined && 'fr'.indexOf(obj.items[i].fixed) >= 0) { fixeds[i].value = obj.items[i].fixed; } } document.getElementById('size').value = obj.size; document.getElementById('weight').value = obj.weight; document.getElementById('sort').value = obj.sort; putList(drawGraph(obj)); document.getElementById('icon').addEventListener('click', function(){ const p = document.querySelector('.panel'); if(p.style.height !== '22px') { this.textContent = '▲'; p.style.height = '22px'; } else { this.textContent = '▼'; p.style.height = ''; } }); }); function update(s) { const checkboxs = document.querySelectorAll('input[type=checkbox]'), colorcodes = document.querySelectorAll('input[class=colorcode]'), sliders = document.querySelectorAll('input[class=slider]'), pickers = document.querySelectorAll('input[class=picker]'), names = document.querySelectorAll('input[class=name]'), fixeds = document.querySelectorAll('select[class=fixed]'); const p = []; obj.items = []; for(let i = 0; i < sliders.length; i++) { if(!checkboxs[i].checked) continue; if(s) colorcodes[i].value = pickers[i].value; else pickers[i].value = cvtColorCode(colorcodes[i].value); const colorcode = colorcodes[i].value, slider = sliders[i].value, name = names[i].value, fixed = fixeds[i].value; p.push({c:colorcode,v:slider,n:name,fixed:fixed}); obj.items.push({c:colorcode,v:+slider,n:name,fixed:fixed}); } obj.size = document.getElementById('size').value; obj.weight = document.getElementById('weight').value; obj.sort = document.getElementById('sort').value; putList(drawGraph(obj)); } function putList(obj) { let str = ''; obj.items.forEach(i => { if(i.v > 0) { str += `<span style='color:${i.c}'>■</span> ${i.n.replace(/^\s*$/, '----').replace(/</g, '&lt;')} : ${i.v} (${i.p.toFixed(1)}%)<br>`; } }); str += `<br>&emsp;TOTAL : ${obj.total}`; document.getElementById('id2').innerHTML = str; } function chgActive() { const ps = document.querySelectorAll('span.p'), checkboxs = document.querySelectorAll('input[type="checkbox"]'); for(let i = 0; i < checkboxs.length; i++) { ps[i].style.opacity = checkboxs[i].checked ? '1' : '0.5'; } update(); } function cvtColorCode(s) { const dummy = document.createElement('p'); dummy.style.background = s; const c = dummy.style.background.split(','); for(let i = 0; i < c.length; i++) { c[i] = parseFloat(c[i].replace(/^\D+/,'')); if(!isNaN(c[i])) c[i] = c[i].toString(16).padStart(2, '0'); } return /^[\da-f]{2}$/i.test(c[0]) ? '#' + c.slice(0, 3).join('') : '#dddddd'; } </script> </body> </html> 動作デモ スライダーが多いのでスマホでは被って見辛いかもしれません。

Viewing all articles
Browse latest Browse all 9004

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>