mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
mod: split some pages
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -9,6 +9,11 @@ import 'package:PiliPlus/common/widgets/segment_progress_bar.dart';
|
||||
import 'package:PiliPlus/http/init.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/user.dart';
|
||||
import 'package:PiliPlus/models/common/sponsor_block/action_type.dart';
|
||||
import 'package:PiliPlus/models/common/sponsor_block/post_segment_model.dart';
|
||||
import 'package:PiliPlus/models/common/sponsor_block/segment_model.dart';
|
||||
import 'package:PiliPlus/models/common/sponsor_block/segment_type.dart';
|
||||
import 'package:PiliPlus/models/common/sponsor_block/skip_type.dart';
|
||||
import 'package:PiliPlus/models/video/later.dart';
|
||||
import 'package:PiliPlus/models/video/play/subtitle.dart';
|
||||
import 'package:PiliPlus/models/video_detail_res.dart';
|
||||
@@ -43,114 +48,6 @@ import 'package:material_design_icons_flutter/material_design_icons_flutter.dart
|
||||
import '../../../utils/id_utils.dart';
|
||||
import 'widgets/header_control.dart';
|
||||
|
||||
enum SegmentType {
|
||||
sponsor,
|
||||
selfpromo,
|
||||
interaction,
|
||||
intro,
|
||||
outro,
|
||||
preview,
|
||||
music_offtopic,
|
||||
poi_highlight,
|
||||
filler,
|
||||
exclusive_access
|
||||
}
|
||||
|
||||
extension SegmentTypeExt on SegmentType {
|
||||
/// from https://github.com/hanydd/BilibiliSponsorBlock/blob/master/public/_locales/zh_CN/messages.json
|
||||
String get title => [
|
||||
'赞助广告', //sponsor
|
||||
'无偿/自我推广', //selfpromo
|
||||
'三连/订阅提醒', //interaction
|
||||
'过场/开场动画', //intro
|
||||
'鸣谢/结束画面', //outro
|
||||
'回顾/概要', //preview
|
||||
'音乐:非音乐部分', //music_offtopic
|
||||
'精彩时刻/重点', //poi_highlight
|
||||
'离题闲聊/玩笑', //filler
|
||||
'柔性推广/品牌合作', //exclusive_access
|
||||
][index];
|
||||
|
||||
String get shortTitle => [
|
||||
'赞助广告', //sponsor
|
||||
'推广', //selfpromo
|
||||
'订阅提醒', //interaction
|
||||
'开场', //intro
|
||||
'片尾', //outro
|
||||
'预览', //preview
|
||||
'非音乐', //music_offtopic
|
||||
'精彩时刻', //poi_highlight
|
||||
'闲聊', //filler
|
||||
'品牌合作', //exclusive_access
|
||||
][index];
|
||||
|
||||
String get description => [
|
||||
'付费推广、付费推荐和直接广告。不是自我推广或免费提及他们喜欢的商品/创作者/网站/产品。', //sponsor
|
||||
'类似于 “赞助广告” ,但无报酬或是自我推广。包括有关商品、捐赠的部分或合作者的信息。', //selfpromo
|
||||
'视频中间简短提醒观众来一键三连或关注。 如果片段较长,或是有具体内容,则应分类为自我推广。', //interaction
|
||||
'没有实际内容的间隔片段。可以是暂停、静态帧或重复动画。不适用于包含内容的过场。', //intro
|
||||
'致谢画面或片尾画面。不包含内容的结尾。', //outro
|
||||
'展示此视频或同系列视频将出现的画面集锦,片段中所有内容都将在之后的正片中再次出现。', //preview
|
||||
'仅用于音乐视频。此分类只能用于音乐视频中未包括于其他分类的部分。', //music_offtopic
|
||||
'大部分人都在寻找的空降时间。类似于“封面在12:34”的评论。', //poi_highlight
|
||||
"仅作为填充内容或增添趣味而添加的离题片段,这些内容对理解视频的主要内容并非必需。这不包括提供背景信息或上下文的片段。这是一个非常激进的分类,适用于当你不想看'娱乐性'内容的时候。", //filler
|
||||
'仅用于对整个视频进行标记。适用于展示UP主免费或获得补贴后使用的产品、服务或场地的视频。', //exclusive_access
|
||||
][index];
|
||||
|
||||
Color get color => [
|
||||
Color(0xFF00d400), //sponsor
|
||||
Color(0xFFffff00), //selfpromo
|
||||
Color(0xFFcc00ff), //interaction
|
||||
Color(0xFF00ffff), //intro
|
||||
Color(0xFF0202ed), //outro
|
||||
Color(0xFF008fd6), //preview
|
||||
Color(0xFFff9900), //music_offtopic
|
||||
Color(0xFFff1684), //poi_highlight
|
||||
Color(0xFF7300FF), //filler
|
||||
Color(0xFF008a5c), //exclusive_access
|
||||
][index];
|
||||
}
|
||||
|
||||
enum SkipType { alwaysSkip, skipOnce, skipManually, showOnly, disable }
|
||||
|
||||
extension SkipTypeExt on SkipType {
|
||||
String get title => ['总是跳过', '跳过一次', '手动跳过', '仅显示', '禁用'][index];
|
||||
}
|
||||
|
||||
class SegmentModel {
|
||||
SegmentModel({
|
||||
// ignore: non_constant_identifier_names
|
||||
required this.UUID,
|
||||
required this.segmentType,
|
||||
required this.segment,
|
||||
required this.skipType,
|
||||
this.hasSkipped,
|
||||
});
|
||||
// ignore: non_constant_identifier_names
|
||||
String UUID;
|
||||
SegmentType segmentType;
|
||||
Pair<int, int> segment;
|
||||
SkipType skipType;
|
||||
bool? hasSkipped;
|
||||
}
|
||||
|
||||
class PostSegmentModel {
|
||||
PostSegmentModel({
|
||||
required this.segment,
|
||||
required this.category,
|
||||
required this.actionType,
|
||||
});
|
||||
Pair<int, int> segment;
|
||||
SegmentType category;
|
||||
ActionType actionType;
|
||||
}
|
||||
|
||||
enum ActionType { skip, mute, full, poi }
|
||||
|
||||
extension ActionTypeExt on ActionType {
|
||||
String get title => ['跳过', '静音', '整个视频', '精彩时刻'][index];
|
||||
}
|
||||
|
||||
class VideoDetailController extends GetxController
|
||||
with GetSingleTickerProviderStateMixin {
|
||||
/// 路由传参
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/pages/video/detail/introduction/pay_coins_page.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:expandable/expandable.dart';
|
||||
@@ -846,328 +846,3 @@ class VideoIntroController extends GetxController
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
class PayCoinsPage extends StatefulWidget {
|
||||
const PayCoinsPage({
|
||||
super.key,
|
||||
required this.callback,
|
||||
this.copyright = 1,
|
||||
});
|
||||
|
||||
final Function callback;
|
||||
final int copyright;
|
||||
|
||||
@override
|
||||
State<PayCoinsPage> createState() => _PayCoinsPageState();
|
||||
}
|
||||
|
||||
class _PayCoinsPageState extends State<PayCoinsPage>
|
||||
with TickerProviderStateMixin {
|
||||
bool _isPaying = false;
|
||||
late final _controller = PageController(viewportFraction: 0.30);
|
||||
|
||||
int get _index => _controller.hasClients ? _controller.page?.round() ?? 0 : 0;
|
||||
|
||||
late AnimationController _slide22Controller;
|
||||
late AnimationController _scale22Controller;
|
||||
late AnimationController _coinSlideController;
|
||||
late AnimationController _coinFadeController;
|
||||
late AnimationController _boxAnimController;
|
||||
|
||||
final List _images = [
|
||||
'assets/images/paycoins/ic_thunder_1.png',
|
||||
'assets/images/paycoins/ic_thunder_2.png',
|
||||
'assets/images/paycoins/ic_thunder_3.png',
|
||||
];
|
||||
late int _imageIndex = -1;
|
||||
Timer? _timer;
|
||||
bool get _showThunder => _imageIndex != -1 && _imageIndex != _images.length;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_slide22Controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 50),
|
||||
);
|
||||
_scale22Controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 50),
|
||||
);
|
||||
_coinSlideController = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
);
|
||||
_coinFadeController = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 100),
|
||||
);
|
||||
_boxAnimController = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 50),
|
||||
);
|
||||
_scale();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_timer?.cancel();
|
||||
_slide22Controller.dispose();
|
||||
_scale22Controller.dispose();
|
||||
_coinSlideController.dispose();
|
||||
_coinFadeController.dispose();
|
||||
_boxAnimController.dispose();
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _scale() {
|
||||
_scale22Controller.forward().whenComplete(() {
|
||||
_scale22Controller.reverse();
|
||||
});
|
||||
}
|
||||
|
||||
void _onScroll(int index) {
|
||||
_controller.animateToPage(
|
||||
index,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.ease,
|
||||
);
|
||||
_scale();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
return _buildBody(constraints.maxHeight > constraints.maxWidth);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildBody(isV) => Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Visibility(
|
||||
visible: _showThunder,
|
||||
maintainSize: true,
|
||||
maintainAnimation: true,
|
||||
maintainState: true,
|
||||
child: Image.asset(_images[_showThunder ? _imageIndex : 0]),
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Visibility(
|
||||
visible: !_isPaying && widget.copyright == 1,
|
||||
maintainSize: true,
|
||||
maintainAnimation: true,
|
||||
maintainState: true,
|
||||
child: GestureDetector(
|
||||
onTap: _index == 0
|
||||
? null
|
||||
: () {
|
||||
_onScroll(0);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 12),
|
||||
child: Image.asset(
|
||||
width: 16,
|
||||
height: 28,
|
||||
_index == 0
|
||||
? 'assets/images/paycoins/ic_left_disable.png'
|
||||
: 'assets/images/paycoins/ic_left.png',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 100,
|
||||
child: PageView.builder(
|
||||
itemCount: widget.copyright == 1 ? 2 : 1,
|
||||
controller: _controller,
|
||||
onPageChanged: (index) => setState(() {
|
||||
_scale();
|
||||
}),
|
||||
itemBuilder: (context, index) {
|
||||
return ListenableBuilder(
|
||||
listenable: _controller,
|
||||
builder: (context, child) {
|
||||
double factor = index == 0 ? 1 : 0;
|
||||
if (_controller.position.hasContentDimensions) {
|
||||
factor = 1 - (_controller.page! - index).abs();
|
||||
}
|
||||
return Visibility(
|
||||
visible: !_isPaying || _index == index,
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
height: 70 + (factor * 30),
|
||||
width: 70 + (factor * 30),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
SlideTransition(
|
||||
position: _boxAnimController.drive(
|
||||
Tween(
|
||||
begin: const Offset(0.0, 0.0),
|
||||
end: const Offset(0.0, -0.2),
|
||||
),
|
||||
),
|
||||
child: Image.asset(
|
||||
'assets/images/paycoins/ic_pay_coins_box.png',
|
||||
),
|
||||
),
|
||||
SlideTransition(
|
||||
position: _coinSlideController.drive(
|
||||
Tween(
|
||||
begin: const Offset(0.0, 0.0),
|
||||
end: const Offset(0.0, -2),
|
||||
),
|
||||
),
|
||||
child: FadeTransition(
|
||||
opacity: Tween<double>(
|
||||
begin: 1, end: 0)
|
||||
.animate(_coinFadeController),
|
||||
child: Image.asset(
|
||||
height: 35 + (factor * 15),
|
||||
width: 35 + (factor * 15),
|
||||
index == 0
|
||||
? 'assets/images/paycoins/ic_coins_one.png'
|
||||
: 'assets/images/paycoins/ic_coins_two.png',
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: !_isPaying && widget.copyright == 1,
|
||||
maintainSize: true,
|
||||
maintainAnimation: true,
|
||||
maintainState: true,
|
||||
child: GestureDetector(
|
||||
onTap: _index == 1
|
||||
? null
|
||||
: () {
|
||||
_onScroll(1);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
child: Image.asset(
|
||||
width: 16,
|
||||
height: 28,
|
||||
_index == 1
|
||||
? 'assets/images/paycoins/ic_right_disable.png'
|
||||
: 'assets/images/paycoins/ic_right.png',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPanUpdate: _handlePanUpdate,
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: 140,
|
||||
child: Center(
|
||||
child: GestureDetector(
|
||||
onTap: _onPayCoin,
|
||||
onPanUpdate: (e) => _handlePanUpdate(e, true),
|
||||
child: ScaleTransition(
|
||||
scale: _scale22Controller.drive(
|
||||
Tween(begin: 1, end: 1.2),
|
||||
),
|
||||
child: SlideTransition(
|
||||
position: _slide22Controller.drive(
|
||||
Tween(
|
||||
begin: const Offset(0.0, 0.0),
|
||||
end: const Offset(0.0, -0.2),
|
||||
),
|
||||
),
|
||||
child: SizedBox(
|
||||
width: 100,
|
||||
height: 140,
|
||||
child: Image.asset(
|
||||
_index == 0
|
||||
? 'assets/images/paycoins/ic_22_mario.png'
|
||||
: 'assets/images/paycoins/ic_22_gun_sister.png',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height:
|
||||
(isV ? 50 : 0) + MediaQuery.of(context).padding.bottom),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
void _handlePanUpdate(DragUpdateDetails e, [bool needV = false]) {
|
||||
if (needV && e.delta.dy.abs() > max(2, e.delta.dx.abs())) {
|
||||
if (e.delta.dy < 0) {
|
||||
_onPayCoin();
|
||||
}
|
||||
} else if (widget.copyright == 1 &&
|
||||
e.delta.dx.abs() > max(2, e.delta.dy.abs())) {
|
||||
if (e.delta.dx > 0) {
|
||||
if (_index == 1) {
|
||||
_onScroll(0);
|
||||
setState(() {});
|
||||
}
|
||||
} else {
|
||||
if (_index == 0) {
|
||||
_onScroll(1);
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _onPayCoin() {
|
||||
if (_isPaying) return;
|
||||
setState(() {
|
||||
_isPaying = true;
|
||||
});
|
||||
_slide22Controller.forward().whenComplete(() {
|
||||
_slide22Controller.reverse().whenComplete(() {
|
||||
if (_index == 1) {
|
||||
_timer ??= Timer.periodic(const Duration(milliseconds: 50 ~/ 3), (_) {
|
||||
if (_imageIndex != _images.length) {
|
||||
setState(() {
|
||||
_imageIndex = _imageIndex + 1;
|
||||
});
|
||||
} else {
|
||||
_timer?.cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
_boxAnimController.forward().whenComplete(() {
|
||||
_boxAnimController.reverse();
|
||||
});
|
||||
_coinSlideController.forward().whenComplete(() {
|
||||
_coinFadeController.forward().whenComplete(() {
|
||||
Get.back();
|
||||
widget.callback(_index + 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
330
lib/pages/video/detail/introduction/pay_coins_page.dart
Normal file
330
lib/pages/video/detail/introduction/pay_coins_page.dart
Normal file
@@ -0,0 +1,330 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class PayCoinsPage extends StatefulWidget {
|
||||
const PayCoinsPage({
|
||||
super.key,
|
||||
required this.callback,
|
||||
this.copyright = 1,
|
||||
});
|
||||
|
||||
final Function callback;
|
||||
final int copyright;
|
||||
|
||||
@override
|
||||
State<PayCoinsPage> createState() => _PayCoinsPageState();
|
||||
}
|
||||
|
||||
class _PayCoinsPageState extends State<PayCoinsPage>
|
||||
with TickerProviderStateMixin {
|
||||
bool _isPaying = false;
|
||||
late final _controller = PageController(viewportFraction: 0.30);
|
||||
|
||||
int get _index => _controller.hasClients ? _controller.page?.round() ?? 0 : 0;
|
||||
|
||||
late AnimationController _slide22Controller;
|
||||
late AnimationController _scale22Controller;
|
||||
late AnimationController _coinSlideController;
|
||||
late AnimationController _coinFadeController;
|
||||
late AnimationController _boxAnimController;
|
||||
|
||||
final List _images = [
|
||||
'assets/images/paycoins/ic_thunder_1.png',
|
||||
'assets/images/paycoins/ic_thunder_2.png',
|
||||
'assets/images/paycoins/ic_thunder_3.png',
|
||||
];
|
||||
late int _imageIndex = -1;
|
||||
Timer? _timer;
|
||||
bool get _showThunder => _imageIndex != -1 && _imageIndex != _images.length;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_slide22Controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 50),
|
||||
);
|
||||
_scale22Controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 50),
|
||||
);
|
||||
_coinSlideController = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
);
|
||||
_coinFadeController = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 100),
|
||||
);
|
||||
_boxAnimController = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 50),
|
||||
);
|
||||
_scale();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_timer?.cancel();
|
||||
_slide22Controller.dispose();
|
||||
_scale22Controller.dispose();
|
||||
_coinSlideController.dispose();
|
||||
_coinFadeController.dispose();
|
||||
_boxAnimController.dispose();
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _scale() {
|
||||
_scale22Controller.forward().whenComplete(() {
|
||||
_scale22Controller.reverse();
|
||||
});
|
||||
}
|
||||
|
||||
void _onScroll(int index) {
|
||||
_controller.animateToPage(
|
||||
index,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.ease,
|
||||
);
|
||||
_scale();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
return _buildBody(constraints.maxHeight > constraints.maxWidth);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildBody(isV) => Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Visibility(
|
||||
visible: _showThunder,
|
||||
maintainSize: true,
|
||||
maintainAnimation: true,
|
||||
maintainState: true,
|
||||
child: Image.asset(_images[_showThunder ? _imageIndex : 0]),
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Visibility(
|
||||
visible: !_isPaying && widget.copyright == 1,
|
||||
maintainSize: true,
|
||||
maintainAnimation: true,
|
||||
maintainState: true,
|
||||
child: GestureDetector(
|
||||
onTap: _index == 0
|
||||
? null
|
||||
: () {
|
||||
_onScroll(0);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 12),
|
||||
child: Image.asset(
|
||||
width: 16,
|
||||
height: 28,
|
||||
_index == 0
|
||||
? 'assets/images/paycoins/ic_left_disable.png'
|
||||
: 'assets/images/paycoins/ic_left.png',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 100,
|
||||
child: PageView.builder(
|
||||
itemCount: widget.copyright == 1 ? 2 : 1,
|
||||
controller: _controller,
|
||||
onPageChanged: (index) => setState(() {
|
||||
_scale();
|
||||
}),
|
||||
itemBuilder: (context, index) {
|
||||
return ListenableBuilder(
|
||||
listenable: _controller,
|
||||
builder: (context, child) {
|
||||
double factor = index == 0 ? 1 : 0;
|
||||
if (_controller.position.hasContentDimensions) {
|
||||
factor = 1 - (_controller.page! - index).abs();
|
||||
}
|
||||
return Visibility(
|
||||
visible: !_isPaying || _index == index,
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
height: 70 + (factor * 30),
|
||||
width: 70 + (factor * 30),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
SlideTransition(
|
||||
position: _boxAnimController.drive(
|
||||
Tween(
|
||||
begin: const Offset(0.0, 0.0),
|
||||
end: const Offset(0.0, -0.2),
|
||||
),
|
||||
),
|
||||
child: Image.asset(
|
||||
'assets/images/paycoins/ic_pay_coins_box.png',
|
||||
),
|
||||
),
|
||||
SlideTransition(
|
||||
position: _coinSlideController.drive(
|
||||
Tween(
|
||||
begin: const Offset(0.0, 0.0),
|
||||
end: const Offset(0.0, -2),
|
||||
),
|
||||
),
|
||||
child: FadeTransition(
|
||||
opacity: Tween<double>(
|
||||
begin: 1, end: 0)
|
||||
.animate(_coinFadeController),
|
||||
child: Image.asset(
|
||||
height: 35 + (factor * 15),
|
||||
width: 35 + (factor * 15),
|
||||
index == 0
|
||||
? 'assets/images/paycoins/ic_coins_one.png'
|
||||
: 'assets/images/paycoins/ic_coins_two.png',
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: !_isPaying && widget.copyright == 1,
|
||||
maintainSize: true,
|
||||
maintainAnimation: true,
|
||||
maintainState: true,
|
||||
child: GestureDetector(
|
||||
onTap: _index == 1
|
||||
? null
|
||||
: () {
|
||||
_onScroll(1);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
child: Image.asset(
|
||||
width: 16,
|
||||
height: 28,
|
||||
_index == 1
|
||||
? 'assets/images/paycoins/ic_right_disable.png'
|
||||
: 'assets/images/paycoins/ic_right.png',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPanUpdate: _handlePanUpdate,
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: 140,
|
||||
child: Center(
|
||||
child: GestureDetector(
|
||||
onTap: _onPayCoin,
|
||||
onPanUpdate: (e) => _handlePanUpdate(e, true),
|
||||
child: ScaleTransition(
|
||||
scale: _scale22Controller.drive(
|
||||
Tween(begin: 1, end: 1.2),
|
||||
),
|
||||
child: SlideTransition(
|
||||
position: _slide22Controller.drive(
|
||||
Tween(
|
||||
begin: const Offset(0.0, 0.0),
|
||||
end: const Offset(0.0, -0.2),
|
||||
),
|
||||
),
|
||||
child: SizedBox(
|
||||
width: 100,
|
||||
height: 140,
|
||||
child: Image.asset(
|
||||
_index == 0
|
||||
? 'assets/images/paycoins/ic_22_mario.png'
|
||||
: 'assets/images/paycoins/ic_22_gun_sister.png',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height:
|
||||
(isV ? 50 : 0) + MediaQuery.of(context).padding.bottom),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
void _handlePanUpdate(DragUpdateDetails e, [bool needV = false]) {
|
||||
if (needV && e.delta.dy.abs() > max(2, e.delta.dx.abs())) {
|
||||
if (e.delta.dy < 0) {
|
||||
_onPayCoin();
|
||||
}
|
||||
} else if (widget.copyright == 1 &&
|
||||
e.delta.dx.abs() > max(2, e.delta.dy.abs())) {
|
||||
if (e.delta.dx > 0) {
|
||||
if (_index == 1) {
|
||||
_onScroll(0);
|
||||
setState(() {});
|
||||
}
|
||||
} else {
|
||||
if (_index == 0) {
|
||||
_onScroll(1);
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _onPayCoin() {
|
||||
if (_isPaying) return;
|
||||
setState(() {
|
||||
_isPaying = true;
|
||||
});
|
||||
_slide22Controller.forward().whenComplete(() {
|
||||
_slide22Controller.reverse().whenComplete(() {
|
||||
if (_index == 1) {
|
||||
_timer ??= Timer.periodic(const Duration(milliseconds: 50 ~/ 3), (_) {
|
||||
if (_imageIndex != _images.length) {
|
||||
setState(() {
|
||||
_imageIndex = _imageIndex + 1;
|
||||
});
|
||||
} else {
|
||||
_timer?.cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
_boxAnimController.forward().whenComplete(() {
|
||||
_boxAnimController.reverse();
|
||||
});
|
||||
_coinSlideController.forward().whenComplete(() {
|
||||
_coinFadeController.forward().whenComplete(() {
|
||||
Get.back();
|
||||
widget.callback(_index + 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import 'package:PiliPlus/models/video/reply/emote.dart';
|
||||
import 'package:PiliPlus/models/video/reply/item.dart';
|
||||
import 'package:PiliPlus/pages/emote/index.dart';
|
||||
import 'package:PiliPlus/utils/feed_back.dart';
|
||||
import 'package:get/get_rx/src/rx_workers/utils/debouncer.dart';
|
||||
|
||||
import 'toolbar_icon_button.dart';
|
||||
|
||||
@@ -38,7 +39,8 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
|
||||
final GlobalKey _formKey = GlobalKey<FormState>();
|
||||
late double emoteHeight = 0.0;
|
||||
double keyboardHeight = 0.0; // 键盘高度
|
||||
final _debouncer = Debouncer(milliseconds: 200); // 设置延迟时间
|
||||
final _debouncer =
|
||||
Debouncer(delay: const Duration(milliseconds: 200)); // 设置延迟时间
|
||||
String toolbarType = 'input';
|
||||
bool _enablePublish = false;
|
||||
final _publishStream = StreamController<bool>();
|
||||
@@ -121,7 +123,7 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
|
||||
// 键盘高度
|
||||
final viewInsets = EdgeInsets.fromViewPadding(
|
||||
View.of(context).viewInsets, View.of(context).devicePixelRatio);
|
||||
_debouncer.run(() {
|
||||
_debouncer(() {
|
||||
if (!mounted) return;
|
||||
if (keyboardHeight == 0 && emoteHeight == 0) {
|
||||
emoteHeight = keyboardHeight =
|
||||
@@ -275,22 +277,3 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
typedef DebounceCallback = void Function();
|
||||
|
||||
class Debouncer {
|
||||
DebounceCallback? callback;
|
||||
final int? milliseconds;
|
||||
Timer? _timer;
|
||||
|
||||
Debouncer({this.milliseconds});
|
||||
|
||||
run(DebounceCallback callback) {
|
||||
if (_timer != null) {
|
||||
_timer!.cancel();
|
||||
}
|
||||
_timer = Timer(Duration(milliseconds: milliseconds!), () {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@ import 'dart:math';
|
||||
|
||||
import 'package:PiliPlus/common/widgets/icon_button.dart';
|
||||
import 'package:PiliPlus/http/danmaku.dart';
|
||||
import 'package:PiliPlus/pages/setting/sponsor_block_page.dart'
|
||||
show SlideColorPicker;
|
||||
import 'package:PiliPlus/pages/setting/slide_color_picker.dart';
|
||||
import 'package:PiliPlus/pages/video/detail/reply_new/reply_page.dart'
|
||||
show PanelType;
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
|
||||
Reference in New Issue
Block a user