我是 javascript 和 canvas 的新手,我有一个程序可以检测椭圆路径上的动画元素。它后来会形成一棵树。但这是我链接到 jsfiddle 的基本结构。 它在没有缩放或平移的情况下工作正常,但一旦我尝试缩放或平移,鼠标坐标就会变得困惑。 我尝试遵循 markE 来自 HTML5 canvas get coordinates after zoom and translate 的建议 但我肯定做错了什么,我显然不明白 Canvas 和转换矩阵发生了什么。我花了大约 3 天的时间尝试更改我能想到的所有组合,但我似乎无法弄清楚:s
已解决: 这是我的代码,带有缩放和鼠标平移以及用于动画和检测椭圆上的元素的代码: http://jsfiddle.net/metalloyd/A8hgz/
theCanvas = document.getElementById("canvasOne");
context = theCanvas.getContext("2d");
var status = document.getElementById('status');
var $canvas = $("#canvasOne");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
var cw = theCanvas.width;
var ch = theCanvas.height;
var scaleFactor = 1.00;
var panX = 0;
var panY = 0;
var mainX = 250;
// setting the middle point position X value
var mainY = 100;
// setting the middle point position Y value
var mainR = 125;
// main ellipse radius R
var no = 5;
// number of nodes to display
var div_angle = 360 / no;
var circle = {
centerX: mainX,
centerY: mainY + 100,
radius: mainR,
angle: .9
};
var ball = {
x: 0,
y: 0,
speed: .1
};
var a = 1.8;
//Ellipse width
var b = .5;
//Ellipse height
//Scale and Pan variables
var translatePos = {
x: 1,
y: 1
};
var startDragOffset = {};
var mouseDown = false;
var elements = [{}];
// Animate
var animateInterval = setInterval(drawScreen, 1);
//Animation
function drawScreen() {
context.clearRect(0, 0, cw, ch);
// Background box
context.beginPath();
context.fillStyle = '#EEEEEE';
context.fillRect(0, 0, theCanvas.width, theCanvas.height);
context.strokeRect(1, 1, theCanvas.width - 2, theCanvas.height - 2);
context.closePath();
context.save();
context.translate(panX, panY);
context.scale(scaleFactor, scaleFactor);
ball.speed = ball.speed + 0.001;
for (var i = 1; i <= no; i++) {
// male
new_angle = div_angle * i;
//Starting positions for ball 1 at different points on the ellipse
circle.angle = (new_angle * (0.0174532925)) + ball.speed;
//elliptical x position and y position for animation for the first ball
//xx and yy records the first balls coordinates
xx = ball.x = circle.centerX - (a * Math.cos(circle.angle)) * (circle.radius);
yy = ball.y = circle.centerY + (b * Math.sin(circle.angle)) * (circle.radius);
//Draw the first ball with position x and y
context.fillStyle = "#000000";
context.beginPath();
context.arc(ball.x, ball.y, 10, 0, Math.PI * 2, true);
context.fill();
context.closePath();
//alert("male Positions "+"X: "+ball.x+ " Y: "+ball.y);
// female
new_angle = div_angle * i + 4;
//Starting positions for ball 2 at different points on the ellipse
circle.angle = (new_angle * (0.0174532925)) + ball.speed;
//elliptical x position and y position for animation for the second ball
//ball.x and ball.y record the second balls positions
ball.x = circle.centerX - (a * Math.cos(circle.angle)) * (circle.radius);
ball.y = circle.centerY + (b * Math.sin(circle.angle)) * (circle.radius);
context.fillStyle = "#000000";
context.beginPath();
context.arc(ball.x, ball.y, 10, 0, Math.PI * 2, true);
context.fill();
context.closePath();
//alert("female Positions "+"X: "+ball.x+ " Y: "+ball.y);
//Record the ball positions in elements array for locating positions with mouse coordinates.
elements[i] = {
id: i,
femaleX: ball.x,
femaleY: ball.y,
maleX: xx,
maleY: yy,
w: 10 //radius of the ball to draw while locating the positions
};
//Text Numbering
context.beginPath();
context.fillStyle = "blue";
context.font = "bold 16px Arial";
context.fillText(elements[i].id, ball.x - 20, ball.y + 20);
context.closePath();
// line drawing--Connecting lines to the balls from the center.
context.moveTo(mainX, mainY);
context.lineTo((ball.x + xx) / 2, (ball.y + yy) / 2);
//Draw line till the middle point between ball1 and ball2
context.stroke();
context.fill();
context.closePath();
}
// center point
context.fillStyle = "#000000";
context.beginPath();
context.arc(mainX, mainY, 15, 0, Math.PI * 2, true);
context.fill();
context.closePath();
context.restore();
}
// Event Listeners
// Mouse move event to alert the position of the ball on screen
document.getElementById("plus").addEventListener("click", function () {
scaleFactor *= 1.1;
drawScreen();
}, false);
document.getElementById("minus").addEventListener("click", function () {
scaleFactor /= 1.1;
drawScreen();
}, false);
// Event listeners to handle screen panning
context.canvas.addEventListener("mousedown", function (evt) {
mouseDown = true;
startDragOffset.x = evt.clientX - translatePos.x;
startDragOffset.y = evt.clientY - translatePos.y;
});
context.canvas.addEventListener("mouseup", function (evt) {
mouseDown = false;
});
context.canvas.addEventListener("mouseover", function (evt) {
mouseDown = false;
});
context.canvas.addEventListener("mouseout", function (evt) {
mouseDown = false;
});
context.canvas.addEventListener("mousemove", function (evt) {
if (mouseDown) {
translatePos.x = evt.clientX - startDragOffset.x;
translatePos.y = evt.clientY - startDragOffset.y;
panX = translatePos.x;
panY = translatePos.y;
drawScreen();
}
evt.preventDefault();
evt.stopPropagation();
var mouseX = parseInt(evt.clientX - offsetX);
var mouseY = parseInt(evt.clientY - offsetY);
var mouseXT = parseInt((mouseX - panX) / scaleFactor);
var mouseYT = parseInt((mouseY - panY) / scaleFactor);
status.innerHTML = mouseXT + " | " + mouseYT;
for (var i = 1; i < elements.length; i++) {
var b = elements[i];
context.closePath();
context.beginPath();
context.arc(b.femaleX, b.femaleY, 10, 0, Math.PI * 2);
context.arc(b.maleX, b.maleY, 10, 0, Math.PI * 2);
if (context.isPointInPath(mouseXT, mouseYT)) {
theCanvas.style.cursor = 'pointer';
alert(b.id + " female.x: " + b.femaleX + " female.y: " + b.femaleY + " ball.x: " + ball.x + " ball.y: " + ball.y);
return;
} else theCanvas.style.cursor = 'default';
context.closePath();
}
});`
最佳答案
在这些情况下使用变换矩阵是有用的,甚至是必要的:
但是对于平移和缩放整个 Canvas 的更简单的情况,有一种更简单的方法。
首先,设置变量来保存当前的缩放和平移量:
var scaleFactor=1.00;
var panX=0;
var panY=0;
然后使用这些平移和缩放变量来完成您的所有绘图。
panX 变量进行翻译。scaleFactor 变量进行缩放。示例代码:
function drawTranslated(){
ctx.clearRect(0,0,cw,ch);
ctx.save();
ctx.translate(panX,panY);
ctx.scale(scaleFactor,scaleFactor);
ctx.beginPath();
ctx.arc(circleX,circleY,15,0,Math.PI*2);
ctx.closePath();
ctx.fillStyle=randomColor();
ctx.fill();
ctx.restore();
}
现在,关于鼠标坐标:
浏览器总是以未转换的坐标返回鼠标位置。您的绘图是在转换后的空间中完成的。如果您想知道鼠标在转换空间中的位置,可以将未转换的鼠标坐标转换为转换后的坐标,如下所示:
var mouseXTransformed = (mouseX-panX) / scaleFactor;
var mouseYTransformed = (mouseY-panY) / scaleFactor;
这里是示例代码和演示: http://jsfiddle.net/m1erickson/HwNp3/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var scrollX=$canvas.scrollLeft();
var scrollY=$canvas.scrollTop();
var cw=canvas.width;
var ch=canvas.height;
var scaleFactor=1.00;
var panX=0;
var panY=0;
var circleX=150;
var circleY=150;
var $screen=$("#screen");
var $transformed=$("#transformed");
var $trx=$("#trx");
drawTranslated();
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#scaledown").click(function(){ scaleFactor/=1.1; drawTranslated(); });
$("#scaleup").click(function(){ scaleFactor*=1.1; drawTranslated(); });
$("#panleft").click(function(){ panX-=10; drawTranslated(); });
$("#panright").click(function(){ panX+=10; drawTranslated(); });
function drawTranslated(){
ctx.clearRect(0,0,cw,ch);
ctx.save();
ctx.translate(panX,panY);
ctx.scale(scaleFactor,scaleFactor);
ctx.beginPath();
ctx.arc(circleX,circleY,15,0,Math.PI*2);
ctx.closePath();
ctx.fillStyle=randomColor();
ctx.fill();
ctx.restore();
$trx.text("Pan: "+panX+", Scale: "+scaleFactor);
}
function handleMouseMove(e){
e.preventDefault();
e.stopPropagation();
var mouseX=parseInt(e.clientX-offsetX);
var mouseY=parseInt(e.clientY-offsetY);
var mouseXT=parseInt((mouseX-panX)/scaleFactor);
var mouseYT=parseInt((mouseY-panY)/scaleFactor);
$screen.text("Screen Coordinates: "+mouseX+"/"+mouseY);
$transformed.text("Transformed Coordinates: "+mouseXT+"/"+mouseYT);
}
function randomColor(){
return('#'+Math.floor(Math.random()*16777215).toString(16));
}
}); // end $(function(){});
</script>
</head>
<body>
<h3>Transformed coordinates are mouseXY in transformed space.<br>The circles center is always at translated [150,150]</h3>
<h4 id=screen>Screen Coordinates:</h4>
<h4 id=transformed>Transformed Coordinates:</h4>
<h4 id=trx>Pan & Scale</h4>
<button id=scaledown>Scale Down</button>
<button id=scaleup>Scale Up</button>
<button id=panleft>Pan Left</button>
<button id=panright>Pan Right</button><br>
<canvas id="canvas" width=350 height=400></canvas>
</body>
</html>
关于javascript - 缩放和平移 Canvas 后鼠标坐标不匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24883585/
在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg
如何匹配未被反斜杠转义的平衡定界符对(其本身未被反斜杠转义)(无需考虑嵌套)?例如对于反引号,我试过了,但是转义的反引号没有像转义那样工作。regex=/(?!$1:"how\\"#expected"how\\`are"上面的正则表达式不考虑由反斜杠转义并位于反引号前面的反斜杠,但我愿意考虑。StackOverflow如何做到这一点?这样做的目的并不复杂。我有文档文本,其中包括内联代码的反引号,就像StackOverflow一样,我想在HTML文件中显示它,内联代码用一些spanMaterial装饰。不会有嵌套,但转义反引号或转义反斜杠可能出现在任何地方。
我有一个驼峰式字符串,例如:JustAString。我想按照以下规则形成长度为4的字符串:抓取所有大写字母;如果超过4个大写字母,只保留前4个;如果少于4个大写字母,则将最后大写字母后的字母大写并添加字母,直到长度变为4。以下是可能发生的3种情况:ThisIsMyString将产生TIMS(大写字母);ThisIsOneVeryLongString将产生TIOV(前4个大写字母);MyString将生成MSTR(大写字母+tr大写)。我设法用这个片段解决了前两种情况:str.scan(/[A-Z]/).first(4).join但是,我不太确定如何最好地修改上面的代码片段以处理最后一种
我真的为这个而疯狂。我一直在搜索答案并尝试我找到的所有内容,包括相关问题和stackoverflow上的答案,但仍然无法正常工作。我正在使用嵌套资源,但无法使表单正常工作。我总是遇到错误,例如没有路线匹配[PUT]"/galleries/1/photos"表格在这里:/galleries/1/photos/1/edit路线.rbresources:galleriesdoresources:photosendresources:galleriesresources:photos照片Controller.rbdefnew@gallery=Gallery.find(params[:galle
我已经在mountainlion上成功安装了rbenv和rubybuild。运行rbenvinstall1.9.3-p392结束于:校验和不匹配:ruby-1.9.3-p392.tar.gz(文件已损坏)预期f689a7b61379f83cbbed3c7077d83859,得到1cfc2ff433dbe80f8ff1a9dba2fd5636它正在下载的文件看起来没问题,如果我使用curl手动下载文件,我会得到同样不正确的校验和。有没有人遇到过这个?他们是如何解决的? 最佳答案 tl:博士;使用浏览器从http://ftp.rub
@raw_array[i]=~/[\W]/非常简单的正则表达式。当我用一些非拉丁字母(具体来说是俄语)尝试时,条件是错误的。我能用它做什么? 最佳答案 @raw_array[i]=~/[\p{L}]/使用西里尔字符进行测试。引用:http://www.regular-expressions.info/unicode.html#prop 关于ruby-正则表达式将非英文字母匹配为非单词字符,我们在StackOverflow上找到一个类似的问题: https://
前言一般来说,前端根据后台返回code码展示对应内容只需要在前台判断code值展示对应的内容即可,但要是匹配的code码比较多或者多个页面用到时,为了便于后期维护,后台就会使用字典表让前端匹配,下面我将在微信小程序中通过wxs的方法实现这个操作。为什么要使用wxs?{{method(a,b)}}可以看到,上述代码是一个调用方法传值的操作,在vue中很常见,多用于数据之间的转换,但由于微信小程序诸多限制的原因,你并不能优雅的这样操作,可能有人会说,为什么不用if判断实现呢?但是if判断的局限性在于如果存在数据量过大时,大量重复性操作和if判断会让你的代码显得异常冗余。wxswxs相当于是一个独立
我们有一个字符串:“”这个正则表达式://i如何从当前字符串中获取所有匹配项? 最佳答案 "".scan(//)参见scan在ruby-docs上 关于ruby-如何遍历Ruby中所有正则表达式匹配的字符串?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/6857852/
我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的
我正在尝试通过正则表达式拆分参数列表。这是一个带有我的参数列表的字符串:"a=b,c=3,d=[1,3,5,7],e,f=g"我想要的是:["a=b","c=3","d=[1,3,5,7]","e","f=g"]我试过先行,但Ruby不允许使用动态范围后行,所以这行不通:/(?如何让正则表达式忽略方括号中的所有内容? 最佳答案 也许这样的东西对你有用:str.scan(/(?:\[.*?\]|[^,])+/)编辑再三考虑。简单的非贪婪匹配器在某些嵌套括号的情况下会失败。 关于Ruby正则