如何让interact.js支持多个同class元素的捏合缩放?
解决interact.js多元素捏合缩放仅第一个生效的问题
这问题我之前也碰到过!核心原因很简单:你用了document.querySelector(),它只会返回匹配选择器的第一个元素,所以只有第一个.gesture-area和.scale-element被绑定了交互事件。要让所有同类元素都生效,我们需要遍历所有匹配元素,为每个元素单独绑定事件,并且给每个元素分配独立的状态(避免多个元素共享角度和缩放比例导致冲突)。
关键修改步骤
- 获取所有目标元素:用
document.querySelectorAll('.gesture-area')替代querySelector,拿到页面上所有手势区域元素 - 为每个元素创建独立状态:每个可缩放元素需要自己的
angle和scale状态,不能共用同一个全局对象 - 绑定独立的交互事件:遍历每个手势区域,找到它内部对应的缩放元素,然后为这对元素绑定gesture和drag事件,使用各自的状态
修改后的完整代码
JavaScript部分
function dragMoveListener(event) { var target = event.target; // 确保找到当前手势区域内的缩放元素 var scaleElement = target.querySelector('.scale-element') || target; var x = (parseFloat(scaleElement.getAttribute('data-x')) || 0) + event.dx; var y = (parseFloat(scaleElement.getAttribute('data-y')) || 0) + event.dy; scaleElement.style.webkitTransform = scaleElement.style.transform = `translate(${x}px, ${y}px)`; scaleElement.setAttribute('data-x', x); scaleElement.setAttribute('data-y', y); } window.dragMoveListener = dragMoveListener; // 获取所有手势区域元素 var gestureAreas = document.querySelectorAll('.gesture-area'); // 遍历每个元素,绑定独立的交互事件 gestureAreas.forEach(function(gestureArea) { // 获取当前手势区域内的缩放元素 var scaleElement = gestureArea.querySelector('.scale-element'); // 为当前元素创建独立的状态对象 var elementState = { angle: 0, scale: 1 }; var resetTimeout; interact(gestureArea) .gesturable({ onstart: function(event) { elementState.angle -= event.angle; clearTimeout(resetTimeout); scaleElement.classList.remove('reset'); }, onmove: function(event) { var currentAngle = event.angle + elementState.angle; var currentScale = event.scale * elementState.scale; // 组合平移、旋转和缩放变换 var translate = `translate(${parseFloat(scaleElement.getAttribute('data-x')) || 0}px, ${parseFloat(scaleElement.getAttribute('data-y')) || 0}px)`; var rotate = `rotate(${currentAngle}deg)`; var scale = `scale(${currentScale})`; scaleElement.style.webkitTransform = scaleElement.style.transform = `${translate} ${rotate} ${scale}`; dragMoveListener(event); }, onend: function(event) { elementState.angle += event.angle; elementState.scale *= event.scale; // 如需自动重置可取消注释 // resetTimeout = setTimeout(() => reset(scaleElement, elementState), 1000); scaleElement.classList.add('reset'); } }) .draggable({ onmove: dragMoveListener }); }); // 重置函数,接收目标元素和对应的状态 function reset(scaleElement, elementState) { scaleElement.style.webkitTransform = scaleElement.style.transform = 'translate(0,0) scale(1)'; scaleElement.setAttribute('data-x', 0); scaleElement.setAttribute('data-y', 0); elementState.angle = 0; elementState.scale = 1; }
CSS部分(保持不变)
.pinchzoomarea { display: flex; width: 300px; height: 300px; margin-bottom: 9px; justify-content: center; align-items: center; background-color: #a80000; } .gesture-area { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; width: 100px; height: 100px; -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; -webkit-box-pack: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; } .scale-element { display: block; max-width: 100%; margin: auto; touch-action: none; width: 100%; height: 100%; background-color: #fff; background-position: 0px 0px; background-size: cover; cursor: move; }
HTML部分(保持不变)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/interactjs@latest/dist/interact.min.js"></script> <div class="pinchzoomarea"> <div id="gesture-area" class="gesture-area"> <div id="scale-element" class="scale-element"></div> </div> </div> <div class="pinchzoomarea"> <div class="gesture-area"> <div class="scale-element"></div> </div> </div>
额外说明
- 我调整了
dragMoveListener,确保它能正确找到当前手势区域内的缩放元素,避免操作错误的目标 - 每个元素都有自己的
elementState对象,这样多个元素的缩放、旋转状态不会互相干扰 - 如果不需要旋转功能,可以直接去掉旋转相关的代码,只保留缩放和平移
内容的提问来源于stack exchange,提问作者s.zane
相关产品推荐
相关产品推荐

