Signature签名组件在移动端显示错位怎么办?
我用Canvas做的签名组件,在PC上正常,但一到手机上就偏移严重,手指点的位置和画出来的线对不上,特别影响体验。
试过加了 touch-action: none,也调整了canvas的宽高比,但还是不行。是不是transform或者缩放的问题?下面是我目前的样式:
.signature-canvas {
width: 100%;
height: 200px;
border: 1px solid #ccc;
touch-action: none;
/* 尝试过加这个也没用 */
/* transform: scale(1); */
}
你现在的 CSS 把 Canvas 拉伸了,但 JS 里获取坐标的时候,如果不做比例换算,画笔位置肯定偏。加上移动端还有各种视口缩放、设备像素比(DPR)的问题,这就乱套了。
别去折腾 transform 了,解决思路其实就一句话:算出 CSS 宽高和 Canvas 属性宽高的比例,然后把触摸坐标按这个比例缩放回去。
这里分三步来解决:
第一步,正确初始化 Canvas 的尺寸。
很多时候我们直接在 HTML 标签写 width="100%" 是没用的,必须在 JS 里动态设置它的属性宽高,让它等于 CSS 渲染出来的实际像素宽高。如果想让线条在高清屏上不模糊,还得考虑设备像素比。
第二步,获取准确的坐标偏移量。
千万别用 offsetTop/offsetLeft,这玩意儿在有父级定位或者滚动条的时候会算错。一定要用 getBoundingClientRect(),这个方法能拿到 Canvas 相对于浏览器视口的精确位置。
第三步,核心的坐标转换公式。
当你手指触摸屏幕时,拿到的是屏幕坐标,你需要减去 Canvas 的偏移量,再乘以缩放比例。公式是:Canvas坐标 = (触摸坐标 - Canvas偏移) * (Canvas内部宽度 / CSS显示宽度)。
下面给你一段可以直接用的代码,你把原来获取坐标的逻辑替换成这个试试:
这里有几个细节需要你注意一下。
第一,事件监听里我加了 { passive: false },并且在代码里调用了 e.preventDefault()。这和你 CSS 里的 touch-action: none 是配套使用的,目的就是为了阻止手指画画时页面跟着滚动。
第二,getBoundingClientRect() 是获取位置的关键,它返回的 rect.left 是 Canvas 左边缘相对于视口的距离,用 clientX 减去它,就得到了手指相对于 Canvas 左上角的距离。
第三,如果你发现线条虽然位置对了,但是看起来有锯齿或者太细/太粗,那是 dpr(设备像素比)在作怪。我在 initCanvas 里处理了这个问题,通过 ctx.scale 缩放上下文,这样你写 lineWidth = 2,在手机和电脑上看起来粗细就是一样的,不会忽大忽小。
你按这个逻辑改一下,基本就能解决偏移问题了。如果还有问题,检查一下你的 Canvas 是不是被什么父级容器加了 transform 或者奇怪的定位,那也会影响 getBoundingClientRect 的计算值。