如何利用js画出极坐标渲染图?D3js能不能做到?

Yxiaoyu 发布于 2019/11/07 17:29
阅读 746
收藏 2

【开源中国 APP 全新上线】“动弹” 回归、集成大模型对话、畅读技术报告”

希望能够通过js画出这样的一个图。不知道各路大神是否有做过这样的图没有。d3js感觉可以实现,奈何实力不够。请大家多多指教!

加载中
0
梅开源
梅开源

现在得到的是这样。折腾了几天复杂算法忽然想明白了,只要三角形三个边,分别沿着径向渲染其起点终点,再合成下。

之所以颜色看起来有点奇怪,是因为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]);
		
        
}

 

阿鸿哥啊
阿鸿哥啊
该评论暂时无法显示,详情咨询 QQ 群:点此入群
0
梅开源
梅开源

右边线性渐变,背景圆弧,刻度什么的canvas容易搞定。

参考https://www.cnblogs.com/zhiyishou/p/4430017.html

那个三角形网格,要用到Delaunay算法,那个js好用。

最难的是三角形内部着色,js canvas没这个功能。如果逐点着色涉及到根据权值插值。目前用2d的圆形着色顶着用先。。

 

 

Yxiaoyu
Yxiaoyu
回复 @梅开源 : 我用Delaunay,kringjs,canvas实现了一下,但是也是不太理想的感觉。。后续看看直接调用origin这个软件成图怎么样
梅开源
梅开源
回复 @Yxiaoyu : 这几天断断续续研究了下。想对任意三角形,求出延长到权值为0的两条边,最高权值的角,然后向对面边0权值径向渐变。 不知道为什么还是有些bug画出来不好看。
梅开源
梅开源
回复 @Yxiaoyu : 抽空研究中。好像webgl着色器可以根据三个点直接上色。canvas没有直接的根据三个点渲染。看你的图是径向渲染。目前可以获取三个角位置和C值,或许可以其中任意一点根据位置计算出其V值再根据对应颜色逐像素修改imgdata,或许可以直接三个角算出一个方向的径向渲染fill之。
阿鸿哥啊
阿鸿哥啊
同求,找了好久了
Yxiaoyu
Yxiaoyu
差不多就是这个了,:kissing_heart:就是内部怎么渲染
0
火眼金睛容嬷嬷
火眼金睛容嬷嬷

试试echarts呗,虽然没有现成的例子,但两种图单独出现是有的。

火眼金睛容嬷嬷
火眼金睛容嬷嬷
回复 @Yxiaoyu : 大概得用web assembly了吧,如果计算量大的话,直接用js也跑worker
Yxiaoyu
Yxiaoyu
echarts是有热力图等,但是要结合组成这种需要利用高斯等插值渲染的,貌似做不到。有人用C做过,我想,js应该可以实现
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部