SVG签名捕获组件位置变更后失效的原因及解决方法求助
SVG签名组件位置变动后失效的原因与解决办法
失效原因
原代码里获取坐标用的clientX/clientY是相对于浏览器视口左上角的坐标,但SVG内部的绘图坐标系是基于自身元素的左上角。当SVG上方添加了其他内容(比如<p>标签),或者页面滚动后,视口坐标和SVG内部坐标就会出现偏移,导致绘制的路径不在SVG可见区域内,看起来组件就失效了。
适配任意页面位置的解决办法
核心是把视口坐标转换成SVG内部的相对坐标,通过SVG元素的getScreenCTM()方法获取转换矩阵,再用inverse()计算逆矩阵来完成坐标映射。
修改后的完整代码
HTML
<p>这里可以添加任意内容,签名组件放在文档底部也能正常工作</p> <!-- 其他页面内容 --> <svg id="sig_panel" xmlns="http://www.w3.org/2000/svg" width="300" height="100" viewBox="0 0 300 100"> <rect id="r" width="300" height="100" fill="#ffa"/> <line x1="0" y1="80" x2="300" y2="80" stroke="#666" stroke-width="1" stroke-dasharray="3" shape-rendering="crispEdges" pointer-events="none"/> <path id="p" stroke="navy" stroke-width="2" fill="none" pointer-events="none"/> </svg> <div class="flex"> <button id="show">显示签名路径数据</button> <button id="clear">清除签名</button> </div> <div id="pathdata"></div>
CSS
svg { margin: .5em; border: 1px solid gray; border-radius: .5em; } .flex { display: flex; } button { margin: .5em; } #pathdata { font-family: monospace; background: #ddd; padding: 1em; margin: 1em .5em; }
JavaScript
//init let r = document.getElementById('r'), p = document.getElementById('p'), signaturePath = '', isDown = false, svg = document.getElementById('sig_panel'), b_show = document.getElementById('show'), b_clear = document.getElementById('clear'), pathdata = document.getElementById('pathdata'); //drawing functions function isTouchEvent(e) { return e.type.match(/^touch/); } // 转换坐标为SVG内部相对坐标 function convertToSVGCoords(x, y) { const point = svg.createSVGPoint(); point.x = x; point.y = y; // 获取SVG的转换矩阵并计算逆矩阵,将视口坐标转为SVG内部坐标 const matrix = svg.getScreenCTM().inverse(); return point.matrixTransform(matrix); } function getCoords(e) { let clientX, clientY; if (isTouchEvent(e)) { clientX = e.targetTouches[0].clientX; clientY = e.targetTouches[0].clientY; } else { clientX = e.clientX; clientY = e.clientY; } // 转换为SVG内部坐标 const svgPoint = convertToSVGCoords(clientX, clientY); return `${svgPoint.x},${svgPoint.y}`; } function down(e) { signaturePath += 'M' + getCoords(e) + ' '; p.setAttribute('d', signaturePath); isDown = true; if (isTouchEvent(e)) e.preventDefault(); } function move(e) { if (isDown) { signaturePath += 'L' + getCoords(e) + ' '; p.setAttribute('d', signaturePath); } if (isTouchEvent(e)) e.preventDefault(); } function up(e) { isDown = false; if (isTouchEvent(e)) e.preventDefault(); } //input handlers r.addEventListener('touchstart', down, false); r.addEventListener('touchmove', move, false); r.addEventListener('touchend', up, false); r.addEventListener('mousedown', down, false); r.addEventListener('mousemove', move, false); r.addEventListener('mouseup', up, false); r.addEventListener('mouseout', up, false); //helper functions function clearSignature() { pathdata.textContent = ''; signaturePath = ''; p.setAttribute('d', ''); } function getSignature() { pathdata.textContent = signaturePath; return signaturePath; } //button handlers b_show.addEventListener('click', getSignature); b_clear.addEventListener('click', clearSignature);
关键改动说明
- 新增
convertToSVGCoords函数:利用SVG的原生API将视口坐标转换为SVG内部的相对坐标,不管SVG在页面的哪个位置,都能准确映射到SVG的绘图区域。 - 修改
getCoords函数:先获取视口坐标,再通过转换函数得到正确的SVG内部坐标,确保绘制路径始终在SVG可见范围内。
内容的提问来源于stack exchange,提问作者gatorreina
相关产品推荐
相关产品推荐

