【问题标题】:Mapping mouse coordinates with context使用上下文映射鼠标坐标
【发布时间】:2019-11-30 02:17:31
【问题描述】:

我在画布元素上呈现了一个 webgl。渲染后,我想允许用户用鼠标在它上面绘图(例如矩形)。由于 getContext 第二次不起作用,我在我的 webgl 画布上添加了另一个透明画布,我想用鼠标在透明画布上绘制一个矩形。问题是 mousedown 事件中的坐标与上下文坐标有很大不同

我的画布如下

<div id="container">
  <canvas id="webglCanvas" tabindex='1'></canvas>
  <canvas id="transCanvas" tabindex='1'></canvas>
</div>

获取上下文

var $canvas1 = document.getElementById('transCanvas');
var ctx = $canvas1.getContext("2d");

transCanvas 的鼠标按下事件。请注意,我已经在鼠标按下事件时对矩形进行了硬编码。稍后我将在鼠标移动等上执行此操作。这在我的画布上运行良好,我可以在我的屏幕上看到矩形。但是鼠标坐标例如 e.clientX 和 e.clientY 是百分百并离开屏幕?

function handleCanvasMouseMove(e) {
   ctx.beginPath();
   ctx.fillStyle = '#F30';
   ctx.fillRect(75, 75, 75, 75);
}

【问题讨论】:

    标签: javascript mapping


    【解决方案1】:

    请记住,您正在将每个轴上范围为 [-1..1] 的 NormalizedDeviceCoords 转换为屏幕上的某个位置。您应用的所有变换都采用模型空间顶点并将其放入以原点为中心的大小为 2 的立方体中。

    所以...我想您也想在同一空间中取回鼠标坐标。如果是这样,只需构造一个矩阵,然后将屏幕空间位置乘以该矩阵即可得到 x,y 在 [-1..1] 范围内的问题

    过去我在做类似的事情时,使用了如下一系列的变换:

    function makeClipToPixMat(width,height)
    {
        // flip the Y
        let s1 = mat4.scaling(1,-1,1);
    
        // translate so space is now [0..2]
        let t1 = mat4.translation(1,1,0);
    
        // scale so space is now [0..width], [0..height]
        let s2 = mat4.scaling(width/2,height/2,1);
    
        // s1, then t1, then s2 are applied to the input coords
        let result = mat4.matrixByMatrix(s1,t1);
        result = result.multiply(s2);
    
        return result;
    }
    

    但您会从名称中注意到,这是一个错误方向的映射。我们想将屏幕坐标映射到 NDC,但这段代码却相反。那现在呢?简单 - 要么反转矩阵,要么确定所需的一系列变换,然后构造一个矩阵,一次性完成所有这些。这是一个足够简单的转换,矩阵求逆似乎是一种非常昂贵的方法来完成如此简单的事情。

    事实上,这是我使用的函数。反转也可以正常工作,并且可以在运行时降低代码大小。

    function pixelsToClipspace(width,height)
    {
        let scaleMat = mat4.scaling( 1/(width/2), -1/(height/2), 1);
        let translateMat = mat4.translation(-width/2, -height/2, 0); //.transpose();
        let mat = mat4.matrixByMatrix(translateMat, scaleMat);
        return mat;
    }
    

    由于我现在有一些时间,所以我为您制作了一个快速演示。可惜,vec4 和矩阵有 380 行代码,而演示只有大约 35 行。 :laughs: 也就是说,它完美地说明了矩阵 .inverse() 函数是多么昂贵和复杂。

    最后:请注意,我不对包含但未使用的任何代码的准确性做出任何声明。像这样的练习对我们每个人都有好处。你得到了一些理解,我得到了更多的调试测试用例。 :) 矩阵主要是列(就像所有好的 GL 一样)

    "use strict";
    window.addEventListener('load', onLoaded, false);
    
    let s2Clip = null;
    
    function onLoaded(evt)
    {
    	let can = document.querySelector('canvas');
    	s2Clip = pixelsToClipspace(can.clientWidth, can.clientHeight);	// use clienWidth/clientHeight to avoid CSS scaling problems
    	can.addEventListener('mousemove', onMouse, false);
    }
    
    
    function onMouse(evt)
    {
    	var rawPos = new vec4(evt.offsetX, evt.offsetY, 0, 1);			
    	var trPos = s2Clip.timesVector(rawPos);
    	document.getElementById('rawMouse').innerText = `${rawPos.x}, ${rawPos.y}`
    	document.getElementById('transMouse').innerText = `${trPos.x.toFixed(2)}, ${trPos.y.toFixed(2)}`
    }
    
    function pixelsToClipspace(width,height)
    {
    	let scaleMat = mat4.scaling( 1/(width/2), -1/(height/2), 1);
    	let translateMat = mat4.translation(-width/2, -height/2, 0); //.transpose();
    	let mat = mat4.matrixByMatrix(translateMat, scaleMat);
    	return mat;
    }
    // </script>
    
    
    
    // <script origSrc='vector.js'>
    class vec4
    {
    	// w=0 for dir (cant translate), w=1 for pos (can)
    	constructor(x=0,y=0,z=0,w=0){this.values = [x,y,z,w];}
    	clone(){ return new vec4(this.x,this.y,this.z,this.w); }
    	get x(){return this.values[0];}
    	get y(){return this.values[1];}
    	get z(){return this.values[2];}
    	get w(){return this.values[3];}
    	set x(x){this.values[0]=x;}
    	set y(y){this.values[1]=y;}
    	set z(z){this.values[2]=z;}
    	set w(w){this.values[3]=w;}
    	get length(){return Math.hypot( ...this.values ); }
    	normalize(){ var l = this.length; if (l>1e-6) {this.x/=l;this.y/=l;this.z/=l;this.w/=l;} return this;}
    	scaleBy(scalar){this.x*=scalar;this.y*=scalar;this.z*=scalar;this.w*=scalar;return this;}
    	divBy(scalar){this.x/=scalar;this.y/=scalar;this.z/=scalar;this.w/=scalar;return this;}
    	add(other){return new vec4(this.x+other.x, this.y+other.y, this.z+other.z, this.w+other.w);}
    	sub(other){return new vec4(this.x-other.x, this.y-other.y, this.z-other.z, this.w-other.w);}
    	get xyz(){return new vec3(this.x,this.y,this.z);}
    	
    	toStringN(n){return `[${pad(this.x,n)}, ${pad(this.y,n)}, ${pad(this.z,n)}, ${pad(this.w,n)}]`;}
    	
    	timesMatrix(matrix)
    	{
    		let m0 = matrix.getCol(0), m1 = matrix.getCol(1), m2 = matrix.getCol(2), m3 = matrix.getCol(3);
    		return new vec4(
    			(m0.x*this.x) + (m1.x*this.y) + m2.x*this.z + m3.x*this.w,
    			(m0.y*this.x) + (m1.y*this.y) + m2.y*this.z + m3.y*this.w,
    			(m0.z*this.x) + (m1.z*this.y) + m2.z*this.z + m3.z*this.w,
    			(m0.w*this.x) + (m1.w*this.y) + m2.w*this.z + m3.w*this.w		
    		);
    	}
    	
    	vecByMatrix(m) /// operator * (matrix, vector)
    	{
    		let mc0 = m.getCol(0), mc1=m.getCol(1), mc2=m.getCol(2), mc3=m.getCol(3);
    		return new vec4(
    			(mc0.x * this.x) + (mc1.x * this.y) + (mc2.x * this.z) + (mc3.x * this.w),
    			(mc0.y * this.x) + (mc1.y * this.y) + (mc2.y * this.z) + (mc3.y * this.w),
    			(mc0.z * this.x) + (mc1.z * this.y) + (mc2.z * this.z) + (mc3.z * this.w),
    			(mc0.w * this.x) + (mc1.w * this.y) + (mc2.w * this.z) + (mc3.w * this.w),
    		);
    	}
    	
    	matrixByVec(m) /// operator * (vector, matrix)
    	{
    		let mCol0 = m.getCol(0), mCol1=m.getCol(1), mCol2=m.getCol(2), mCol3=m.getCol(3);
    		return new vec4(
    			this.x*mCol0.x + this.y*mCol0.y + this.z*mCol0.z + this.w*mCol0.w,
    			this.x*mCol1.x + this.y*mCol1.y + this.z*mCol1.z + this.w*mCol1.w,
    			this.x*mCol2.x + this.y*mCol2.y + this.z*mCol2.z + this.w*mCol2.w,
    			this.x*mCol3.x + this.y*mCol3.y + this.z*mCol3.z + this.w*mCol3.w
    		);
    	}
    }
    
    
    class mat4
    {
    	constructor(xVec4=new vec4(1,0,0,0), yVec4=new vec4(0,1,0,0), zVec4=new vec4(0,0,1,0), wVec4=new vec4(0,0,0,1) )
    	{
    		this.columns = [ 
    							xVec4.clone(), 
    							yVec4.clone(), 
    							zVec4.clone(), 
    							wVec4.clone() 
    						];
    	}
    	getCol(colIndex) {return this.columns[colIndex];}
    	setCol(colIndex, newVec) {this.columns[colIndex] = newVec.clone();}
    	
    	setIdentity()
    	{
    		let x=new vec4(1,0,0,0);
    		let y=new vec4(0,1,0,0);
    		let z=new vec4(0,0,1,0);
    		let w=new vec4(0,0,0,1);
    		this.setCol(0,x);
    		this.setCol(0,y);
    		this.setCol(0,z);
    		this.setCol(0,w);
    		return this;
    	}
    	
    	static clone(other)
    	{
    		var result = new mat4( other.columns[0], other.columns[1], other.columns[2], other.columns[3] );
    		return result;
    	}
    	clone()
    	{
    		return mat4.clone(this);
    	}
    	
    	static scaling(sx=1,sy=1,sz=1)
    	{
    		let x = new vec4(sx,0,0,);
    		let y = new vec4(0,sy,0,);
    		let z = new vec4(0,0,sz,);
    		let w = new vec4(0,0,0,1);
    		return new mat4(x,y,z,w);
    	}
    	
    	static translation(tx=0,ty=0,tz=0)
    	{
    		let X = new vec4(1,0,0,tx);
    		let Y = new vec4(0,1,0,ty);
    		let Z = new vec4(0,0,1,tz);
    		let W = new vec4(0,0,0,1);
    		return new mat4(X,Y,Z,W);
    	}
    	
    	static matrixByMatrix(m1, m2)
    	{
    		let mCol0 = m2.getCol(0), mCol1=m2.getCol(1), mCol2=m2.getCol(2), mCol3=m2.getCol(3);
    		let X = mCol0.vecByMatrix(m1);
    		let Y = mCol1.vecByMatrix(m1);
    		let Z = mCol2.vecByMatrix(m1);
    		let W = mCol3.vecByMatrix(m1);
    		return new mat4(X,Y,Z,W);
    	}
    	
    	static matTimeMat(m1,m2)
    	{
    		let mc0=m2.getCol(0),mc1=m2.getCol(1),mc2=m2.getCol(2),mc3=m2.getCol(3);
    		let x = m1.timesVector(mc0);
    		let y = m1.timesVector(mc1);
    		let z = m1.timesVector(mc2);
    		let w = m1.timesVector(mc3);
    		return new mat4(x,y,z,w);
    	}
    	
    	multiply(other,shouldPrepend=false)
    	{
    		var a=this,b=other,c;
    		if (shouldPrepend===true){a=other;b=this;}
    		c = mat4.matrixByMatrix(a,b);
    		this.columns = c.columns.slice();
    		return this;
    	}
    	
    	translate(tx=0,ty=0,tz=0)
    	{
    		return this.multiply( mat4.translation(tx,ty,tz) );
    	}
    	
    	setScale(sx=1,sy=1,sz=1)
    	{
    		let x = new vec4(sx,0,0,0);
    		let y = new vec4(0,sy,0,0);
    		let z = new vec4(0,0,sz,0);
    		let w = new vec4(0,0,0,1);
    		let tmp = new mat4(x,y,z,w);
    		this.columns = tmp.columns.slice();
    		return this;
    	}
    	setTrans(tx=0,ty=0,tz=0)
    	{
    		let x = new vec4( 1, 0, 0, 0);
    		let y = new vec4( 0, 1, 0, 0);
    		let z = new vec4( 0, 0, 1, 0);
    		let w = new vec4( tx, ty, tz, 1);
    		var tmp = new mat4(x,y,z,w);
    		this.columns = tmp.columns.slice();
    		return this;
    	}
    	setRotX(degrees)
    	{
    		let cosa = Math.cos(degrees * 3.141/180);
    		let sina = Math.sin(degrees * 3.141/180);
    		let x = new vec4(1,0,0,0);
    		let y = new vec4(0,cosa,sina,0)
    		let z = new vec4(0,-sina,cosa,0);
    		let w = new vec4(0,0,0,1);
    		let tmp = new mat4(x,y,z,w);
    		this.columns = tmp.columns.slice();
    		return this;
    	}
    	setRotY(degrees)
    	{
    		let cosa = Math.cos(degrees * 3.141/180);
    		let sina = Math.sin(degrees * 3.141/180);
    		let x = new vec4( cosa,	0,-sina,0);
    		let y = new vec4(    0,	1,   0,	0)
    		let z = new vec4( sina,	0,cosa,	0);
    		let w = new vec4(    0,	0,   0,	1);
    		let tmp = new mat4(x,y,z,w);
    		this.columns = tmp.columns.slice();
    		return this;
    	}
    	setRotZ(degrees)
    	{
    		let cosa = Math.cos(degrees * 3.141/180);
    		let sina = Math.sin(degrees * 3.141/180);
    		let x = new vec4(cosa,sina,0,0);
    		let y = new vec4(-sina,cosa,0,0)
    		let z = new vec4(0,0,1,0);
    		let w = new vec4(0,0,0,1);
    		let tmp = new mat4(x,y,z,w);
    		this.columns = tmp.columns.slice();
    		return this;
    	}
    	
    	scaleEach(sX=1,sY=1,sZ=1,shouldPrepend=false)
    	{
    		let tmp = new mat4();
    		let X = tmp.getCol(0);
    		X.x = sX;
    		tmp.setCol(0,X);
    		let Y = tmp.getCol(1);
    		Y.y = sY;
    		tmp.setCol(1,Y);
    		let Z = tmp.getCol(2);
    		Z.z = sZ;
    		tmp.setCol(2,Z);
    		return this.multiply(tmp, shouldPrepend);
    		//return this;
    	}
    	
    	scaleAll(sXYZ, shouldPrepend=false)
    	{
    		return this.scaleEach(sXYZ,sXYZ,sXYZ,shouldPrepend);
    		//return this;
    	}
    	
    	/*
    	translate(tX=0, tY=0, tZ=0, shouldPrepend=false)
    	{
    		let tmp = new mat4();
    		let W = tmp.getCol(3);
    		W.x = tX;
    		W.y = tY;
    		W.z = tZ;
    		tmp.setCol(3,W);
    		return this.multiply(tmp, shouldPrepend);
    	}
    	*/
    	
    	timesVector(vector)
    	{
    		let m0=this.getCol(0), m1=this.getCol(1), m2=this.getCol(2), m3=this.getCol(3);
    		return new vec4(
    			(vector.x*m0.x) + (vector.y*m0.y) + (vector.z*m0.z) + (vector.w*m0.w),
    			(vector.x*m1.x) + (vector.y*m1.y) + (vector.z*m1.z) + (vector.w*m1.w),
    			(vector.x*m2.x) + (vector.y*m2.y) + (vector.z*m2.z) + (vector.w*m2.w),
    			(vector.x*m3.x) + (vector.y*m3.y) + (vector.z*m3.z) + (vector.w*m3.w)
    		);
    	}
    	
    	toString()
    	{
    		let result = '', row=0,col=0;
    		result  = `[ ${this.getCol(0).x}, ${this.getCol(1).x}, ${this.getCol(2).x}, ${this.getCol(3).x} ]\n`;
    		result += `[ ${this.getCol(0).y}, ${this.getCol(1).y}, ${this.getCol(2).y}, ${this.getCol(3).y} ]\n`;
    		result += `[ ${this.getCol(0).z}, ${this.getCol(1).z}, ${this.getCol(2).z}, ${this.getCol(3).z} ]\n`;
    		result += `[ ${this.getCol(0).w}, ${this.getCol(1).w}, ${this.getCol(2).w}, ${this.getCol(3).w} ]\n`;
    		return result;
    	}
    	
    	toStrN(n)
    	{
    		return this.toStringN(n);
    	}
    	
    	toStringN(nDigs)
    	{
    		let result = '';
    		let xVec=this.getCol(0).clone(), 
    			yVec=this.getCol(1).clone(), 
    			zVec=this.getCol(2).clone(), 
    			wVec=this.getCol(3).clone();
    			
    		let vs=[xVec,yVec,zVec,wVec];
    		
    		for (var i=0,n=vs.length; i<n; i++)
    		{
    			vs[i].x = pad(vs[i].x, nDigs);
    			vs[i].y = pad(vs[i].y, nDigs);
    			vs[i].z = pad(vs[i].z, nDigs);
    			vs[i].w = pad(vs[i].w, nDigs);
    		}
    		result  = `[ ${xVec.x}, ${yVec.x}, ${zVec.x}, ${wVec.x} ]\n`;
    		result += `[ ${xVec.y}, ${yVec.y}, ${zVec.y}, ${wVec.y} ]\n`;
    		result += `[ ${xVec.z}, ${yVec.z}, ${zVec.z}, ${wVec.z} ]\n`;
    		result += `[ ${xVec.w}, ${yVec.w}, ${zVec.w}, ${wVec.w} ]\n`;
    		return result;
    	}
    	asRows(nDigs=2)
    	{
    		let result = '',xVec=this.getCol(0),yVec=this.getCol(1),zVec=this.getCol(2),wVec=this.getCol(3);
    		result = `[${xVec.x.toFixed(nDigs)}, ${xVec.y.toFixed(nDigs)}, ${xVec.z.toFixed(nDigs)}, ${xVec.w.toFixed(nDigs)}]\n`;
    		result += `[${yVec.x.toFixed(nDigs)}, ${yVec.y.toFixed(nDigs)}, ${yVec.z.toFixed(nDigs)}, ${yVec.w.toFixed(nDigs)}]\n`;
    		result += `[${zVec.x.toFixed(nDigs)}, ${zVec.y.toFixed(nDigs)}, ${zVec.z.toFixed(nDigs)}, ${zVec.w.toFixed(nDigs)}]\n`;
    		result += `[${wVec.x.toFixed(nDigs)}, ${wVec.y.toFixed(nDigs)}, ${wVec.z.toFixed(nDigs)}, ${wVec.w.toFixed(nDigs)}]\n`;
    		return result;
    	}
    
    	transpose()
    	{
    		let X=this.getCol(0), Y=this.getCol(1), Z=this.getCol(2), W=this.getCol(3);
    		
    		let tmp = new mat4(
    							new vec4(X.x,Y.x,Z.x,W.x),
    							new vec4(X.y,Y.y,Z.y,W.y),
    							new vec4(X.z,Y.z,Z.z,W.z),
    							new vec4(X.w,Y.w,Z.w,W.w),
    						);
    		this.setCol(0,X);
    		this.setCol(1,Y);
    		this.setCol(2,Z);
    		this.setCol(3,W);
    		return tmp; //this.copy(tmp);
    	}
    	
    	inverse()
    	{
    		let X = this.getCol(0), Y = this.getCol(1), Z = this.getCol(2), W = this.getCol(3);
    		let m00=X.x, m01=X.y, m02=X.z, m03=X.w,
    			m10=Y.x, m11=Y.y, m12=Y.z, m13=Y.w,
    			m20=Z.x, m21=Z.y, m22=Z.z, m23=Z.w,
    			m30=W.x, m31=W.y, m32=W.z, m33=W.w;
    			
    		let tmp_0=m22*m33, tmp_1=m32*m23, tmp_2=m12*m33,
    			tmp_3=m32*m13, tmp_4=m12*m23, tmp_5=m22*m13,
    			tmp_6=m02*m33, tmp_7=m32*m03, tmp_8=m02*m23,
    			tmp_9=m22*m03, tmp_10=m02*m13,tmp_11=m12*m03,
    			tmp_12=m20*m31,tmp_13=m30*m21,tmp_14=m10*m31,
    			tmp_15=m30*m11,tmp_16=m10*m21,tmp_17=m20*m11,
    			tmp_18=m00*m31,tmp_19=m30*m01,tmp_20=m00*m21,
    			tmp_21=m20*m01,tmp_22=m00*m11,tmp_23=m10*m01;
    
    			var t0 = (tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31) - (tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31);
    			var t1 = (tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31) - (tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31);
    			var t2 = (tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31) - (tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31);
    			var t3 = (tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21) - (tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21);
    
    			var d = 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3);
    
    			let Xo = new vec4(d*t0, d*t1, d*t2, d*t3);
    			//      d * t0,
    			//      d * t1,
    			//      d * t2,
    			//      d * t3,
    
    			let Yo = new vec4(
    								  d * ((tmp_1 * m10 + tmp_2 * m20 + tmp_5 * m30) - (tmp_0 * m10 + tmp_3 * m20 + tmp_4 * m30)),
    								  d * ((tmp_0 * m00 + tmp_7 * m20 + tmp_8 * m30) - (tmp_1 * m00 + tmp_6 * m20 + tmp_9 * m30)),
    								  d * ((tmp_3 * m00 + tmp_6 * m10 + tmp_11 * m30) - (tmp_2 * m00 + tmp_7 * m10 + tmp_10 * m30)),
    								  d * ((tmp_4 * m00 + tmp_9 * m10 + tmp_10 * m20) - (tmp_5 * m00 + tmp_8 * m10 + tmp_11 * m20))
    							);
    							
    			let Zo = new vec4(
    								  d * ((tmp_12 * m13 + tmp_15 * m23 + tmp_16 * m33) - (tmp_13 * m13 + tmp_14 * m23 + tmp_17 * m33)),
    								  d * ((tmp_13 * m03 + tmp_18 * m23 + tmp_21 * m33) - (tmp_12 * m03 + tmp_19 * m23 + tmp_20 * m33)),
    								  d * ((tmp_14 * m03 + tmp_19 * m13 + tmp_22 * m33) - (tmp_15 * m03 + tmp_18 * m13 + tmp_23 * m33)),
    								  d * ((tmp_17 * m03 + tmp_20 * m13 + tmp_23 * m23) - (tmp_16 * m03 + tmp_21 * m13 + tmp_22 * m23))
    							);
    							
    			let Wo = new vec4(
    								  d * ((tmp_14 * m22 + tmp_17 * m32 + tmp_13 * m12) - (tmp_16 * m32 + tmp_12 * m12 + tmp_15 * m22)),
    								  d * ((tmp_20 * m32 + tmp_12 * m02 + tmp_19 * m22) - (tmp_18 * m22 + tmp_21 * m32 + tmp_13 * m02)),
    								  d * ((tmp_18 * m12 + tmp_23 * m32 + tmp_15 * m02) - (tmp_22 * m32 + tmp_14 * m02 + tmp_19 * m12)),
    								  d * ((tmp_22 * m22 + tmp_16 * m02 + tmp_21 * m12) - (tmp_20 * m12 + tmp_23 * m22 + tmp_17 * m02))
    							);
    			this.columns = [Xo,Yo,Zo,Wo];
    			return this;
    	}
    }
    
    function pad(num, n)
    {
    	let str = num.toFixed(n);
    	if (num >= 0)
    		str = " " + str;
    	return str;
    }
    canvas
    {
    	background-color: #333;
    	cursor: crosshair;
    }
    <body>
    	<canvas width='300' height='300'></canvas><br>
    	<div>Screen Coords of mouse: <span id='rawMouse'></span></div>
    	<div>(2d) NDC of mouse: <span id='transMouse'></span></div>
    </body>

    【讨论】:

    • "您正在转换范围为 [-1..1] 的 NormalizedDeviceCoords" 抱歉,您是从哪里得到的?我认为 OP 正在使用标准画布上下文和 vanilla JS
    • @Apolo - 来自我们每个人在页面顶部阅读的帖子,其中明确指出使用了 webgl。 (帖子的第一句话!-“我在画布元素上呈现了一个 webgl”)
    • @enhzflep 但我试图在没有 webgl 的画布上绘图。它只是坐在上面?
    • @user2837961 - 在这种情况下,我建议您从一开始就做应该做的事情 - 您应该包含重现问题所需的最少代码。没有这方面关注的问题通常是无法回答的。如果您阅读了帮助中心,您肯定会遇到以下页面:stackoverflow.com/help/minimal-reproducible-example,其中解释了您的一些期望。
    【解决方案2】:

    我已经在代码中以 cmets 的形式进行了解释

    // Canvas viewport (4:3)
    const DRAW_WIDTH = 800;
    const DRAW_HEIGHT = 600;
    
    const RECT_SIZE = 10;
    const RECT_FILL = 'black';
    
    let canvas, ctx;
    
    function init() {
      canvas = document.querySelector("canvas");
      // setup canvas drawing space
      // this will give an aspect-ratio to the canvas
      canvas.setAttribute('width', DRAW_WIDTH);
      canvas.setAttribute('height', DRAW_HEIGHT);
      ctx = canvas.getContext('2d');
      // attach listener
      canvas.addEventListener("click", onMouseDown);
    }
    
    function onMouseDown(e) {
      // get canvas position and size infos:
      const bbox = canvas.getBoundingClientRect();
      const {
        x: canvasX,
        y: canvasY,
        width: canvasW,
        height: canvasH
      } = bbox;
      // mouse click position
      const {
        clientX: mouseX,
        clientY: mouseY
      } = e;
    
      // compute ratio between drawing size (viewport) and actual size
      const widthRatio = DRAW_WIDTH / canvasW;
      // compute x relative to your canvas
      const relativeX = (mouseX - canvasX);
      // I advise you to use int values when drawing on canvas
      // thus Math.round
      const finalX = Math.round(widthRatio * relativeX);
    
      // same for Y-axis
      const heightRatio = DRAW_HEIGHT / canvasH;
      const relativeY = (mouseY - canvasY);
      const finalY = Math.round(heightRatio * relativeY);
    
      // draw something with that:
      ctx.fillStyle = RECT_FILL;
      ctx.rect(finalX - RECT_SIZE / 2, finalY - RECT_SIZE / 2, RECT_SIZE, RECT_SIZE);
      ctx.fill();
      ctx.closePath();
    }
    
    init();
    /* set canvas width in the document */
    
    canvas {
      width: 80vw;
      margin-left: 5vw;
      background: coral;
      display: block;
    }
    &lt;canvas&gt;&lt;/canvas&gt;

    【讨论】:

    • 该操作几乎可以肯定是在 NDC 中工作。您将如何通过简单的缩放获得 -1 部分??
    • 我明白你关于 webgl 的观点,但我认为我不需要这样做来实现将鼠标坐标映射到画布 2d 坐标的主要目标
    • @Apollo - 我想我们会发现 OP 何时返回线程并提供更多细节或天堂禁止,一个“工作”示例。我们不必猜测问题是什么。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-09
    • 2023-04-01
    • 1970-01-01
    • 2021-05-05
    • 1970-01-01
    相关资源
    最近更新 更多