Index: sources/gui/qml/compounds/InteractiveImage.qml =================================================================== diff -u -rd66feb12d37023f6f67672ff4acdb5abebdbff15 -r1ddfe05d5767681c487385339ae265b131cbb039 --- sources/gui/qml/compounds/InteractiveImage.qml (.../InteractiveImage.qml) (revision d66feb12d37023f6f67672ff4acdb5abebdbff15) +++ sources/gui/qml/compounds/InteractiveImage.qml (.../InteractiveImage.qml) (revision 1ddfe05d5767681c487385339ae265b131cbb039) @@ -1,7 +1,7 @@ -import QtQuick 2.0 +import QtQuick 2.15 Item { id: _root - property alias source:_image.source + property alias source: _image.source property real minScale : 1 property real maxScale : 4 @@ -11,15 +11,16 @@ onSourceChanged: reset() function snapBack() { - _flickable.contentX = Math.max( 0, Math.min(_flickable.contentWidth - _flickable.width, _flickable.contentX) ) - _flickable.contentY = Math.max( 0, Math.min(_flickable.contentHeight - _flickable.height, _flickable.contentY) ) + _flickable.contentX = Math.max(0, Math.min(_flickable.contentWidth - _flickable.width, _flickable.contentX)) + _flickable.contentY = Math.max(0, Math.min(_flickable.contentHeight - _flickable.height, _flickable.contentY)) } function rubberClamp() { let minX = 0 let maxX = Math.max(0, _flickable.contentWidth - _flickable.width) let minY = 0 let maxY = Math.max(0, _flickable.contentHeight - _flickable.height) + let resistance = 0.35 _flickable.contentX = (_flickable.contentX < minX) ? minX + (_flickable.contentX - minX) * resistance @@ -34,6 +35,8 @@ function reset() { _scaleTransform.xScale = 1 _scaleTransform.yScale = 1 + _scaleTransform.origin.x = 0 + _scaleTransform.origin.y = 0 _flickable.contentX = 0 _flickable.contentY = 0 } @@ -43,74 +46,73 @@ contentWidth : _image.width * _scaleTransform.xScale contentHeight : _image.height * _scaleTransform.yScale clip : true - interactive : ! pinching + interactive : !pinching - PinchArea { id: _pinchArea - anchors.fill : parent - pinch.target: _image + Behavior on contentX { NumberAnimation { duration: 150; easing.type: Easing.OutCubic } } + Behavior on contentY { NumberAnimation { duration: 150; easing.type: Easing.OutCubic } } + PinchArea { + anchors.fill: parent + + pinch.minimumScale: minScale + pinch.maximumScale: maxScale + pinch.dragAxis: Pinch.XAndYAxis + Image { id: _image - fillMode : Image.PreserveAspectFit + fillMode: Image.PreserveAspectFit transform: Scale { id: _scaleTransform origin.x: 0 origin.y: 0 - xScale : 1 - yScale : 1 + xScale: 1 + yScale: 1 - // smooth zoom feel - Behavior on xScale { NumberAnimation { duration: 80; easing.type: Easing.OutQuad } } - Behavior on yScale { NumberAnimation { duration: 80; easing.type: Easing.OutQuad } } + Behavior on xScale { NumberAnimation { duration: 150; easing.type: Easing.OutQuad } } + Behavior on yScale { NumberAnimation { duration: 150; easing.type: Easing.OutQuad } } } - // ✅ bottom-right when not zoomed - x: (_flickable.contentWidth <= _flickable.width) ? (_flickable.width - width * _scaleTransform.xScale) : -_flickable.contentX - y: (_flickable.contentHeight <= _flickable.height) ? (_flickable.height - height * _scaleTransform.yScale) : -_flickable.contentY + x: (_flickable.contentWidth <= _flickable.width) + ? (_flickable.width - width * _scaleTransform.xScale) + : -_flickable.contentX - // smooth snap + y: (_flickable.contentHeight <= _flickable.height) + ? (_flickable.height - height * _scaleTransform.yScale) + : -_flickable.contentY + Behavior on x { NumberAnimation { duration: 80; easing.type: Easing.OutCubic } } Behavior on y { NumberAnimation { duration: 80; easing.type: Easing.OutCubic } } } onPinchStarted: (pinch) => { - pinching = true - startScale = _scaleTransform.xScale + pinching = true + startScale = _scaleTransform.xScale } onPinchUpdated: (pinch) => { if (Math.abs(pinch.scale - 1) < 0.01) return - // smooth scale + // --- scale --- let newScale = startScale * pinch.scale newScale = Math.max(minScale, Math.min(maxScale, newScale)) - // 🔑 ratio between scales - let scaleRatio = newScale / _flickable.xScale + // 🔑 origin follows fingers (convert to content space) + _scaleTransform.origin.x = pinch.center.x + _flickable.contentX + _scaleTransform.origin.y = pinch.center.y + _flickable.contentY - // 🔑 adjust content so zoom follows fingers - let cx = pinch.center.x - let cy = pinch.center.y - - _flickable.contentX = (_flickable.contentX + cx) * scaleRatio - cx - _flickable.contentY = (_flickable.contentY + cy) * scaleRatio - cy - - // apply scale AFTER adjusting content _scaleTransform.xScale = newScale _scaleTransform.yScale = newScale - // 🔑 now apply drag (correct direction) + // --- pan --- let dx = pinch.center.x - pinch.previousCenter.x let dy = pinch.center.y - pinch.previousCenter.y - _flickable.contentX -= dx * 0.8 - _flickable.contentY -= dy * 0.8 + _flickable.contentX -= dx + _flickable.contentY -= dy rubberClamp() } onPinchFinished: { - reset() - snapBack() Qt.callLater(() => pinching = false) } }