终于解决了这个切换试题放大的内容乱跑的问题(更新视图zoomController位置重置时机应在key重新后,不该在前,被覆盖)
This commit is contained in:
parent
bac6a7410a
commit
f050263ddf
|
|
@ -106,7 +106,8 @@ Widget $questionNumberScrollView({
|
|||
/// 无法联动
|
||||
// if (sateData.panQuestView == false) sateData.slide.value = scrollControllerNum.offset;
|
||||
|
||||
final currentMatrix = controller.zoomController.value;
|
||||
final currentMatrix = controller.zoomController?.value;
|
||||
if (currentMatrix == null) return;
|
||||
|
||||
// 创建一个新的变换矩阵,只修改垂直平移分量
|
||||
final newMatrix = Matrix4.copy(currentMatrix)
|
||||
|
|
@ -116,7 +117,7 @@ Widget $questionNumberScrollView({
|
|||
currentMatrix.getTranslation().z, // 保持 Z 轴不变
|
||||
));
|
||||
|
||||
controller.zoomController.value = newMatrix;
|
||||
controller.zoomController?.value = newMatrix;
|
||||
});
|
||||
var listenVal = sateData.slide.listen((e) {
|
||||
if (sateData.panQuestView != null && sateData.panQuestView == true && e != scrollControllerNum.offset) {
|
||||
|
|
|
|||
|
|
@ -46,10 +46,10 @@ class QuestionPaperView extends GetView<HomeworkReviewLogic> {
|
|||
if (zoomFileModel == null) {
|
||||
/// 计算高度
|
||||
return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => zoomState.zoomFile.value = ZoomFileModel(
|
||||
viewWidth: constraints.maxWidth,
|
||||
viewHeight: constraints.maxHeight,
|
||||
));
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => zoomState.zoomFile.value =
|
||||
ZoomFileModel(viewWidth: constraints.maxWidth, viewHeight: constraints.maxHeight),
|
||||
);
|
||||
return const SizedBox();
|
||||
});
|
||||
}
|
||||
|
|
@ -345,7 +345,18 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
|
|||
|
||||
var zoomKey = useState<GlobalKey>(GlobalKey());
|
||||
useValueChanged<int?, void>(zoomState.zoomFile.value?.templateId, (old, __) {
|
||||
zoomKey.value = GlobalKey();
|
||||
// 延迟重建Zoom组件,确保重置完成后再重建
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
zoomKey.value = GlobalKey();
|
||||
|
||||
// 重建后再次设置居中矩阵,确保居中状态不被覆盖
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final double offsetY = (zoomState.zoomFile.value?.imageHeightOffsetStart ?? 0).toDouble();
|
||||
if (offsetY > 0) {
|
||||
logic.zoomController?.value = Matrix4.identity()..setTranslationRaw(0, offsetY, 0);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var vnHandWritings = useValueNotifier<List<dynamic>>([]);
|
||||
|
|
@ -523,7 +534,7 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
|
|||
Offset localPosition = event.localPosition; // 相对
|
||||
|
||||
// 使用矩阵进行统一坐标反算(避免重复补偿)
|
||||
final Matrix4 matrix = logic.zoomController.value; // 当前变换矩阵
|
||||
final Matrix4 matrix = logic.zoomController?.value ?? Matrix4.identity(); // 当前变换矩阵
|
||||
final double theScale = matrix.getMaxScaleOnAxis();
|
||||
// if (theScale != 1) {
|
||||
// print("PPPPPPPPPPPPPPPPPPPPPPPP ${(zoomFile.imageHeightOffsetStart ?? 0)}");
|
||||
|
|
@ -586,69 +597,55 @@ class QuestionImageView extends HookWidget with EventBusMixin<BottomOperationBar
|
|||
width: double.infinity,
|
||||
alignment: Alignment.center,
|
||||
child: IgnorePointer(
|
||||
// 仅当“滑动试题”启用时,让 Zoom 接收触摸;否则屏蔽以避免与批注冲突
|
||||
ignoring: !annotationState.gestureMove.value,
|
||||
child:
|
||||
// ZoomView(
|
||||
// key: zoomKey.value,
|
||||
// viewWidth: maxWidth,
|
||||
// viewHeight: maxHeight,
|
||||
// imageDisplayHeight: actualHeight,
|
||||
// url: sateData.data.value!.zgtAnswer,
|
||||
// onScale: logic.zoomLogic.onScaleUpdate,
|
||||
// // onTrans: (offset) {
|
||||
// // print("偏移位置:$offset");
|
||||
// // },
|
||||
// onContentOffset: logic.zoomLogic.onPanUpPosition,
|
||||
// ),
|
||||
|
||||
Obx(
|
||||
() => Zoom(
|
||||
key: zoomKey.value,
|
||||
// initTotalZoomOut: true, // 展示全部内容 初始化不产生滚动条
|
||||
zoomSensibility: 0.05,
|
||||
scrollWeight: 4.r,
|
||||
doubleTapAnimDuration: Duration.zero,
|
||||
maxZoomWidth: maxWidth,
|
||||
maxZoomHeight: actualHeight,
|
||||
canvasColor: Colors.transparent,
|
||||
doubleTapScaleChange: 1,
|
||||
// initPosition: initPosition.value,
|
||||
// initScale: logic.zoomLogic.zoomState.initScale.value ?? 1,
|
||||
backgroundColor: Colors.transparent,
|
||||
onScaleUpdate: (double scale, double zoom) => logic.zoomLogic.onScaleUpdate(zoom),
|
||||
onPositionUpdate: logic.zoomLogic.onPanUpPosition,
|
||||
transformationController: logic.zoomController,
|
||||
child: Stack(
|
||||
children: [
|
||||
$TheCachedNetworkImage(
|
||||
imgWidth: maxWidth,
|
||||
imageUrl: sateData.data.value!.zgtAnswer,
|
||||
(_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
|
||||
// 仅当“滑动试题”启用时,让 Zoom 接收触摸;否则屏蔽以避免与批注冲突
|
||||
ignoring: !annotationState.gestureMove.value,
|
||||
child: Zoom(
|
||||
key: zoomKey.value,
|
||||
// initTotalZoomOut: true, // 展示全部内容 初始化不产生滚动条
|
||||
zoomSensibility: 0.05,
|
||||
scrollWeight: 4.r,
|
||||
doubleTapAnimDuration: Duration.zero,
|
||||
maxZoomWidth: maxWidth,
|
||||
maxZoomHeight: actualHeight,
|
||||
canvasColor: Colors.transparent,
|
||||
doubleTapScaleChange: 1,
|
||||
initScale: 1,
|
||||
backgroundColor: Colors.transparent,
|
||||
// onScaleUpdate: (double scale, double zoom) => logic.zoomLogic.onScaleUpdate(zoom),
|
||||
// onPositionUpdate: logic.zoomLogic.onPanUpPosition,
|
||||
transformationController: logic.zoomController,
|
||||
child: Obx(() {
|
||||
return Stack(
|
||||
children: [
|
||||
$TheCachedNetworkImage(
|
||||
imgWidth: maxWidth,
|
||||
imageUrl: sateData.data.value!.zgtAnswer,
|
||||
(_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
|
||||
),
|
||||
RepaintBoundary(
|
||||
key: logic.pictureOverviewKey,
|
||||
child: CustomPaint(
|
||||
// isComplex: true,
|
||||
size: Size(maxWidth, actualHeight),
|
||||
foregroundPainter: DrawingPainter(ctrl: vnHandWritings),
|
||||
// child: $TheCachedNetworkImage(
|
||||
// imgWidth: maxWidth,
|
||||
// imageUrl: showZgtAnnotate ?? sateData.data.value!.zgtAnswer,
|
||||
// (_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
|
||||
// ),
|
||||
child: showZgtAnnotate != null
|
||||
? $TheCachedNetworkImage(
|
||||
imgWidth: maxWidth,
|
||||
imageUrl: showZgtAnnotate,
|
||||
(_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth))
|
||||
: null,
|
||||
),
|
||||
RepaintBoundary(
|
||||
key: logic.pictureOverviewKey,
|
||||
child: CustomPaint(
|
||||
// isComplex: true,
|
||||
size: Size(maxWidth, actualHeight),
|
||||
foregroundPainter: DrawingPainter(ctrl: vnHandWritings),
|
||||
// child: $TheCachedNetworkImage(
|
||||
// imgWidth: maxWidth,
|
||||
// imageUrl: showZgtAnnotate ?? sateData.data.value!.zgtAnswer,
|
||||
// (_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth),
|
||||
// ),
|
||||
child: showZgtAnnotate != null
|
||||
? $TheCachedNetworkImage(
|
||||
imgWidth: maxWidth,
|
||||
imageUrl: showZgtAnnotate,
|
||||
(_, imageProvider) => Image(image: imageProvider, fit: BoxFit.fitWidth))
|
||||
: null,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
|
|||
|
||||
StreamSubscription<TestQuestionsImageInfo?>? imageScaleZoomStream;
|
||||
|
||||
late final zoomWidget.TransformationController zoomController;
|
||||
zoomWidget.TransformationController? zoomController;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
|
|
@ -105,15 +105,6 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
|
|||
// 试题数据
|
||||
_dataListen = state.data.listen((e) {
|
||||
if (e == null) return;
|
||||
var zoomState = zoomLogic.zoomState;
|
||||
|
||||
final currentTemplateId = zoomState.zoomFile.value?.templateId; // 获取旧题号ID
|
||||
if (currentTemplateId != null && currentTemplateId != e.templateId) {
|
||||
// zoom 题号判断是否有变 有变就需要清空 zoom文件
|
||||
zoomState.initScale.value = null;
|
||||
zoomState.zoomFile.value!.clearZoomFile(e.templateId);
|
||||
zoomState.zoomFile.update((_) {}); // 更新的是对象 需要执行此回调
|
||||
}
|
||||
if (state.favorite.value != e.isFav) state.favorite.value = !state.favorite.value;
|
||||
});
|
||||
|
||||
|
|
@ -147,7 +138,7 @@ class HomeworkReviewLogic extends GetxController with RequestToolMixin, EventBus
|
|||
|
||||
@override
|
||||
void onClose() {
|
||||
zoomController.dispose();
|
||||
zoomController?.dispose();
|
||||
// SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.top]); // 屏幕刘海
|
||||
eventCancel();
|
||||
_dataListen.cancel();
|
||||
|
|
|
|||
|
|
@ -17,47 +17,44 @@ class ZoomLogic extends GetxController {
|
|||
);
|
||||
|
||||
var oldTemplateId;
|
||||
late StreamSubscription _streamHomework;
|
||||
late StreamSubscription _streamZoomState;
|
||||
StreamSubscription? _streamHomework;
|
||||
StreamSubscription? _streamZoomState;
|
||||
|
||||
StreamSubscription<double?>? initScaleStream;
|
||||
|
||||
Map<int, ZoomFileModel> imageSizeMap = {};
|
||||
|
||||
HomeworkReviewLogic get homeworkReviewLogic => Get.find<HomeworkReviewLogic>();
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
oldTemplateId = zoomState.zoomFile.value?.templateId;
|
||||
|
||||
/// 根据第一次加载的试题题号 分析试题图片所占的宽高
|
||||
_streamZoomState = zoomState.zoomFile.listen((e) {
|
||||
var templateId = e?.templateId;
|
||||
if (templateId == null) return;
|
||||
|
||||
var homeworkData = Get.find<HomeworkReviewLogic>().state.data.value;
|
||||
var zgtAnswer = homeworkData?.zgtAnswer;
|
||||
if (zgtAnswer == null) return;
|
||||
if (oldTemplateId == templateId) return;
|
||||
|
||||
// getNetworkImageDimensions(zgtAnswer);
|
||||
|
||||
// 第三方库网络图,图片会被解码并缓存于内存
|
||||
oldTemplateId = templateId;
|
||||
CachedNetworkImageProvider(zgtAnswer).getImageSize().then((s) {
|
||||
// 提取宽度和高度
|
||||
if (s == null) return;
|
||||
var oldVal = zoomState.zoomFile.value!;
|
||||
oldVal.fileWidth = s.width;
|
||||
oldVal.fileHeight = s.height;
|
||||
zoomState.zoomFile.value = ZoomFileModel.fromJson(oldVal.toJson());
|
||||
});
|
||||
});
|
||||
_streamZoomState = zoomState.zoomFile.listen((e) => refreshZoomFile(e));
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((e) {
|
||||
/// 试题加载后更新尺寸对象(只要题号没有变就不更新尺寸对象)
|
||||
_streamHomework = Get.find<HomeworkReviewLogic>().state.data.listen((e) {
|
||||
_streamHomework = homeworkReviewLogic.state.data.listen((e) {
|
||||
print("HOMEWORKSTATE 变化了");
|
||||
if (e == null || zoomState.zoomFile.value == null) return;
|
||||
zoomState.zoomFile.value!.templateId = e.templateId;
|
||||
print("666666 ${e.templateId}");
|
||||
zoomState.zoomFile.value = ZoomFileModel.fromJson(zoomState.zoomFile.value!.toJson());
|
||||
var zoomFile = zoomState.zoomFile.value;
|
||||
if (e == null || zoomFile == null) return;
|
||||
|
||||
if (zoomFile.templateId != e.templateId) {
|
||||
// zoom 题号判断是否有变 有变就需要清空 zoom文件
|
||||
zoomState.initScale.value = null;
|
||||
zoomFile = ZoomFileModel(
|
||||
templateId: e.templateId,
|
||||
viewWidth: zoomFile.viewWidth,
|
||||
viewHeight: zoomFile.viewHeight,
|
||||
);
|
||||
|
||||
/// 重置缩放和位置
|
||||
_resetZoom();
|
||||
}
|
||||
|
||||
/// 更新尺寸对象
|
||||
refreshZoomFile(zoomFile);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -69,11 +66,51 @@ class ZoomLogic extends GetxController {
|
|||
|
||||
@override
|
||||
void onClose() {
|
||||
_streamHomework.cancel();
|
||||
_streamZoomState.cancel();
|
||||
_streamHomework?.cancel();
|
||||
_streamZoomState?.cancel();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
void refreshZoomFile(ZoomFileModel? e) {
|
||||
var templateId = e?.templateId;
|
||||
if (templateId == null) return;
|
||||
|
||||
var homeworkData = homeworkReviewLogic.state.data.value;
|
||||
var zgtAnswer = homeworkData?.zgtAnswer;
|
||||
if (zgtAnswer == null || oldTemplateId == templateId) return;
|
||||
|
||||
// getNetworkImageDimensions(zgtAnswer);
|
||||
|
||||
oldTemplateId = templateId;
|
||||
final fileSize = imageSizeMap[templateId];
|
||||
print("fileSize: ${fileSize?.toJson()}");
|
||||
if (fileSize != null) {
|
||||
zoomState.zoomFile.value = fileSize;
|
||||
return;
|
||||
}
|
||||
|
||||
/// 第三方库网络图,图片会被解码并缓存于内存
|
||||
CachedNetworkImageProvider(zgtAnswer).getImageSize().then((s) {
|
||||
if (s == null) return;
|
||||
|
||||
// 提取宽度和高度
|
||||
var oldZoomFile = zoomState.zoomFile.value!;
|
||||
|
||||
/// 重置部分数据 避免数据被覆盖
|
||||
var newZoomFile = ZoomFileModel(
|
||||
templateId: templateId,
|
||||
viewWidth: oldZoomFile.viewWidth,
|
||||
viewHeight: oldZoomFile.viewHeight,
|
||||
fileWidth: s.width,
|
||||
fileHeight: s.height,
|
||||
);
|
||||
|
||||
/// 初始化zoom文件
|
||||
imageSizeMap[templateId] = newZoomFile;
|
||||
zoomState.zoomFile.value = newZoomFile;
|
||||
});
|
||||
}
|
||||
|
||||
// 缩放组件 ==> 缩放监听
|
||||
void onScaleUpdate(double zoom) async {
|
||||
print("缩放比例:$zoom");
|
||||
|
|
@ -86,12 +123,34 @@ class ZoomLogic extends GetxController {
|
|||
// 缩放组件 ==> 位置更新
|
||||
void onPanUpPosition(Offset val) async {
|
||||
// 手指在移动 非物体移动的位置
|
||||
var state = Get.find<HomeworkReviewLogic>().state;
|
||||
var state = homeworkReviewLogic.state;
|
||||
print('**************** 正在移动位置 YYY:${val.dy}');
|
||||
print('**************** 正在移动位置 XXX:${val.dx}');
|
||||
state.zoomOffset = val;
|
||||
state.slide.value = val.dy.abs().toInt().toDouble();
|
||||
}
|
||||
|
||||
// 重置缩放和位置
|
||||
void _resetZoom() {
|
||||
// 重置缩放状态
|
||||
zoomState.initScale.value = 1.0;
|
||||
|
||||
// 重置位置偏移
|
||||
homeworkReviewLogic.state.zoomOffset = Offset.zero;
|
||||
homeworkReviewLogic.state.slide.value = 0.0;
|
||||
|
||||
// final zoomFile = zoomState.zoomFile.value;
|
||||
// final double imageOffsetY = (zoomFile?.imageHeightOffsetStart ?? 0).toDouble();
|
||||
// final controller = homeworkReviewLogic.zoomController;
|
||||
// if (controller != null) {
|
||||
// // 以单位矩阵为基,设置垂直平移到居中位置
|
||||
// final Matrix4 centered = Matrix4.identity()..setTranslationRaw(0, imageOffsetY > 0 ? imageOffsetY : 0, 0);
|
||||
|
||||
// controller.value = centered;
|
||||
// }
|
||||
|
||||
print('缩放已重置');
|
||||
}
|
||||
}
|
||||
|
||||
class HomeworkReviewZoomBinding extends Bindings {
|
||||
|
|
@ -143,18 +202,19 @@ class ZoomFileModel extends Object {
|
|||
this.imageHeightOffsetend,
|
||||
this.pixelRatio,
|
||||
}) {
|
||||
/// 初始化尺寸信息
|
||||
// 图片已视图宽为基准,高度自适应可滑动 图片的实际宽高都需要乘此基准值.
|
||||
if (fileHeight == null || fileWidth == null) return;
|
||||
if (fileHeight != null && fileWidth != null) {
|
||||
scaleRatio = viewWidth / fileWidth!;
|
||||
actualWidth = fileWidth! * scaleRatio;
|
||||
actualHeight = fileHeight! * scaleRatio;
|
||||
|
||||
scaleRatio = viewWidth / fileWidth!;
|
||||
actualWidth = fileWidth! * scaleRatio;
|
||||
actualHeight = fileHeight! * scaleRatio;
|
||||
pixelRatio = fileWidth! / viewWidth; // 图片在此设备的像素比例
|
||||
|
||||
pixelRatio = fileWidth! / viewWidth; // 图片在此设备的像素比例
|
||||
|
||||
remainingHeight = viewHeight - actualHeight!;
|
||||
imageHeightOffsetStart = remainingHeight! / 2;
|
||||
imageHeightOffsetend = imageHeightOffsetStart! + actualHeight!;
|
||||
remainingHeight = viewHeight - actualHeight!;
|
||||
imageHeightOffsetStart = remainingHeight! / 2;
|
||||
imageHeightOffsetend = imageHeightOffsetStart! + actualHeight!;
|
||||
}
|
||||
}
|
||||
|
||||
factory ZoomFileModel.fromJson(Map<String, dynamic> srcJson) => _$ZoomFileModelFromJson(srcJson);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ dependencies:
|
|||
# http请求插件
|
||||
dio: ^5.4.2+1
|
||||
# 网络缓存图片
|
||||
cached_network_image: ^3.2.1
|
||||
cached_network_image: ^3.4.1
|
||||
# 上拉加载和下拉刷新的组件
|
||||
flutter_easyrefresh: ^2.2.2
|
||||
photo_view: ^0.15.0
|
||||
|
|
@ -94,7 +94,7 @@ dependencies:
|
|||
flutter_spinkit: ^5.2.1
|
||||
event_bus: ^2.0.0
|
||||
path_provider: ^2.1.3
|
||||
uuid: ^3.0.7
|
||||
|
||||
flutter_echarts: ^2.4.0
|
||||
# 饼图
|
||||
flutter_echart: ^2.0.0
|
||||
|
|
@ -112,6 +112,7 @@ dependencies:
|
|||
|
||||
dependency_overrides:
|
||||
archive: ^4.0.2
|
||||
uuid: ^4.4.2
|
||||
# meta: ^1.15.0
|
||||
|
||||
dev_dependencies:
|
||||
|
|
|
|||
Loading…
Reference in New Issue