Files
work-secretfile-selfcheck/UmiOCR-data/qt_res/qml/Widgets/ImageViewer/ImageScale.qml

229 lines
7.2 KiB
QML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ==================================================
// =============== 可缩放的图片预览组件 ===============
// ==================================================
import QtQuick 2.15
import QtQuick.Controls 2.15
import ".."
Rectangle {
id: iRoot
// ========================= 【接口】 =========================
// 可设置
property real scaleMax: 2.0 // 比例上下限
property real scaleMin: 0.1 // 比例上下限
property QtObject overlayLayer // 图片叠加层
property var border: bRect.border // 边框
// 只读
property alias showImage: showImage // 图片组件
property real scale: 1.0 // 图片缩放比例
property int imageSW: 0 // 图片原始宽高
property int imageSH: 0
// 子类重写
property var beforeShow: undefined // 展示图片之前执行的操作
// 设置图片源,展示一张图片
function setSource(source) {
if(source) {
// 特殊字符#替换为%23
if(source.startsWith("file:///") && source.includes("#"))
source = source.replace(new RegExp("#", "g"), "%23");
showImage.source = source // 设置源
}
else
showImage.source = ""
}
// 传入路径,展示图片
function showPath(path) {
if(beforeShow) beforeShow()
showImage.showPath(path)
}
// 传入imgID展示图片
function showImgID(imgID) {
if(beforeShow) beforeShow()
showImage.showImgID(imgID)
}
// 清空展示
function clear() {
if(beforeShow) beforeShow()
// showImage.clear()
showImage.source = ""
imageSW = imageSH = 0
}
// 复制当前图片
function copyImage() {
if(showImage.source == "") return
const res = qmlapp.imageManager.copyImage(showImage.source)
if(res === "[Success]")
qmlapp.popup.simple(qsTr("复制图片"), "")
else
qmlapp.popup.simple(qsTr("复制图片失败"), res)
}
// 用系统默认应用打开图片
function openImage() {
if(showImage.source == "") return
const res = qmlapp.imageManager.openImage(showImage.source)
if(res === "[Success]")
qmlapp.popup.simple(qsTr("打开图片"), "")
else
qmlapp.popup.simple(qsTr("打开图片失败"), res)
}
// 保存当前图片
function saveImage() {
if(showImage.source == "") return
saveDialog.open()
}
FileDialog_ {
id: saveDialog
title: qsTr("保存图片")
selectExisting: false
selectFolder: false
folder: shortcuts.desktop // 默认放桌面
nameFilters: ["*.png", "*.jpg"]
onAccepted: {
if(!fileUrl) {
console.log("文件对话框:未选择任何文件")
return
}
let filePath = fileUrl
const res = qmlapp.imageManager.saveImage(showImage.source, filePath)
if(res.startsWith("[Success]"))
qmlapp.popup.simple(qsTr("保存图片"), res)
else
qmlapp.popup.simple(qsTr("保存图片失败"), res)
}
}
// ========================= 【处理】 =========================
Component.onCompleted: {
// 叠加层挂父级
if(overlayLayer && overlayLayer.hasOwnProperty("parent"))
overlayLayer.parent = showImage
}
// 图片组件的状态改变
function imageStatusChanged(s) {
// 已就绪
if(s == Image.Ready) {
imageSW = showImage.sourceSize.width // 记录图片原始宽高
imageSH = showImage.sourceSize.height
imageFullFit() // 初始大小
}
else {
imageSW = imageSH = 0
iRoot.scale = 1
}
}
// 缩放,传入 flag>0 放大, <0 缩小 0回归100%。以相框中心为锚点。
function imageScaleAddSub(flag=0, step=0.1) {
if(showImage.status != Image.Ready) return
// 计算缩放比例
let s = 1.0 // flag==0 时复原
if (flag > 0) { // 放大
s = (iRoot.scale + step).toFixed(1)
// 禁止大于上限 或 图片填满大小(裁切)
const max = Math.max(flickable.width/imageSW, flickable.height/imageSH, scaleMax)
if(s > max) s = max
}
else if(flag < 0) { // 缩小
s = (iRoot.scale - step).toFixed(1)
// 禁止小于下限 或 图片填满大小(不裁切)
const min = Math.min(flickable.width/imageSW, flickable.height/imageSH, scaleMin)
if(s < min) s = min
}
// 目标锚点
let gx = -flickable.width/2
let gy = -flickable.height/2
// 目标锚点在图片中的原比例
let s1x = (flickable.contentX-gx)/showImageContainer.width
let s1y = (flickable.contentY-gy)/showImageContainer.height
// 目标锚点在图片中的新比例,及差值
iRoot.scale = s // 更新缩放
let s2x = (flickable.contentX-gx)/showImageContainer.width
let s2y = (flickable.contentY-gy)/showImageContainer.height
let sx = s2x-s1x
let sy = s2y-s1y
// 实际长度差值
let lx = sx*showImageContainer.width
let ly = sy*showImageContainer.height
// 偏移
flickable.contentX -= lx
flickable.contentY -= ly
}
// 图片填满组件,不裁切
function imageFullFit() {
if(showImage.source == "" || imageSW <= 0) return
iRoot.scale = Math.min(flickable.width/imageSW, flickable.height/imageSH)
// 图片中心对齐相框
flickable.contentY = - (flickable.height - showImageContainer.height)/2
flickable.contentX = - (flickable.width - showImageContainer.width)/2
}
// ======================== 【布局】 =========================
color: theme.bgColor
// 滑动区域,显示图片,监听左键拖拽
Flickable {
id: flickable
anchors.fill: parent
contentWidth: showImageContainer.width
contentHeight: showImageContainer.height
clip: true
// 图片容器,大小不小于滑动区域
Item {
id: showImageContainer
width: Math.max( imageSW * iRoot.scale , flickable.width )
height: Math.max( imageSH * iRoot.scale , flickable.height )
Image_ {
id: showImage
anchors.centerIn: parent
scale: iRoot.scale
onStatusChanged: imageStatusChanged(status)
}
}
// 滚动条
ScrollBar.vertical: ScrollBar { }
ScrollBar.horizontal: ScrollBar { }
}
// 监听滚轮缩放
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
// 滚轮缩放
onWheel: {
if (wheel.angleDelta.y > 0) {
imageScaleAddSub(1) // 放大
}
else {
imageScaleAddSub(-1) // 缩小
}
}
}
// 边框
Rectangle {
id: bRect
anchors.fill: parent
color: "#00000000"
border.width: 1
border.color: theme.coverColor4
}
}