Touch events are wrong for transformed SVG elements

允我心安 提交于 2021-01-29 03:14:20

问题


In the process of experimenting with scaling/panning an inline SVG image by applying a matrix transform I have discovered a rather peculiar thing. After loading the image I am attaching a touchstart event listener to some of the elements in the SVG image and the event fires right away when the object is touched. However, after applying a transform

document.getElementById('mysvg').setAttribute('transform''matrix(a b c d e)')

which has the effect of scaling and/or translating the entire SVG image touching the same object no longer triggered the expected touch event. After some experiment I found that the event could still be triggered by the touch location on screen had no bearing to the actual new placement of the object on the screen. I then proceeded to first removeEventListener followed by addEventListener for the object after issuing the matrix transform and lo & behold the touch event handling was back to normal.

Quite apart from the fact that I would like to avoid the rather expensive operations of removing & then reassigning the same event listener after each pan/zoom I would like to understand just why this is happening. It is like the browser is locating the pixel location of the object at the addEventListener stage and then holds on to that somewhere in its memory blissfully ignorant of any object displacements that might have occurred later.

Can anyone here tell me what is going on here and how I can go about retaining the utility of the touch event after pan & zoom in a more efficient manner?


回答1:


I've set up a similar issue: There is a <circle> element, with a transform attribute inside an <svg>. The 'touchstart' event fires only at the first tap on the <circle>. After that it doesn't trigger the 'touchstart' event anymore.

I have found a strange workaround: Add a 'touchstart' eventListener to the <svg> element with a noop handler:

document.querySelector('svg').addEventListener('touchstart', () => {});

After this the <circle> triggers the 'touchstart' events perfectly.

You can test it with the folllowing snipet:

let debugLines = [];
let lines = 0;
function writeDebug(...t) {
  let d = document.getElementById('debug');
  debugLines.unshift(`${lines++}: ${t.join(' ')}`);
  debugLines.splice(5);
  d.innerHTML = debugLines.join('<br />');
}

document.querySelectorAll('circle')[0].addEventListener('touchstart', writeDebug);

/* remove comment from the line below to test workaround */
// document.querySelector('svg').addEventListener('touchstart', () => {});
<!doctype html>
<html>
<head>
    <style>
        svg { background: #f0f0f0; width: 200px; float: left; }
    </style>
</head>
<body>
    <div id="wrap">
        <svg viewBox="-50, -50, 100, 100" class="b-circular-slider-svg">
            <circle cx="0" cy="0" r="8" 
                    stroke="#ccc" fill="#fafafa" 
                    transform="translate(0, -10)"></circle>
        </svg>
    </div>
    <strong>debug:</strong>
    <div id="debug"></div>
</body>
</html>


来源:https://stackoverflow.com/questions/56197907/touch-events-are-wrong-for-transformed-svg-elements

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!