diff --git a/lib/common/widgets/image_viewer/hero.dart b/lib/common/widgets/image_viewer/hero.dart new file mode 100644 index 000000000..227e78def --- /dev/null +++ b/lib/common/widgets/image_viewer/hero.dart @@ -0,0 +1,26 @@ +import 'package:flutter/widgets.dart'; + +Widget fromHero({ + required Object tag, + required Widget child, +}) => Hero( + tag: tag, + createRectTween: createEndRectTween, + child: child, +); + +RectTween createEndRectTween(Rect? begin, Rect? end) { + if (begin != null && end != null) { + final endWidth = end.width; + final endHeight = end.height; + // TODO: use real image rect + final beginRect = Rect.fromLTWH( + begin.left + (begin.width - endWidth) / 2, + begin.top + (begin.height - endHeight) / 2, + endWidth, + endHeight, + ); + return RectTween(begin: beginRect, end: end); + } + return RectTween(begin: begin, end: end); +} diff --git a/lib/pages/article/widgets/html_render.dart b/lib/pages/article/widgets/html_render.dart index 868b05254..b3eb183ea 100644 --- a/lib/pages/article/widgets/html_render.dart +++ b/lib/pages/article/widgets/html_render.dart @@ -1,3 +1,4 @@ +import 'package:PiliPlus/common/widgets/image_viewer/hero.dart'; import 'package:PiliPlus/models/common/image_preview_type.dart'; import 'package:PiliPlus/utils/extension/num_ext.dart'; import 'package:PiliPlus/utils/image_utils.dart'; @@ -54,7 +55,7 @@ Widget htmlRender({ imgList: [SourceModel(url: imgUrl)], quality: 60, ), - child: Hero( + child: fromHero( tag: imgUrl, child: CachedNetworkImage( width: width, diff --git a/lib/pages/article/widgets/opus_content.dart b/lib/pages/article/widgets/opus_content.dart index 69ef55858..825f04ee6 100644 --- a/lib/pages/article/widgets/opus_content.dart +++ b/lib/pages/article/widgets/opus_content.dart @@ -4,6 +4,7 @@ import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart'; import 'package:PiliPlus/common/widgets/image/cached_network_svg_image.dart'; import 'package:PiliPlus/common/widgets/image/custom_grid_view.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; +import 'package:PiliPlus/common/widgets/image_viewer/hero.dart'; import 'package:PiliPlus/http/constants.dart'; import 'package:PiliPlus/models/common/image_preview_type.dart'; import 'package:PiliPlus/models/common/image_type.dart'; @@ -222,26 +223,28 @@ class OpusContent extends StatelessWidget { ? null : width * pic.height! / pic.width!; width ??= maxWidth; + Widget child = CachedNetworkImage( + width: width, + height: height, + memCacheWidth: width.cacheSize(context), + imageUrl: ImageUtils.thumbnailUrl(pic.url!, 60), + fadeInDuration: const Duration(milliseconds: 120), + fadeOutDuration: const Duration(milliseconds: 120), + placeholder: (_, _) => + Image.asset('assets/images/loading.png'), + ); + if (!(pic.isLongPic ?? false)) { + child = fromHero( + tag: pic.url!, + child: child, + ); + } return GestureDetector( onTap: () => PageUtils.imageView( imgList: [SourceModel(url: pic.url!)], quality: 60, ), - child: Center( - child: Hero( - tag: pic.url!, - child: CachedNetworkImage( - width: width, - height: height, - memCacheWidth: width.cacheSize(context), - imageUrl: ImageUtils.thumbnailUrl(pic.url!, 60), - fadeInDuration: const Duration(milliseconds: 120), - fadeOutDuration: const Duration(milliseconds: 120), - placeholder: (_, _) => - Image.asset('assets/images/loading.png'), - ), - ), - ), + child: child, ); } else { return CustomGridView( diff --git a/lib/pages/audio/view.dart b/lib/pages/audio/view.dart index ab5025891..48c7c1ca8 100644 --- a/lib/pages/audio/view.dart +++ b/lib/pages/audio/view.dart @@ -5,6 +5,7 @@ import 'package:PiliPlus/common/widgets/button/icon_button.dart'; import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart'; import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; +import 'package:PiliPlus/common/widgets/image_viewer/hero.dart'; import 'package:PiliPlus/common/widgets/progress_bar/audio_video_progress_bar.dart'; import 'package:PiliPlus/common/widgets/progress_bar/segment_progress_bar.dart'; import 'package:PiliPlus/grpc/bilibili/app/listener/v1.pb.dart'; @@ -893,7 +894,7 @@ class _AudioPageState extends State { onTap: () => PageUtils.imageView( imgList: [SourceModel(url: cover)], ), - child: Hero( + child: fromHero( tag: cover, child: NetworkImgLayer( src: cover, diff --git a/lib/pages/member/widget/user_info_card.dart b/lib/pages/member/widget/user_info_card.dart index b6b0a5e19..91f46d3c4 100644 --- a/lib/pages/member/widget/user_info_card.dart +++ b/lib/pages/member/widget/user_info_card.dart @@ -1,5 +1,6 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/avatars.dart'; +import 'package:PiliPlus/common/widgets/image_viewer/hero.dart'; import 'package:PiliPlus/common/widgets/pendant_avatar.dart'; import 'package:PiliPlus/common/widgets/view_safe_area.dart'; import 'package:PiliPlus/models/common/image_preview_type.dart'; @@ -125,7 +126,7 @@ class UserInfoCard extends StatelessWidget { .http2https; return GestureDetector( onTap: () => PageUtils.imageView(imgList: [SourceModel(url: imgUrl)]), - child: Hero( + child: fromHero( tag: imgUrl, child: CachedNetworkImage( fit: .cover, @@ -454,7 +455,7 @@ class UserInfoCard extends StatelessWidget { ], ); - Widget get _buildAvatar => Hero( + Widget get _buildAvatar => fromHero( tag: card.face ?? '', child: PendantAvatar( avatar: card.face, diff --git a/lib/pages/music/view.dart b/lib/pages/music/view.dart index 03cd99125..72a355e97 100644 --- a/lib/pages/music/view.dart +++ b/lib/pages/music/view.dart @@ -5,6 +5,7 @@ import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/custom_icon.dart'; import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; +import 'package:PiliPlus/common/widgets/image_viewer/hero.dart'; import 'package:PiliPlus/common/widgets/marquee.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/music.dart'; @@ -439,7 +440,7 @@ class _MusicDetailPageState extends CommonDynPageState { onTap: () => PageUtils.imageView( imgList: [SourceModel(url: item.mvCover!)], ), - child: Hero( + child: fromHero( tag: item.mvCover!, child: NetworkImgLayer( src: item.mvCover, diff --git a/lib/pages/video/introduction/pgc/view.dart b/lib/pages/video/introduction/pgc/view.dart index b400cf243..ceb91efe0 100644 --- a/lib/pages/video/introduction/pgc/view.dart +++ b/lib/pages/video/introduction/pgc/view.dart @@ -5,6 +5,7 @@ import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/button/icon_button.dart'; import 'package:PiliPlus/common/widgets/dialog/dialog.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; +import 'package:PiliPlus/common/widgets/image_viewer/hero.dart'; import 'package:PiliPlus/common/widgets/stat/stat.dart'; import 'package:PiliPlus/models/common/image_preview_type.dart'; import 'package:PiliPlus/models/common/image_type.dart'; @@ -147,7 +148,7 @@ class _PgcIntroPageState extends State { onTap: () => PageUtils.imageView( imgList: [SourceModel(url: item.cover!)], ), - child: Hero( + child: fromHero( tag: item.cover!, child: NetworkImgLayer( width: 115, diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index bfa75055e..e47db1603 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -1,10 +1,11 @@ import 'dart:convert'; -import 'dart:math'; +import 'dart:math' as math; import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/widgets/badge.dart'; import 'package:PiliPlus/common/widgets/gesture/tap_gesture_recognizer.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; +import 'package:PiliPlus/common/widgets/image_viewer/hero.dart'; import 'package:PiliPlus/grpc/bilibili/im/interfaces/v1.pb.dart' show EmotionInfo; import 'package:PiliPlus/grpc/bilibili/im/type.pb.dart' show Msg, MsgType; @@ -590,16 +591,24 @@ class ChatItem extends StatelessWidget { Widget msgTypePic_2(Map content) { final url = content['url']; + final imgWidth = (content['width'] as num).toDouble(); + final imgHeight = (content['height'] as num).toDouble(); + final width = math.min(220.0, imgWidth); + final ratio = imgHeight / imgWidth; + Widget child = NetworkImgLayer( + width: width, + height: width * ratio, + src: url, + ); + if (ratio <= StyleString.imgMaxRatio) { + child = fromHero( + tag: url, + child: child, + ); + } return GestureDetector( onTap: () => PageUtils.imageView(imgList: [SourceModel(url: url)]), - child: Hero( - tag: url, - child: NetworkImgLayer( - width: 220, - height: 220 * content['height'] / content['width'], - src: url, - ), - ), + child: child, ); } @@ -750,7 +759,7 @@ class ChatItem extends StatelessWidget { final String? url = content['jump_url']; return LayoutBuilder( builder: (context, constraints) { - final maxWidth = max(400.0, constraints.maxWidth); + final maxWidth = math.max(400.0, constraints.maxWidth); Widget child = ClipRRect( borderRadius: StyleString.mdRadius, child: CachedNetworkImage(