feat: use canvas_danmaku

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2024-12-14 16:00:43 +08:00
parent 4e7cf0a1bd
commit fee1ad56f7
16 changed files with 216 additions and 597 deletions

View File

@@ -1,7 +1,7 @@
import 'package:canvas_danmaku/canvas_danmaku.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:ns_danmaku/ns_danmaku.dart';
import 'package:PiliPalaX/models/danmaku/dm.pb.dart';
import 'package:PiliPalaX/pages/danmaku/index.dart';
import 'package:PiliPalaX/plugin/pl_player/index.dart';
@@ -29,17 +29,8 @@ class _PlDanmakuState extends State<PlDanmaku> {
late PlPlayerController playerController;
late PlDanmakuController _plDanmakuController;
DanmakuController? _controller;
// bool danmuPlayStatus = true;
Box setting = GStorage.setting;
late bool enableShowDanmaku;
// late List blockTypes;
// late double showArea;
// late double opacityVal;
// late double fontSizeVal;
// late double fontSizeFSVal;
// late double danmakuDurationVal;
// late double strokeWidth;
// late int fontWeight;
int latestAddedPosition = -1;
bool? _isFullScreen;
@@ -83,14 +74,6 @@ class _PlDanmakuState extends State<PlDanmaku> {
}
}
});
// blockTypes = playerController.blockTypes;
// showArea = playerController.showArea;
// opacityVal = playerController.opacityVal;
// fontSizeVal = playerController.fontSizeVal;
// fontSizeFSVal = playerController.fontSizeFSVal;
// strokeWidth = playerController.strokeWidth;
// fontWeight = playerController.fontWeight;
// danmakuDurationVal = playerController.danmakuDurationVal;
}
// 播放器状态监听
@@ -121,18 +104,16 @@ class _PlDanmakuState extends State<PlDanmaku> {
playerController.playerStatus.status.value == PlayerStatus.playing &&
currentDanmakuList != null &&
_controller != null) {
Color? defaultColor = playerController.blockTypes.contains(6)
? DmUtils.decimalToColor(16777215)
: null;
Color? defaultColor =
playerController.blockTypes.contains(6) ? Colors.white : null;
_controller!.addItems(currentDanmakuList
.map((e) => DanmakuItem(
e.content,
color: defaultColor ?? DmUtils.decimalToColor(e.color),
time: e.progress,
type: DmUtils.getPosition(e.mode),
))
.toList());
for (DanmakuElem e in currentDanmakuList) {
_controller!.addDanmaku(DanmakuContentItem(
e.content,
color: defaultColor ?? DmUtils.decimalToColor(e.color),
type: DmUtils.getPosition(e.mode),
));
}
}
}
@@ -157,7 +138,7 @@ class _PlDanmakuState extends State<PlDanmaku> {
() => AnimatedOpacity(
opacity: playerController.isOpenDanmu.value ? 1 : 0,
duration: const Duration(milliseconds: 100),
child: DanmakuView(
child: DanmakuScreen(
createdController: (DanmakuController e) async {
playerController.danmakuController = _controller = e;
},
@@ -169,13 +150,10 @@ class _PlDanmakuState extends State<PlDanmaku> {
hideTop: playerController.blockTypes.contains(5),
hideScroll: playerController.blockTypes.contains(2),
hideBottom: playerController.blockTypes.contains(4),
duration: playerController.danmakuDurationVal /
duration: playerController.danmakuDurationVal ~/
playerController.playbackSpeed,
strokeWidth: playerController.strokeWidth,
// initDuration /
// (danmakuSpeedVal * widget.playerController.playbackSpeed),
),
statusChanged: (isPlaying) {},
),
),
);

View File

@@ -4,6 +4,7 @@ import 'package:PiliPalaX/models/live/danmu_info.dart';
import 'package:PiliPalaX/tcp/live.dart';
import 'package:PiliPalaX/utils/danmaku.dart';
import 'package:PiliPalaX/utils/storage.dart';
import 'package:canvas_danmaku/canvas_danmaku.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart';
@@ -11,8 +12,6 @@ import 'package:PiliPalaX/http/constants.dart';
import 'package:PiliPalaX/http/live.dart';
import 'package:PiliPalaX/models/live/room_info.dart';
import 'package:PiliPalaX/plugin/pl_player/index.dart';
import 'package:ns_danmaku/danmaku_controller.dart';
import 'package:ns_danmaku/models/danmaku_item.dart';
import '../../models/live/room_info_h5.dart';
import '../../utils/video_utils.dart';
@@ -164,14 +163,13 @@ class LiveRoomController extends GetxController {
});
Map json = jsonDecode(obj['info'][0][15]['extra']);
if (showDanmaku) {
controller?.addItems([
DanmakuItem(
controller?.addDanmaku(
DanmakuContentItem(
json['content'],
color: DmUtils.decimalToColor(json['color']),
// time: e.progress,
type: DmUtils.getPosition(json['mode']),
)
]);
),
);
WidgetsBinding.instance.addPostFrameCallback(
(_) => scrollToBottom(),
);

View File

@@ -3,6 +3,7 @@ import 'dart:io';
import 'package:PiliPalaX/http/live.dart';
import 'package:PiliPalaX/pages/live_room/widgets/chat.dart';
import 'package:PiliPalaX/utils/utils.dart';
import 'package:canvas_danmaku/canvas_danmaku.dart';
import 'package:floating/floating.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -10,9 +11,6 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:PiliPalaX/common/widgets/network_img_layer.dart';
import 'package:PiliPalaX/plugin/pl_player/index.dart';
import 'package:ns_danmaku/danmaku_controller.dart';
import 'package:ns_danmaku/danmaku_view.dart';
import 'package:ns_danmaku/models/danmaku_option.dart';
import 'package:screen_brightness/screen_brightness.dart';
import '../../utils/storage.dart';
@@ -42,15 +40,6 @@ class _LiveRoomPageState extends State<LiveRoomPage>
late final _node = FocusNode();
late final _ctr = TextEditingController();
// late bool enableShowDanmaku;
// late List blockTypes;
// late double showArea;
// late double opacityVal;
// late double fontSizeVal;
// late double fontSizeFSVal;
// late double danmakuDurationVal;
// late double strokeWidth;
// late int fontWeight;
int latestAddedPosition = -1;
bool? _isFullScreen;
bool? _isPipMode;
@@ -108,14 +97,6 @@ class _LiveRoomPageState extends State<LiveRoomPage>
Future<void> videoSourceInit() async {
_futureBuilder = _liveRoomController.queryLiveInfoH5();
plPlayerController = _liveRoomController.plPlayerController;
// blockTypes = plPlayerController.blockTypes;
// showArea = plPlayerController.showArea;
// opacityVal = plPlayerController.opacityVal;
// fontSizeVal = plPlayerController.fontSizeVal;
// fontSizeFSVal = plPlayerController.fontSizeFSVal;
// strokeWidth = plPlayerController.strokeWidth;
// fontWeight = plPlayerController.fontWeight;
// danmakuDurationVal = plPlayerController.danmakuDurationVal;
}
@override
@@ -158,7 +139,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
() => AnimatedOpacity(
opacity: plPlayerController.isOpenDanmu.value ? 1 : 0,
duration: const Duration(milliseconds: 100),
child: DanmakuView(
child: DanmakuScreen(
createdController: (DanmakuController e) async {
plPlayerController.danmakuController =
_liveRoomController.controller = e;
@@ -172,13 +153,10 @@ class _LiveRoomPageState extends State<LiveRoomPage>
hideTop: plPlayerController.blockTypes.contains(5),
hideScroll: plPlayerController.blockTypes.contains(2),
hideBottom: plPlayerController.blockTypes.contains(4),
duration: plPlayerController.danmakuDurationVal /
duration: plPlayerController.danmakuDurationVal ~/
plPlayerController.playbackSpeed,
strokeWidth: plPlayerController.strokeWidth,
// initDuration /
// (danmakuSpeedVal * widget.playerController.playbackSpeed),
),
statusChanged: (isPlaying) {},
),
),
),
@@ -206,8 +184,6 @@ class _LiveRoomPageState extends State<LiveRoomPage>
child: Image.asset(
'assets/images/live/default_bg.webp',
fit: BoxFit.cover,
// width: Get.width,
// height: Get.height,
),
),
),

View File

@@ -8,7 +8,6 @@ import 'package:PiliPalaX/models/common/theme_type.dart';
import 'package:PiliPalaX/utils/feed_back.dart';
import 'package:PiliPalaX/utils/login.dart';
import 'package:PiliPalaX/utils/storage.dart';
import 'package:webview_flutter/webview_flutter.dart';
import '../../models/common/dynamic_badge_mode.dart';
import '../../models/common/nav_bar_config.dart';
import '../main/index.dart';
@@ -75,14 +74,6 @@ class SettingController extends GetxController {
userInfoCache.put('userInfoCache', null);
localCache.put(LocalCacheKey.accessKey,
{'mid': -1, 'value': '', 'refresh': ''});
try {
final WebViewController controller = WebViewController();
controller.clearCache();
controller.clearLocalStorage();
WebViewCookieManager().clearCookies();
} catch (e) {
debugPrint(e.toString());
}
userLogin.value = false;
if (Get.isRegistered<MainController>()) {
MainController mainController = Get.find<MainController>();

View File

@@ -10,6 +10,7 @@ import 'package:PiliPalaX/http/danmaku.dart';
import 'package:PiliPalaX/http/init.dart';
import 'package:PiliPalaX/models/video/play/subtitle.dart';
import 'package:PiliPalaX/utils/extension.dart';
import 'package:canvas_danmaku/models/danmaku_content_item.dart';
import 'package:dio/dio.dart';
import 'package:floating/floating.dart';
import 'package:flutter/material.dart';
@@ -27,7 +28,6 @@ import 'package:PiliPalaX/utils/storage.dart';
import 'package:PiliPalaX/utils/utils.dart';
import 'package:PiliPalaX/utils/video_utils.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:ns_danmaku/models/danmaku_item.dart';
import '../../../utils/id_utils.dart';
import 'widgets/header_control.dart';
@@ -733,16 +733,13 @@ class VideoDetailController extends GetxController
SmartDialog.showToast('发送成功');
// 发送成功,自动预览该弹幕,避免重新请求
// TODO: 暂停状态下预览弹幕仍会移动与计时可考虑添加到dmSegList或其他方式实现
plPlayerController.danmakuController?.addItems([
DanmakuItem(
msg,
color: Colors.white,
time: plPlayerController
.position.value.inMilliseconds,
type: DanmakuItemType.scroll,
isSend: true,
)
]);
plPlayerController.danmakuController
?.addDanmaku(DanmakuContentItem(
msg,
color: Colors.white,
type: DanmakuItemType.scroll,
selfSend: true,
));
Get.back();
} else {
SmartDialog.showToast('发送失败,错误信息为${res['msg']}');

View File

@@ -349,10 +349,11 @@ class ReplyItem extends StatelessWidget {
onReply?.call();
},
child: Row(children: [
Icon(Icons.reply,
size: 18,
color:
Theme.of(context).colorScheme.outline.withOpacity(0.8)),
Icon(
Icons.reply,
size: 18,
color: Theme.of(context).colorScheme.outline.withOpacity(0.8),
),
const SizedBox(width: 3),
Text(
'回复',
@@ -459,7 +460,7 @@ class ReplyItem extends StatelessWidget {
color: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.8),
.withOpacity(0.85),
height: 1.6),
overflow: TextOverflow.ellipsis,
maxLines: 2,
@@ -468,10 +469,7 @@ class ReplyItem extends StatelessWidget {
TextSpan(
text: '${replies![i].member!.uname}',
style: TextStyle(
color: Theme.of(context)
.colorScheme
.primary
.withOpacity(0.8),
color: Theme.of(context).colorScheme.primary,
),
recognizer: TapGestureRecognizer()
..onTap = () {
@@ -538,17 +536,15 @@ class ReplyItem extends StatelessWidget {
TextSpan(
text: 'UP主等人 ',
style: TextStyle(
color: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.8))),
color: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.85),
)),
TextSpan(
text: replyControl!.entryText!,
style: TextStyle(
color: Theme.of(context)
.colorScheme
.primary
.withOpacity(0.8),
color: Theme.of(context).colorScheme.primary,
),
)
],
@@ -686,7 +682,7 @@ class ReplyItem extends StatelessWidget {
TextSpan(
text: matchStr,
style: TextStyle(
color: Theme.of(context).colorScheme.primary.withOpacity(0.8),
color: Theme.of(context).colorScheme.primary,
),
recognizer: TapGestureRecognizer()
..onTap = () {

View File

@@ -346,10 +346,11 @@ class ReplyItemGrpc extends StatelessWidget {
onReply?.call();
},
child: Row(children: [
Icon(Icons.reply,
size: 18,
color:
Theme.of(context).colorScheme.outline.withOpacity(0.8)),
Icon(
Icons.reply,
size: 18,
color: Theme.of(context).colorScheme.outline.withOpacity(0.8),
),
const SizedBox(width: 3),
Text(
'回复',
@@ -475,7 +476,7 @@ class ReplyItemGrpc extends StatelessWidget {
color: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.8),
.withOpacity(0.85),
height: 1.6),
overflow: TextOverflow.ellipsis,
maxLines: 2,
@@ -484,10 +485,7 @@ class ReplyItemGrpc extends StatelessWidget {
TextSpan(
text: replyItem.replies[i].member.name,
style: TextStyle(
color: Theme.of(context)
.colorScheme
.primary
.withOpacity(0.8),
color: Theme.of(context).colorScheme.primary,
),
recognizer: TapGestureRecognizer()
..onTap = () {
@@ -555,19 +553,18 @@ class ReplyItemGrpc extends StatelessWidget {
children: [
if (replyItem.replyControl.upReply)
TextSpan(
text: 'UP主等人 ',
style: TextStyle(
color: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.8))),
text: 'UP主等人 ',
style: TextStyle(
color: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.85),
),
),
TextSpan(
text: replyItem.replyControl.subReplyEntryText,
style: TextStyle(
color: Theme.of(context)
.colorScheme
.primary
.withOpacity(0.8),
color: Theme.of(context).colorScheme.primary,
),
)
],
@@ -709,7 +706,7 @@ class ReplyItemGrpc extends StatelessWidget {
TextSpan(
text: matchStr,
style: TextStyle(
color: Theme.of(context).colorScheme.primary.withOpacity(0.8),
color: Theme.of(context).colorScheme.primary,
),
recognizer: TapGestureRecognizer()
..onTap = () {

View File

@@ -3,6 +3,7 @@ import 'dart:io';
import 'dart:math';
import 'package:PiliPalaX/utils/id_utils.dart';
import 'package:canvas_danmaku/canvas_danmaku.dart';
import 'package:floating/floating.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -12,7 +13,6 @@ import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:media_kit/media_kit.dart';
import 'package:ns_danmaku/ns_danmaku.dart';
import 'package:PiliPalaX/http/user.dart';
import 'package:PiliPalaX/models/video/play/quality.dart';
import 'package:PiliPalaX/models/video/play/url.dart';
@@ -997,13 +997,6 @@ class _HeaderControlState extends State<HeaderControl> {
danmakuWeight;
widget.controller!.putDanmakuSettings();
setState(() {});
// try {
// final DanmakuOption currentOption =
// danmakuController.option;
// final DanmakuOption updatedOption =
// currentOption.copyWith(strokeWidth: val);
// danmakuController.updateOption(updatedOption);
// } catch (_) {}
},
),
),
@@ -1028,16 +1021,14 @@ class _HeaderControlState extends State<HeaderControl> {
widget.controller?.putDanmakuSettings();
setState(() {});
try {
final DanmakuOption currentOption =
danmakuController.option;
final DanmakuOption updatedOption =
currentOption.copyWith(
hideTop: blockTypes.contains(5),
hideBottom: blockTypes.contains(4),
hideScroll: blockTypes.contains(2),
// 添加或修改其他需要修改的选项属性
danmakuController.updateOption(
danmakuController.option.copyWith(
hideTop: blockTypes.contains(5),
hideBottom: blockTypes.contains(4),
hideScroll: blockTypes.contains(2),
// 添加或修改其他需要修改的选项属性
),
);
danmakuController.updateOption(updatedOption);
} catch (_) {}
},
text: i['label'],
@@ -1061,11 +1052,10 @@ class _HeaderControlState extends State<HeaderControl> {
widget.controller?.putDanmakuSettings();
setState(() {});
try {
final DanmakuOption currentOption =
danmakuController.option;
final DanmakuOption updatedOption =
currentOption.copyWith(area: i['value']);
danmakuController.updateOption(updatedOption);
danmakuController.updateOption(
danmakuController.option
.copyWith(area: i['value']),
);
} catch (_) {}
},
text: i['label'],
@@ -1105,11 +1095,9 @@ class _HeaderControlState extends State<HeaderControl> {
widget.controller?.putDanmakuSettings();
setState(() {});
try {
final DanmakuOption currentOption =
danmakuController.option;
final DanmakuOption updatedOption =
currentOption.copyWith(opacity: val);
danmakuController.updateOption(updatedOption);
danmakuController.updateOption(
danmakuController.option.copyWith(opacity: val),
);
} catch (_) {}
},
),
@@ -1144,11 +1132,10 @@ class _HeaderControlState extends State<HeaderControl> {
widget.controller?.putDanmakuSettings();
setState(() {});
try {
final DanmakuOption currentOption =
danmakuController.option;
final DanmakuOption updatedOption =
currentOption.copyWith(fontWeight: fontWeight);
danmakuController.updateOption(updatedOption);
danmakuController.updateOption(
danmakuController.option
.copyWith(fontWeight: fontWeight),
);
} catch (_) {}
},
),
@@ -1183,11 +1170,10 @@ class _HeaderControlState extends State<HeaderControl> {
widget.controller?.putDanmakuSettings();
setState(() {});
try {
final DanmakuOption currentOption =
danmakuController.option;
final DanmakuOption updatedOption =
currentOption.copyWith(strokeWidth: val);
danmakuController.updateOption(updatedOption);
danmakuController.updateOption(
danmakuController.option
.copyWith(strokeWidth: val),
);
} catch (_) {}
},
),
@@ -1223,13 +1209,11 @@ class _HeaderControlState extends State<HeaderControl> {
setState(() {});
if (widget.controller?.isFullScreen.value == false) {
try {
final DanmakuOption currentOption =
danmakuController.option;
final DanmakuOption updatedOption =
currentOption.copyWith(
fontSize: (15 * fontSizeVal).toDouble(),
danmakuController.updateOption(
danmakuController.option.copyWith(
fontSize: (15 * fontSizeVal).toDouble(),
),
);
danmakuController.updateOption(updatedOption);
} catch (_) {}
}
},
@@ -1266,13 +1250,11 @@ class _HeaderControlState extends State<HeaderControl> {
setState(() {});
if (widget.controller?.isFullScreen.value == true) {
try {
final DanmakuOption currentOption =
danmakuController.option;
final DanmakuOption updatedOption =
currentOption.copyWith(
fontSize: (15 * fontSizeFSVal).toDouble(),
danmakuController.updateOption(
danmakuController.option.copyWith(
fontSize: (15 * fontSizeFSVal).toDouble(),
),
);
danmakuController.updateOption(updatedOption);
} catch (_) {}
}
},
@@ -1380,11 +1362,11 @@ class _HeaderControlState extends State<HeaderControl> {
widget.controller?.putDanmakuSettings();
setState(() {});
try {
final DanmakuOption updatedOption =
danmakuController.option.copyWith(
duration: danmakuDurationVal /
widget.controller!.playbackSpeed);
danmakuController.updateOption(updatedOption);
danmakuController.updateOption(
danmakuController.option.copyWith(
duration: danmakuDurationVal ~/
widget.controller!.playbackSpeed),
);
} catch (_) {}
},
),

View File

@@ -1,96 +0,0 @@
// ignore_for_file: avoid_print
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:PiliPalaX/http/init.dart';
import 'package:PiliPalaX/utils/event_bus.dart';
import 'package:PiliPalaX/utils/id_utils.dart';
import 'package:webview_flutter/webview_flutter.dart';
class WebviewController extends GetxController {
String url = '';
RxString type = ''.obs;
RxString pageTitle = ''.obs;
String uaType = '';
final WebViewController controller = WebViewController();
RxInt loadProgress = 0.obs;
RxBool loadShow = true.obs;
EventBus eventBus = EventBus();
@override
void onInit() {
super.onInit();
url = Get.parameters['url'] ?? '';
type.value = Get.parameters['type'] ?? '';
pageTitle.value = Get.parameters['pageTitle'] ?? '';
uaType = Get.parameters['uaType'] ?? 'mob';
webviewInit(uaType: uaType);
}
webviewInit({String uaType = 'mob'}) {
controller
..setUserAgent(Request().headerUa(type: uaType))
..setJavaScriptMode(JavaScriptMode.unrestricted)
..enableZoom(true)
..setNavigationDelegate(
NavigationDelegate(
// 页面加载
onProgress: (int progress) {
// Update loading bar.
loadProgress.value = progress;
},
onPageStarted: (String url) {
final parseUrl = Uri.parse(url);
if (parseUrl.pathSegments.isEmpty) return;
final String str = parseUrl.pathSegments[0];
final Map matchRes = IdUtils.matchAvorBv(input: str);
final List matchKeys = matchRes.keys.toList();
if (matchKeys.isNotEmpty) {
if (matchKeys.first == 'BV') {
Get.offAndToNamed(
'/searchResult',
parameters: {'keyword': matchRes['BV']},
);
}
}
},
onPageFinished: (String url) async {
if (type.value == 'liveRoom') {
//注入js
controller.runJavaScriptReturningResult('''
document.styleSheets[0].insertRule('div.open-app-btn.bili-btn-warp {display:none;}', 0);
document.styleSheets[0].insertRule('#app__display-area > div.control-panel {display:none;}', 0);
''').then((value) => debugPrint(value.toString()));
} else if (type.value == 'whisper') {
controller.runJavaScriptReturningResult('''
document.querySelector('#internationalHeader').remove();
document.querySelector('#message-navbar').remove();
''').then((value) => debugPrint(value.toString()));
}
pageTitle.value = await controller.getTitle() ?? '';
},
// 加载完成
onUrlChange: (UrlChange urlChange) async {
loadShow.value = false;
// String url = urlChange.url ?? '';
},
onWebResourceError: (WebResourceError error) {},
onNavigationRequest: (NavigationRequest request) {
if (request.url.startsWith('bilibili://')) {
if (request.url.startsWith('bilibili://video/')) {
String str = Uri.parse(request.url).pathSegments[0];
Get.offAndToNamed(
'/searchResult',
parameters: {'keyword': str},
);
}
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
),
)
..loadRequest(Uri.parse(url));
}
}

View File

@@ -1,4 +0,0 @@
library webview;
export './controller.dart';
export './view.dart';

View File

@@ -1,110 +0,0 @@
import 'package:PiliPalaX/utils/utils.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'controller.dart';
import 'package:webview_flutter/webview_flutter.dart';
class WebviewPage extends StatefulWidget {
const WebviewPage({super.key});
@override
State<WebviewPage> createState() => _WebviewPageState();
}
class _WebviewPageState extends State<WebviewPage> {
final WebviewController _webviewController = Get.put(WebviewController());
@override
void dispose() {
Get.delete<WebviewController>();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Obx(
() => Text(_webviewController.pageTitle.value),
),
actions: [
const SizedBox(width: 4),
IconButton(
tooltip: '刷新网页',
onPressed: () {
_webviewController.controller.reload();
},
icon: Icon(Icons.refresh_outlined,
color: Theme.of(context).colorScheme.primary),
),
IconButton(
tooltip: '用外部浏览器打开',
onPressed: () {
Utils.launchURL(_webviewController.url);
},
icon: Icon(Icons.open_in_browser_outlined,
color: Theme.of(context).colorScheme.primary),
),
const SizedBox(width: 12)
],
),
body: Column(
children: [
Obx(
() => AnimatedContainer(
curve: Curves.easeInOut,
duration: const Duration(milliseconds: 350),
height: _webviewController.loadShow.value ? 4 : 0,
child: LinearProgressIndicator(
key: ValueKey(_webviewController.loadProgress),
value: _webviewController.loadProgress / 100,
),
),
),
if (_webviewController.type.value == 'login') ...<Widget>[
Container(
width: double.infinity,
color: Theme.of(context).colorScheme.onInverseSurface,
padding: const EdgeInsets.only(
left: 12, right: 12, top: 6, bottom: 6),
child: const Text('登录成功未自动跳转? 请点击右上角「刷新登录态」'),
),
const SizedBox(height: 4),
Container(
width: double.infinity,
color: Theme.of(context).colorScheme.onInverseSurface,
padding: const EdgeInsets.only(
left: 12, right: 12, top: 6, bottom: 6),
child: const Text(
'如需二维码登录请点击「电脑版」放大左侧二维码截图后官方app或另一设备扫码授权后点击「刷新登录态」'),
),
],
Expanded(
child: SafeArea(
child: WebViewWidget(
controller: _webviewController.controller,
gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{
Factory<VerticalDragGestureRecognizer>(
() => VerticalDragGestureRecognizer(),
),
Factory<PanGestureRecognizer>(
() => PanGestureRecognizer(),
),
Factory<ForcePressGestureRecognizer>(
() => ForcePressGestureRecognizer(),
),
Factory<EagerGestureRecognizer>(
() => EagerGestureRecognizer(),
),
Factory<HorizontalDragGestureRecognizer>(
() => HorizontalDragGestureRecognizer(),
),
}),
),
),
],
));
}
}

View File

@@ -6,6 +6,7 @@ import 'dart:typed_data';
import 'package:PiliPalaX/common/widgets/segment_progress_bar.dart';
import 'package:PiliPalaX/utils/extension.dart';
import 'package:canvas_danmaku/canvas_danmaku.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
@@ -14,7 +15,6 @@ import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:media_kit/media_kit.dart';
import 'package:media_kit_video/media_kit_video.dart';
import 'package:ns_danmaku/ns_danmaku.dart';
import 'package:PiliPalaX/http/video.dart';
import 'package:PiliPalaX/pages/mine/controller.dart';
import 'package:PiliPalaX/plugin/pl_player/index.dart';
@@ -843,9 +843,9 @@ class PlPlayerController {
await _videoPlayerController?.setRate(speed);
try {
DanmakuOption currentOption = danmakuController!.option;
defaultDuration ??= currentOption.duration;
defaultDuration ??= currentOption.duration.toDouble();
DanmakuOption updatedOption =
currentOption.copyWith(duration: defaultDuration! / speed);
currentOption.copyWith(duration: defaultDuration! ~/ speed);
danmakuController!.updateOption(updatedOption);
if (speed == 1.0) {
defaultDuration = null;

View File

@@ -57,7 +57,6 @@ import '../pages/subscription/index.dart';
import '../pages/subscription_detail/index.dart';
import '../pages/video/detail/index.dart';
import '../pages/video/detail/reply_reply/index.dart';
import '../pages/webview/index.dart';
import '../pages/whisper/index.dart';
import '../pages/whisper_detail/index.dart';
import '../utils/storage.dart';
@@ -73,7 +72,6 @@ class Routes {
// 视频详情
CustomGetPage(name: '/video', page: () => const VideoDetailPage()),
//
CustomGetPage(name: '/webview', page: () => const WebviewPage()),
CustomGetPage(name: '/webviewnew', page: () => const WebviewPageNew()),
// 设置
CustomGetPage(name: '/setting', page: () => const SettingPage()),

View File

@@ -1,5 +1,5 @@
import 'package:canvas_danmaku/models/danmaku_content_item.dart';
import 'package:flutter/material.dart';
import 'package:ns_danmaku/ns_danmaku.dart';
class DmUtils {
static Color decimalToColor(int decimalColor) {