最有效的方式找到点鼠标移动

Most efficient way of finding point upon mousemove

本文关键字:鼠标 移动 方式找 有效      更新时间:2023-09-26

将鼠标悬停在SVG图像路径上时,我希望鼠标移动能够识别光标下方的相关点。

逻辑:当前的逻辑是浏览器计算SVG图像路径坐标。坐标存储在二维数组中。将这些坐标与鼠标移动坐标进行比较。当两者匹配时,一个预渲染的圆出现在匹配的目的地。

担忧:我主要担心的是这种行为对性能的影响,尤其是在我目前从事的项目中。如何在浏览器上简化这个过程?我可以使用'throttling'来减少浏览器必须做的计算次数,但我主要关心的是上面的逻辑。

Jsfiddle:

http://jsfiddle.net/a4tb8/

HTML:

<div id="svgContainer" style="float: left;">
    <svg viewBox="0 0 800 600" height="600" width="800">
    <path class="clipPath clipNeck" fill="none" stroke="#848484" stroke-width="1.0244" d="M329.66,99.99l22.1,4c0,0-6.9,45.5-2.1,51.6
          c0,0-11.75,72.7-10.05,83.7c0,0,18.8,81.1,17.3,85.4H238.43c-1.5-4.3,17.3-85.4,17.3-85.4c1.7-11-10.12-83.7-10.12-83.7
          c4.8-6.1-2.1-51.6-2.1-51.6l22.1-4"/>
    </svg>
</div>
<div id="stats" style="float: left;">
    <div id="total_nodes">                
    </div>
    <div id="mouse_cordinates">                
    </div>
    <div id="svg_cordinates">                
    </div>
    <div id="response_time">                
    </div>
    <div id="zoom" data-action="zoomin" style="width: 82px;height: 25px;background-color: red;border: 2px solid #000;cursor: pointer;display: none">
        Zoom In
    </div>
</div>

Js:

            var pointsArray = new Array();
            var nodes = new Array();
            var zoom = document.getElementById('zoom');
            function createNode(type, attributes) {
                var node = document.createElementNS('http://www.w3.org/2000/svg', type);
                if (type === 'image')
                    attributes.preserveAspectRatio = 'none';
                if (typeof attributes !== undefined)
                    setAttributes(node, attributes);
                return node;
            }
            function setAttributes(el, attributes) {
                for (var attribute in attributes) {
                    if (attributes[attribute] !== null && typeof (attributes[attribute]) !== "undefined") {
                        el.setAttribute(attribute, attributes[attribute]);
                    }
                }
            }
            function roundDecimalNumber(number, places) {
                var place;
                if (places === 0)
                    place = places;
                else if (!places)
                    place = 2;
                else
                    place = places;
                number = parseFloat(number);
                number = number.toFixed(place);
                number = parseFloat(number);
                return number;
            }
            function showStats(count) {
                var total_nodes = document.getElementById('total_nodes');
                total_nodes.innerHTML = 'Total Nodes = ' + count;
                zoom.style.display = 'block';
            }
            function fetchTemplatePath() {
                var path = document.getElementsByClassName('clipPath clipNeck')[0];
                if (path) {
                    var length = Math.ceil(path.getTotalLength());
                    var point, tx, ty;
//                                var tab = TabController.currentTab.id;
//                                var view = TabController.currentView;

                    var path2 = createNode('path', {
                        id: 'path_node_test',
                        d: '',
                        stroke: 'red',
                        'stroke-width': 1,
                        fill: 'none'
                    });
                    document.getElementsByTagName('svg')[0].appendChild(path2);
                    var i = 0;
                    var incrementor = 1;
                    var d = '';
                    var intervalVar = setInterval(function() {
                        point = path.getPointAtLength(i);
                        tx = roundDecimalNumber(point.x, 0);
                        ty = roundDecimalNumber(point.y, 0);


                        cordinatesXY = {
                            x: tx,
                            y: ty
                        };
                        if (!nodes[tx]) {
                            nodes[tx] = new Array();
                            nodes[tx][ty] = 1;
                        }
                        else {
                            if (!nodes[tx][ty]) {
                                nodes[tx][ty] = 1;
                            }
                        }
                        if (i === 0)
                            d = 'M ' + tx + ' ' + ty + ' L ';
                        else
                            d += tx + ' ' + ty + ' ';
                        path2.setAttribute('d', d);
                        pointsArray.push(cordinatesXY);
                        i += incrementor;
                        if (i > length) {
                            clearInterval(intervalVar);
                            showStats(i);
                        }
                    }, 1);
                }
            }
            function matchSurrondings(x, y) {
                var flag = false;
                var radius = 10;//boxes left, right, up & down;
                for (var i = x; i <= (x + radius); i++) {// right
                    if (nodes[i]) {
                        if (nodes[i][y]) {
                            return {x: i, y: y};
                        }
                    }
                }
                 for (var i = x; i >= (x - radius); i--) {// left
                    if (nodes[i]) {
                        if (nodes[i][y]) {
                            return {x: i, y: y};
                        }
                    }
                }
                for (var i = y; i >= (y - radius); i--) {// top
                    if (nodes[x]) {
                        if (nodes[x][i]) {
                            return {x: x, y: i};
                        }
                    }
                }
                for (var i = y; i <= (y + radius); i++) {// down
                    if (nodes[x]) {
                        if (nodes[x][i]) {
                            return {x: x, y: i};
                        }
                    }
                }

                return flag;
            }
            function matchMouseCordinatesWithNodes(x, y) {
                if (nodes[x]) {
                    if (nodes[x][y]) {
                        return {x: x, y: y};
                    }
                }
                var value=false;
                value = matchSurrondings(x, y);
                return value;
            }
            function getSvgCordinates(event) {
                if (!event) {
                    return  {
                        x: 0,
                        y: 0
                    };
                }
                var mainsvg = document.getElementsByTagName('svg')[0];
                var m = mainsvg.getScreenCTM();
                var p = mainsvg.createSVGPoint();
                var x, y;
                x = event.pageX;
                y = event.pageY;
                p.x = x;
                p.y = y;
                p = p.matrixTransform(m.inverse());
                x = p.x;
                y = p.y;
                x = roundDecimalNumber(x,0);
                y = roundDecimalNumber(y,0);
                return {x: x, y: y};
            }
            function createCircleNode(x, y) {
                var circle_node = document.getElementById('circle_node');
                if (!circle_node) {
                    circle_node = createNode('circle', {
                        id: 'circle_node',
                        cx: x,
                        cy: y,
                        r: 3,
                        fill: 'green',
                        stroke: 'none'
                    });
                    document.getElementsByTagName('svg')[0].appendChild(circle_node);
                }
                else {
                    circle_node.setAttribute('cx', x);
                    circle_node.setAttribute('cy', y);
                }
            }
            function defaultmousemove(event) {
                var starttime=Date.now();
                var mouse_cordinates = document.getElementById('mouse_cordinates');
                if (mouse_cordinates) {
                    mouse_cordinates.innerHTML = 'Mouse Co-ordinates = X:' + event.pageX + ' , Y:' + event.pageY;
                }
                var svgXY = getSvgCordinates(event);
                var svg_cordinates = document.getElementById('svg_cordinates');
                if (svg_cordinates) {
                    svg_cordinates.innerHTML = 'SVG Co-ordinates = X:' + svgXY.x + ' , Y:' + svgXY.y;
                }
                var nodes_matched = matchMouseCordinatesWithNodes(svgXY.x, svgXY.y);
                if (nodes_matched)
                    createCircleNode(nodes_matched.x, nodes_matched.y);
                var endTime=Date.now();
                var diff=endTime-starttime;
                diff=diff/1000;// in secs;
                var response_time=document.getElementById('response_time');
                response_time.innerHTML='Calculation completed in '+diff+' secs';
            }

            function zoomIn() {
                zoom.setAttribute('data-action', 'zoomout');
                zoom.innerHTML = 'Zoom Out';
                var mainsvg = document.getElementsByTagName('svg')[0];
                mainsvg.setAttribute('viewBox', '100 100 400 200');
            }
            function zoomOut() {
                zoom.setAttribute('data-action', 'zoomin');
                zoom.innerHTML = 'Zoom In';
                var mainsvg = document.getElementsByTagName('svg')[0];
                mainsvg.setAttribute('viewBox', '0 0 800 600');
            }
            function defaultmousedown(event) {
                event.cancelBubble = true;
                event.preventDefault();
                if (event.target.hasAttribute('data-action')) {
                    var action = event.target.getAttribute('data-action');
                    if (action === 'zoomin') {
                        zoomIn();
                    }
                    else if (action === 'zoomout') {
                        zoomOut();
                    }
                }
            }
            fetchTemplatePath();
            var mainsvg = document.getElementsByTagName('svg')[0];
            mainsvg.addEventListener('mousemove', defaultmousemove, false);
            document.addEventListener('mousedown', defaultmousedown, false);

如果路径小于几百个点,我不认为计算有问题。

但是,鼠标和svg坐标不跟踪网页在所有浏览器中滚动的时间。

尝试以下操作获取这些值:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>SCR X,Y using SVGPoint &amp; getScreenCTM</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body style='padding:10px;font-family:arial'>
<center>
<h4>SCR X,Y using SVGPoint &amp; getScreenCTM</h4>
<div style='width:90%;background-color:gainsboro;text-align:justify;padding:10px;border-radius:6px;'>
  Assume the SVG image is contained in a DIV inline within the HTML5 document. However neiher the SVG <b>viewBox</b> nor its <b>width/height</b> are the same as the DIV container.
Therfore you must use getScreenCTM to get the x,y values(scr).
</div>
<table>
<tr><td align=left>
A 400x400 DIV containing SVG.<br />
SVG has a viewBox=0 0 2000 2000
</td>
<td align=left>
<div id="svgDiv" style='background-color:lightgreen;width:400px;height:400px;'>
<svg id="mySVG" width="100%" height="100%" viewBox="0 0 2000 2000" onmousemove="getXY(evt)"  >
<circle r=20 fill=black cx=200 cy=200 />
</svg>
</div>
</td>
<td align=left>
<table style='font-family:lucida console'>
<tr><td colspan=4><b>SVG clientX/Y - DIV offset</b></td></tr>
<tr style='font-size:110%'>
<td align=right>svg X:</td>   <td><input style='border:2px solid black;font-size:120%'  type=text id=svgOffsetXValue size=1 /></td>
<td><input style='border:2px solid black;font-size:120%'  type=text id=svgOffsetYValue size=1 /></td><td align=left>:svg Y</td>
</tr>
<tr><td colspan=4><b>SVGPoint/getScreenCTM</b></td></tr>
<tr style='font-size:110%'>
<td align=right>scr X:</td> <td><input style='border:2px solid black;font-size:120%'  type=text id=scrXValue size=1 /></td>
<td><input style='border:2px solid black;font-size:120%'  type=text id=scrYValue size=1 /></td><td align=left>:scr Y</td>
</tr>
</table>
</td>
</tr>
</table>
<br />SVG Source:<br />
<textarea id=svgSourceValue style='font-size:110%;font-family:lucida console;width:90%;height:200px'></textarea>
<br />Javascript:<br />
<textarea id=jsValue style='border-radius:26px;font-size:110%;font-weight:bold;color:midnightblue;padding:16px;background-color:beige;border-width:0px;font-size:100%;font-family:lucida console;width:90%;height:400px'></textarea>
</center>
<div id='browserDiv' style='padding:3px;position:absolute;top:5px;left:5px;background-color:gainsboro;'></div>
<script id=myScript>
//---mouse move---
function getXY(evt)
{
    var rect = svgDiv.getBoundingClientRect();
    svgOffsetXValue.value=evt.clientX-rect.left
    svgOffsetYValue.value=evt.clientY-rect.top
    var pnt = mySVG.createSVGPoint();
    pnt.x = evt.clientX;
    pnt.y = evt.clientY;
    var sCTM = mySVG.getScreenCTM();
    var PNT = pnt.matrixTransform(sCTM.inverse());
    scrXValue.value=PNT.x
    scrYValue.value=PNT.y
}
</script>
<script>
document.addEventListener("onload",init(),false)
function init()
{
    svgSourceValue.value=svgDiv.innerHTML
    jsValue.value=myScript.text
}
</script>
</body>
</html>