希望能够通过js画出这样的一个图。不知道各路大神是否有做过这样的图没有。d3js感觉可以实现,奈何实力不够。请大家多多指教!
试试echarts呗,虽然没有现成的例子,但两种图单独出现是有的。
右边线性渐变,背景圆弧,刻度什么的canvas容易搞定。
参考https://www.cnblogs.com/zhiyishou/p/4430017.html
那个三角形网格,要用到Delaunay算法,那个js好用。
最难的是三角形内部着色,js canvas没这个功能。如果逐点着色涉及到根据权值插值。目前用2d的圆形着色顶着用先。。
现在得到的是这样。折腾了几天复杂算法忽然想明白了,只要三角形三个边,分别沿着径向渲染其起点终点,再合成下。
之所以颜色看起来有点奇怪,是因为RGB三个1/3暴力混合起来,颜色不是线性分布的。中间如果颜色跨度大的话,需要加入更细致处理,或者用HSL(HSV)的角度处理混色方式。
关键代码如下(未优化)
function drawTri(context, [p1,p2,p3]){ //绘制三重渐变, 从三角形三条边渲染后叠加 // 创建canvas let palatteCanvas = document.createElement("canvas"); palatteCanvas.width = 800; palatteCanvas.height = 800; let ctx = palatteCanvas.getContext("2d"); let width=800; let height=800; ctx.beginPath(); ctx.moveTo(p1.x,p1.y); ctx.lineTo(p2.x,p2.y); ctx.lineTo(p3.x,p3.y); ctx.closePath(); //var g1 = context.createRadialGradient(p1.x, p1.y, 5, p2.x, p2.y, 110); let palette = createPalette(); let color1 = palette.pickColor(p1.v*2.55); let color2 = palette.pickColor(p2.v*2.55); let color3 = palette.pickColor(p3.v*2.55); let g1=ctx.createLinearGradient(p1.x,p1.y,p2.x,p2.y); let g2=ctx.createLinearGradient(p2.x,p2.y,p3.x,p3.y); let g3=ctx.createLinearGradient(p3.x,p3.y,p1.x,p1.y); let colorStops1 = { 0: "rgb("+color1[0]+","+color1[1]+","+color1[2]+")", 1: "rgb("+color2[0]+","+color2[1]+","+color2[2]+")" }; let colorStops2 = { 0: "rgb("+color2[0]+","+color2[1]+","+color2[2]+")", 1: "rgb("+color3[0]+","+color3[1]+","+color3[2]+")" }; let colorStops3 = { 0: "rgb("+color3[0]+","+color3[1]+","+color3[2]+")", 1: "rgb("+color1[0]+","+color1[1]+","+color1[2]+")" }; for (const key in colorStops1) { g1.addColorStop(key, colorStops1[key]); } for (const key in colorStops2) { g2.addColorStop(key, colorStops2[key]); } for (const key in colorStops3) { g3.addColorStop(key, colorStops3[key]); } ctx.fillStyle = g1; ctx.fill(); let imageData1 = ctx.getImageData(0, 0, width, height).data; //注意:缩小这个范围到仅包括那个三角形可以减少很多重复计算 ctx.fillStyle = g2; ctx.fill(); let imageData2 = ctx.getImageData(0, 0, width, height).data; ctx.fillStyle = g3; ctx.fill(); let imageData3 = ctx.getImageData(0, 0, width, height).data; let imageData=ctx.getImageData(0, 0, width, height); let data=imageData.data; //将新三角形和原来的图像合并 //注意:缩小这个范围到仅包括那个三角形可以减少很多重复计算,但需要精确计算匹配位置 let oldImageData=context.getImageData(0, 0, width, height); for (let i = 0; i < imageData.data.length; i+=4) { data[i] = (imageData1[i]+imageData2[i]+imageData3[i])/3; data[i+1] = (imageData1[i+1]+imageData2[i+1]+imageData3[i+1])/3; data[i+2] = (imageData1[i+2]+imageData2[i+2]+imageData3[i+2])/3; data[i+3] = (imageData1[i+3]+imageData2[i+3]+imageData3[i+3])/3; } for (let i = 0; i < oldImageData.data.length; i+=4) { if( (data[i]==0)&&(data[i+1]==0)&&(data[i+2]==0)){ //空数据,不能合并 } else{ oldImageData.data[i] = data[i] ; oldImageData.data[i+1] = data[i+1] ; oldImageData.data[i+2] = data[i+2] ; oldImageData. data[i+3]=255; //如果没有这句,图像填充区域像素会“隐形” } } //console.log(data); context.putImageData(oldImageData, 0, 0); } // 创建调色盘 function createPalette() { let colorStops = { 0: "#f00", //红色 todo: 饱和红色* 该点v/100 0.25: "#ff0", //黄色 0.5: "#0f4", //绿色 0.75: "#07F", //淡蓝色 1: "#00f" //蓝色 todo: 饱和蓝色* 该点v/100 }; let width = 256, height = 20; // 创建canvas let palatteCanvas = document.createElement("canvas"); palatteCanvas.width = width; palatteCanvas.height = height; let ctx = palatteCanvas.getContext("2d"); // 创建线性渐变色 let linearGradient = ctx.createLinearGradient(0, 0, width, 0); for (const key in colorStops) { linearGradient.addColorStop(key, colorStops[key]); } // 绘制渐变色条 ctx.fillStyle = linearGradient; ctx.fillRect(0, 0, width, height); // 读取像素值 let imageData = ctx.getImageData(0, 0, width, 1).data return { canvas: palatteCanvas, pickColor: function (position) { return imageData.slice(position * 4, position * 4 + 3) } } }
调用:
//绘制三角网格内容 var triangles = Delaunay.triangulate(dataList); for(i = triangles.length; i; ) { --i; // 三点坐标 let p1={}; let p2={}; let p3={}; p1.x=dataList[triangles[i]][0]; p1.y=dataList[triangles[i]][1]; p1.v=dataList[triangles[i]][2]; --i; p2.x=dataList[triangles[i]][0]; p2.y=dataList[triangles[i]][1]; p2.v=dataList[triangles[i]][2]; --i; p3.x=dataList[triangles[i]][0]; p3.y=dataList[triangles[i]][1]; p3.v=dataList[triangles[i]][2]; //填充 drawTri(context, [p1,p2,p3]); }
试试echarts呗,虽然没有现成的例子,但两种图单独出现是有的。
右边线性渐变,背景圆弧,刻度什么的canvas容易搞定。
参考https://www.cnblogs.com/zhiyishou/p/4430017.html
那个三角形网格,要用到Delaunay算法,那个js好用。
最难的是三角形内部着色,js canvas没这个功能。如果逐点着色涉及到根据权值插值。目前用2d的圆形着色顶着用先。。
现在得到的是这样。折腾了几天复杂算法忽然想明白了,只要三角形三个边,分别沿着径向渲染其起点终点,再合成下。
之所以颜色看起来有点奇怪,是因为RGB三个1/3暴力混合起来,颜色不是线性分布的。中间如果颜色跨度大的话,需要加入更细致处理,或者用HSL(HSV)的角度处理混色方式。
关键代码如下(未优化)
调用: