Canvas统计图表(多边形,蜘蛛网,渐变色)
let c = document.getElementById("myc"); let ctx = c.getContext("2d"); // 随机值记录 let randomRes = [0, 0, 0, 0, 0, 0]; //const randomRes = [3, 0, 0, 0, 0, 0]; // 颜色值 let COLOR_INIT = "#737373"; // 单个梯形高度 let onceHeight = 30; // 声明画布大小,及窗口文档的显示区的高度和宽度,以像素记 c.width = 600; c.height = 600; // 将坐标原点移至画布中心 ctx.translate(c.width/2,c.height/2); // r多边形半径,n多边形个数,deg:多边形与夹角的度数,layerCount:多边形层数 let r=onceHeight, n=6, deg=360/n, layerCount=4; // 初始坐标点 let prevPoint = {x: 0, y: r}; // 初始渐变色层级点 let prevLinearPoint = {x: 0, y: 0}; // 渐变色坐标点记录 let linearPointArray = []; // 指数结果坐标点记录 let randomResPoint = []; // 上一个结果组 let prevRandomRes = []; var mmm = ["CB2329", "1853B7", "C3253A", "7C0A20"]; var mmmmm = 0; setInterval(() => { mmmmm === mmm.length && (mmmmm = 0); console.log(mmm[mmmmm++]) setResult([getRandom(), getRandom(), getRandom(), getRandom(), getRandom(), getRandom()], ["粒子1", "粒子2", "粒子3", "粒子4", "粒子5", "粒子6"], mmm[mmmmm++]); }, 1500) /** * 设置结果 * @param(randomResArr) array[int] 比例值 0-4 * @param(textArr) array[string] 文字说明 * @param(colorHex) string Hex基准颜色 */ function setResult(randomResArr, textArr, colorHex) { COLOR_INIT = colorHex; prevRandomRes = randomRes; randomRes = randomResArr; var timer = setInterval(() => { drawStatistics(textArr); console.log("run"); }, 16); // 偷个懒 setTimeout(() => { clearInterval(timer); }, 600); } function drawBg (textArr) { r = onceHeight; prevPoint = {x: 0, y: r}; prevLinearPoint = {x: 0, y: 0}; linearPointArray = []; randomResPoint = []; // 指数坐标点记录 // 多边形线的颜色 ctx.strokeStyle="#fff"; // 多边形线的宽度 ctx.lineWidth=1; ctx.globalAlpha = 1; ctx.globalCompositeOperation="destination-over"; // 绘制多个线段 for(let j=0;j<=layerCount;j++){ // 恢复初始坐标点 prevPoint = {x: 0, y: r}; // 临时坐标变量 let temporary = {}; for(let i=1;i<=n;i++){ let currentLinearPoint = {x: angleDistance(r)*Math.sin(getrad((i*2-1)*(deg/2))), y: angleDistance(r)*Math.cos((i*2-1)*getrad(deg/2))}; let prevPointTemporary = {x: prevPoint.x, y: prevPoint.y}; let grd = null; try { grd = ctx.createLinearGradient(linearPointArray[j-1][i-1].x, linearPointArray[j-1][i-1].y, currentLinearPoint.x, currentLinearPoint.y); } catch (err) { grd = ctx.createLinearGradient(0, 0, currentLinearPoint.x, currentLinearPoint.y); } linearPointArray[j] ? linearPointArray[j].push({x: currentLinearPoint.x, y: currentLinearPoint.y}) : linearPointArray[j] = [{x: currentLinearPoint.x, y: currentLinearPoint.y}]; grd.addColorStop(0, getLightColor(COLOR_INIT, 0.6)); grd.addColorStop(1, getLightColor(COLOR_INIT, 0.9)); ctx.fillStyle = grd; // 开始路径 ctx.beginPath(); ctx.moveTo(0,0); ctx.lineTo(prevPoint.x, prevPoint.y); prevPoint = {x: r*Math.sin(getrad(i*deg)), y: r*Math.cos(i*getrad(deg))}; ctx.lineTo(prevPoint.x, prevPoint.y); // 结束路径 ctx.closePath(); // 绘制线段 ctx.stroke(); // 填充 ctx.fill(); switch (i) { case 1: if (j === randomRes[0]) { temporary = {index: 1, x: prevPoint.x, y: prevPoint.y}; randomResPoint.push(temporary); } if (j === 4) { ctx.beginPath(); ctx.fillStyle = "#333"; ctx.textBaseline = "top"; ctx.textAlign = "center"; ctx.fillText(textArr[0], prevPointTemporary.x, prevPointTemporary.y); ctx.closePath(); } break; case 2: if (j === randomRes[1]) { temporary = {index: 2, x: prevPoint.x, y: prevPoint.y}; randomResPoint.push(temporary); } if (j === 4) { ctx.beginPath(); ctx.fillStyle = "#333"; ctx.textBaseline = "middle"; ctx.textAlign = "left"; ctx.fillText(textArr[1], prevPointTemporary.x, prevPointTemporary.y); ctx.closePath(); } break; case 3: if (j === randomRes[2]) { temporary = {index: 3, x: prevPoint.x, y: prevPoint.y}; randomResPoint.push(temporary); } if (j === 4) { ctx.beginPath(); ctx.fillStyle = "#333"; ctx.textBaseline = "middle"; ctx.textAlign = "left"; ctx.fillText(textArr[2], prevPointTemporary.x, prevPointTemporary.y); ctx.closePath(); } break; case 4: if (j === randomRes[3]) { temporary = {index: 4, x: prevPoint.x, y: prevPoint.y}; randomResPoint.push(temporary); } if (j === 4) { ctx.beginPath(); ctx.fillStyle = "#333"; ctx.textBaseline = "bottom"; ctx.textAlign = "center"; ctx.fillText(textArr[3], prevPointTemporary.x, prevPointTemporary.y); ctx.closePath(); } break; case 5: if (j === randomRes[4]) { temporary = {index: 5, x: prevPoint.x, y: prevPoint.y}; randomResPoint.push(temporary); } if (j === 4) { ctx.beginPath(); ctx.fillStyle = "#333"; ctx.textBaseline = "middle"; ctx.textAlign = "right"; ctx.fillText(textArr[4], prevPointTemporary.x, prevPointTemporary.y); ctx.closePath(); } break; case 6: if (j === randomRes[5]) { temporary = {index: 6, x: prevPoint.x, y: prevPoint.y}; randomResPoint.push(temporary); } if (j === 4) { ctx.beginPath(); ctx.fillStyle = "#333"; ctx.textBaseline = "middle"; ctx.textAlign = "right"; ctx.fillText(textArr[5], prevPointTemporary.x, prevPointTemporary.y); ctx.closePath(); } break; default : } } // 递增层级半径 r += onceHeight; } } function drawStatistics(textArr) { ctx.clearRect(-c.width / 2, -c.height / 2, c.width * 2, c.height * 2); // 清除画布 // 画背景 drawBg (textArr); // 画结果图 let grdRadial=ctx.createRadialGradient(0,0,0,0,0,r); grdRadial.addColorStop(0,getLightColor(COLOR_INIT, 0.6)); grdRadial.addColorStop(1,getDarkColor(COLOR_INIT, 0.3)); // ctx.translate(c.width/2,c.height/2); ctx.globalCompositeOperation="source-over"; ctx.lineWidth=2; ctx.strokeStyle=getLightColor(COLOR_INIT, 0.5); ctx.beginPath(); ctx.fillStyle = grdRadial; ctx.globalAlpha = 0.8; let result = randomResPoint.sort((a, b) => (a.index - b.index)); for(let randomIdx=0; randomIdx<result.length; randomIdx++) { let currentIncremnet = 0; if (prevRandomRes.length) { prevRandomRes[randomIdx] += (randomRes[randomIdx] - prevRandomRes[randomIdx]) / 10; } randomIdx === 0 ? ctx.moveTo( prevRandomRes.length ? 0 : result[randomIdx].x, prevRandomRes.length ? (prevRandomRes[randomIdx]+1)*onceHeight : result[0].y ) : ctx.lineTo( prevRandomRes.length ? ((prevRandomRes[randomIdx]+1)*onceHeight)*Math.sin(getrad(randomIdx*deg)) : result[randomIdx].x, prevRandomRes.length ? ((prevRandomRes[randomIdx]+1)*onceHeight)*Math.cos(randomIdx*getrad(deg)) : result[randomIdx].y ); } ctx.closePath(); ctx.stroke(); ctx.fill(); } console.log("randomRes", randomRes); console.log(randomResPoint); // 封装求弧度的方法 度->弧度 function getrad(deg){ return deg/180*Math.PI; } // 封装计算渐变色倾斜角度 function angleDistance(rAngle) { return rAngle*Math.cos(getrad(deg/2)); } // 获取随机指数 function getRandom() { return Math.floor(Math.random() * 5) } // hex颜色转rgb颜色 function HexToRgb (str) { var r = /^\#?[0-9A-F]{6}$/; // test方法检查在字符串中是否存在一个模式,如果存在则返回true,否则返回false if (!r.test(str)) return window.alert("输入错误的hex"); // replace替换查找的到的字符串 str = str.replace("#", ""); // match得到查询数组 var hxs = str.match(/../g); // alert('bf:'+hxs) for (var i = 0; i < 3; i++) hxs[i] = parseInt(hxs[i], 16); // alert(parseInt(80, 16)) // console.log(hxs); return hxs; } // GRB颜色转Hex颜色 function RgbToHex (a, b, c) { var r = /^\d{1,3}$/; if (!r.test(a) || !r.test(b) || !r.test(c)) return window.alert("输入错误的rgb颜色值"); var hexs = [a.toString(16), b.toString(16), c.toString(16)]; for (var i = 0; i < 3; i++) if (hexs[i].length == 1) hexs[i] = "0" + hexs[i]; return "#" + hexs.join(""); } // 得到hex颜色值为color的加深颜色值,level为加深的程度,限0-1之间 function getDarkColor(color, level) { var r = /^\#?[0-9A-F]{6}$/; if (!r.test(color)) return window.alert("输入错误的hex颜色值"); var rgbc = this.HexToRgb(color); // floor 向下取整 for (var i = 0; i < 3; i++) rgbc[i] = Math.floor(rgbc[i] * (1 - level)); return this.RgbToHex(rgbc[0], rgbc[1], rgbc[2]); } // 得到hex颜色值为color的减淡颜色值,level为加深的程度,限0-1之间 function getLightColor(color, level) { var r = /^\#?[0-9A-F]{6}$/; if (!r.test(color)) return window.alert("输入错误的hex颜色值"); var rgbc = this.HexToRgb(color); for (var i = 0; i < 3; i++) rgbc[i] = Math.floor((255 - rgbc[i]) * level + rgbc[i]); return this.RgbToHex(rgbc[0], rgbc[1], rgbc[2]); }