javascript svg.js中文 - 自动调整SVG内的鼠标位置



svg.js教程 svg使用 (4)

@Phrogz:谢谢你的精彩榜样,我从那里学到了很多东西。 我已经改变了下面的一些内容,使它变得有点容易。 我认为就像我们在核心java中处理鼠标事件一样,我们也可以在这里处理相同的方式,所以我在你的例子中尝试了我的方式。

我已经删除了“rotateElement”功能,因为我认为它有些困难,如果它我会找到替代品。

见下面的代码:

var svg=document.getElementById("svg1");
var pt=svg.createSVGPoint();
var end_small=document.getElementById("end_small");
var line=document.getElementById("line1");

end_small.addEventListener('mousemove', function(evt) {

    var loc=getCursor(evt);
    end_small.setAttribute("cx",loc.x);
    end_small.setAttribute("cy",loc.y);

    loc = getCursor(evt); // will get each x,y for mouse move

    line.setAttribute('x2',loc.x); // apply it  as end points of line
    line.setAttribute('y2',loc.y); // apply it as end points of line

}, false);

function getCursor(evt) {
    pt.x=evt.clientX;
    pt.y=evt.clientY;
    return pt.matrixTransform(svg.getScreenCTM().inverse());
}

所以我所做的就是我刚刚将监听器添加到小圆而不是整个SVG中,每当鼠标移动到你的时候,我将从上面所述的getCursor()函数得到x, y ,我将把这个x, y作为x2, y2我的行的x2, y2 ,它不会翻译,也不会旋转。 您必须将鼠标移动到圆圈然后慢慢移动,如果您的鼠标离开圆圈,则线条将不会移动,因为我们刚刚在右侧的小圆圈上添加了侦听器。

我遇到了关于鼠标光标在我的SVG文档中的位置的麻烦。 我想在HTML页面中使用JavaScript设计一个在拖动时跟随光标的电位计。

我尝试了evt.clientX / Y和evt.screenX / Y,但由于我的SVG处于自动缩放状态 ,因此我的SVG中的坐标是不同的。 我现在一直在寻找答案,但我找不到任何解决方案(要么实时了解我的SVG重新缩放因子,要么在SVG坐标系统中具有鼠标位置功能)。

轮换将遵循一个简单的规则:

if(evt.screenX <xc)

ang = Math.atan((evt.screenY - yc)/(evt.screenX - xc))* 360 /(2 * Math.PI) - 90;
if(evt.screenX> xc)
ang = Math.atan((evt.screenY - yc)/(evt.screenX - xc))* 360 /(2 * Math.PI)+ 90;

用(xc; yc)作为旋转中心,用SVG中鼠标的坐标替换所有evt.screenX / Y.


获得正确的svg鼠标坐标很棘手。 首先,一种常见的方法是使用事件属性的clientX和clientY,分别用getBoundingClientRect()和clientLeft clientTop来减去它。

svg.addEventListener('click', event =>
{
    let bound = svg.getBoundingClientRect();

    let x = event.clientX - bound.left - svg.clientLeft - paddingLeft;
    let y = event.clientY - bound.top - svg.clientTop - paddingTop;
}

但是 ,如果svg的填充样式信息大于零,则坐标正在移动。 所以这些信息也必须是减法的:

let paddingLeft = parseFloat(style['padding-left'].replace('px', ''));
let paddingTop = parseFloat(style['padding-top'].replace('px', ''));

let x = event.clientX - bound.left - svg.clientLeft - paddingLeft;
let y = event.clientY - bound.top - svg.clientTop - paddingTop;

不太好的想法是,在某些浏览器中,border属性也会移动坐标,而在其他浏览器中则不会。 我发现,如果事件属性的x和y 不可用,则会发生转换。

if(event.x === undefined)
{
    x -= parseFloat(style['border-left-width'].replace('px', ''));
    y -= parseFloat(style['border-top-width'].replace('px', ''));
}

在此转换之后,x和y坐标可以超出范围,应该修复。 但那不是思考。

let width = svg.width.baseVal.value;
let height = svg.height.baseVal.value;

if(x < 0 || y < 0 || x >= width || y >= height)
{
    return;
}

此解决方案可用于click,mousemove,mousedown,...等。 您可以在此处访问现场演示: https://codepen.io/martinwantke/pen/xpGpZBhttps://codepen.io/martinwantke/pen/xpGpZB


请参阅此代码,该代码不仅展示了如何从屏幕空间转换为全局SVG空间,还展示了如何将点从SVG空间转换为元素的转换空间:
http://phrogz.net/svg/drag_under_transformation.xhtml

简而言之:

// Find your root SVG element
var svg = document.querySelector('svg');

// Create an SVGPoint for future math
var pt = svg.createSVGPoint();

// Get point in global SVG space
function cursorPoint(evt){
  pt.x = evt.clientX; pt.y = evt.clientY;
  return pt.matrixTransform(svg.getScreenCTM().inverse());
}

svg.addEventListener('mousemove',function(evt){
  var loc = cursorPoint(evt);
  // Use loc.x and loc.y here
},false);

编辑 :我已根据您的需求创建了一个样本(尽管只在全球SVG空间中):
http://phrogz.net/svg/rotate-to-point-at-cursor.svg

它为上面添加了以下方法:

function rotateElement(el,originX,originY,towardsX,towardsY){
  var angle = Math.atan2(towardsY-originY,towardsX-originX);
  var degrees = angle*180/Math.PI + 90;
  el.setAttribute(
    'transform',
    'translate('+originX+','+originY+') ' +
      'rotate('+degrees+') ' +
      'translate('+(-originX)+','+(-originY)+')'
  );
}

你应该知道this

this (又名“上下文”)是每个函数内的一个特殊关键字,它的值仅取决于函数的调用方式,而不是如何/何时/何处定义。 与其他变量不同,它不受词汇范围的影响。 这里有些例子:

function foo() {
    console.log(this);
}

// normal function call
foo(); // `this` will refer to `window`

// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`

// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`

要了解更多信息,请查看MDN文档

如何参考正确的this

不要使用this

你实际上不想特别访问this ,而是它所指的对象 。 这就是为什么一个简单的解决方案就是简单地创建一个也指向该对象的新变量。 变量可以有任何名称,但常见的是selfthat

function MyConstructor(data, transport) {
    this.data = data;
    var self = this;
    transport.on('data', function() {
        alert(self.data);
    });
}

由于self是一个普通的变量,它遵从词法作用域规则,并且可以在回调中访问。 这也有一个好处,就是你可以访问回调本身的this值。

明确设置回调 - 第1部分

它可能看起来像你无法控制这个值,因为它的值是自动设置的,但事实并非如此。

每个函数都有方法.bind [docs] ,它返回一个新的函数,并绑定到一个值。 该函数的行为与您所调用的.bind行为完全相同,只是this是您设置的行为。 无论该函数何时或何时被调用, this将始终引用传递的值。

function MyConstructor(data, transport) {
    this.data = data;
    var boundFunction = (function() { // parenthesis are not necessary
        alert(this.data);             // but might improve readability
    }).bind(this); // <- here we are calling `.bind()` 
    transport.on('data', boundFunction);
}

在这种情况下,我们将回调的this绑定到MyConstructorthis值。

注意:当为jQuery绑定上下文时,请改用jQuery.proxy [docs] 。 这样做的原因是,在解除绑定事件回调时,不需要存储对该函数的引用。 jQuery在内部处理。

ECMAScript 6:使用箭头功能

ECMAScript 6引入了箭头函数 ,可以将其视为lambda函数。 他们没有自己的this绑定。 相反, this是在一个正常变量的范围内查找的。 这意味着你不必调用.bind 。 这不是他们唯一的特殊行为,请参阅MDN文档以获取更多信息。

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', () => alert(this.data));
}

设置回调 - 第2部分

一些接受回调的函数/方法也接受这个回调所应该引用的值。 这与你自己绑定的基本相同,但函数/方法可以为你做。 Array#map [docs]就是这样一种方法。 其签名是:

array.map(callback[, thisArg])

第一个参数是回调,第二个参数是this应该引用的值。 这是一个人为的例子:

var arr = [1, 2, 3];
var obj = {multiplier: 42};

var new_arr = arr.map(function(v) {
    return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument

注意:您是否可以this传递值通常在该函数/方法的文档中提及。 例如, jQuery的$.ajax方法[docs]描述了一个名为context的选项:

这个对象将成为所有与Ajax相关的回调的上下文。

常见问题:使用对象方法作为回调/事件处理程序

这个问题的另一个常见表现是当一个对象方法被用作回调/事件处理程序时。 函数是JavaScript中的一等公民,术语“方法”仅仅是一个函数的口语术语,它是对象属性的值。 但是该函数没有与其“包含”对象的特定链接。

考虑下面的例子:

function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = function() {
    console.log(this.data);
};

函数this.method被分配为单击事件处理程序,但如果单击document.body ,则记录的值将是undefined ,因为在事件处理程序中, this指的是document.body ,而不是Foo的实例。
正如开头已经提到的那样, this所指的是取决于函数的调用方式 ,而不是如何定义
如果代码如下所示,则可能更明显该函数没有对该对象的隐式引用:

function method() {
    console.log(this.data);
}


function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = method;

解决方案与上面提到的相同:如果可用,请使用.bind将其明确绑定到特定值

document.body.onclick = this.method.bind(this);

或者通过使用匿名函数作为回调/事件处理程序并将对象( this )赋值给另一个变量,显式地将该函数作为对象的“方法”调用:

var self = this;
document.body.onclick = function() {
    self.method();
};

或使用箭头功能:

document.body.onclick = () => this.method();




javascript svg