React中如何实现双指捏合缩放图片时保持中心点位置?

玉惠🍀 阅读 20

在实现图片双指缩放功能时发现,每次捏合缩放后图片位置会偏移,无法保持双指中心点作为缩放中心。比如用户用两根手指缩放时,图片总是以左上角为轴心缩放。

我尝试监听touchstart和touchend事件,计算两指距离,但发现transform的origin没算对。代码大概是这样写的:


const [scale, setScale] = useState(1);
let startDistance = 0;

const handlePinch = (e) => {
  if (e.touches.length === 2) {
    const currentDistance = 
      Math.hypot(
        e.touches[0].clientX - e.touches[1].clientX,
        e.touches[0].clientY - e.touches[1].clientY
      );
    const newScale = currentDistance / startDistance;
    setScale(prev => prev * newScale);
  }
};

但这样缩放时图片会偏离手指位置,应该怎样正确计算缩放中心点坐标?是不是需要根据手指位置动态设置transform-origin属性?

我来解答 赞 13 收藏
二维码
手机扫码查看
2 条解答
开发者永臣
你说的对,光调整缩放比例是不够的,确实需要动态计算并设置transform-origin属性。不过直接用CSS的transform-origin有点麻烦,推荐通过矩阵变换来实现,这样更灵活。

大致思路是这样的:
1. 在touchstart事件中记录两指的中心点坐标。
2. 在touchmove事件中,根据新的手指位置重新计算中心点,并结合当前缩放比例更新矩阵变换。
3. 使用DOMMatrix或直接拼接transform值来应用变换。

给个代码示例吧:

const [transform, setTransform] = useState({ scale: 1, x: 0, y: 0 });

let startDistance = 0;
let startPoint = { x: 0, y: 0 };

const handleTouchStart = (e) => {
if (e.touches.length === 2) {
const touch1 = e.touches[0];
const touch2 = e.touches[1];

startDistance = Math.hypot(
touch1.clientX - touch2.clientX,
touch1.clientY - touch2.clientY
);

startPoint = {
x: (touch1.clientX + touch2.clientX) / 2,
y: (touch1.clientY + touch2.clientY) / 2
};
}
};

const handleTouchMove = (e) => {
if (e.touches.length === 2) {
const touch1 = e.touches[0];
const touch2 = e.touches[1];

const currentDistance = Math.hypot(
touch1.clientX - touch2.clientX,
touch1.clientY - touch2.clientY
);

const newScale = currentDistance / startDistance;

// 计算偏移量
const dx = (touch1.clientX + touch2.clientX) / 2 - startPoint.x;
const dy = (touch1.clientY + touch2.clientY) / 2 - startPoint.y;

setTransform((prev) => ({
scale: prev.scale * newScale,
x: prev.x + dx * (1 - newScale),
y: prev.y + dy * (1 - newScale)
}));
}
};

// 应用到样式中
const applyTransform = () => {
return
translate(${transform.x}px, ${transform.y}px)
scale(${transform.scale})
;
};


然后在你的图片组件里绑定这些事件,并使用applyTransform生成的样式。这样就能保证缩放时以双指中心点为轴心了。

注意哦,这种方式不仅处理了缩放,还同时解决了平移的问题。毕竟用户捏合的时候可能还会带动图片移动。折腾过手势交互的都懂,这玩意儿调试起来真是够呛,但搞定了就特别有成就感。
点赞 9
2026-02-02 13:16
ლ玉琅
ლ玉琅 Lv1
直接动态计算缩放中心点,用 transform-origin 设置。关键代码:

const handlePinch = (e) => {
if (e.touches.length === 2) {
const [t1, t2] = e.touches;
const centerX = (t1.clientX + t2.clientX) / 2;
const centerY = (t1.clientY + t2.clientY) / 2;
const currentDistance = Math.hypot(t1.clientX - t2.clientX, t1.clientY - t2.clientY);
const newScale = currentDistance / startDistance;

setScale(prev => prev * newScale);
setOrigin({ centerX, centerY }); // 更新状态
}
};

// 样式绑定
style={{ transformOrigin: ${origin.centerX}px ${origin.centerY}px, transform: scale(${scale}) }}


算错就是没动态更新手指中心坐标。
点赞 8
2026-01-30 00:05