feat! js_interop improvements
This commit is contained in:
@@ -11,14 +11,8 @@ import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_in
|
||||
class ThermionFlutterPlugin {
|
||||
ThermionFlutterPlugin._();
|
||||
|
||||
static Future<ThermionViewer> createViewer(
|
||||
{ThermionFlutterOptions options =
|
||||
const ThermionFlutterOptions()}) async {
|
||||
|
||||
final viewer =
|
||||
await ThermionFlutterPlatform.instance.createViewer(options: options);
|
||||
|
||||
return viewer;
|
||||
static Future<ThermionViewer> createViewer() {
|
||||
return ThermionFlutterPlatform.instance.createViewer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ Future kDefaultResizeCallback(Size size, View view, double pixelRatio) async {
|
||||
}
|
||||
|
||||
class ThermionWidget extends StatefulWidget {
|
||||
|
||||
///
|
||||
/// The viewer whose content will be rendered into this widget.
|
||||
///
|
||||
@@ -43,7 +42,7 @@ class ThermionWidget extends StatefulWidget {
|
||||
|
||||
///
|
||||
/// If true, add an overlay showing the FPS on top of the rendered content.
|
||||
///
|
||||
///
|
||||
final bool showFpsCounter;
|
||||
|
||||
///
|
||||
@@ -67,12 +66,9 @@ class ThermionWidget extends StatefulWidget {
|
||||
class _ThermionWidgetState extends State<ThermionWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Web doesn't support imported textures yet
|
||||
if (kIsWeb) {
|
||||
throw Exception();
|
||||
// return ThermionWidgetWeb(
|
||||
// viewer: widget.viewer,
|
||||
// options: ThermionFlutterPlugin.options as ThermionFlutterWebOptions?);
|
||||
return ThermionWidgetWeb(
|
||||
viewer: widget.viewer, options: const ThermionFlutterWebOptions(importCanvasAsWidget: true));
|
||||
}
|
||||
|
||||
return ThermionTextureWidget(
|
||||
|
||||
@@ -1,37 +1,121 @@
|
||||
import 'dart:js_util';
|
||||
import 'dart:async';
|
||||
import 'dart:ui' as ui;
|
||||
import 'dart:ui_web' as ui_web;
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:thermion_flutter/thermion_flutter.dart';
|
||||
import 'package:thermion_flutter_web/thermion_flutter_web.dart';
|
||||
import 'package:thermion_flutter_web/thermion_flutter_web_options.dart';
|
||||
import 'package:web/web.dart';
|
||||
import 'package:web/web.dart' as web;
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class ThermionWidgetWeb extends StatelessWidget {
|
||||
import 'resize_observer.dart';
|
||||
|
||||
class ThermionWidgetWeb extends StatefulWidget {
|
||||
final ThermionFlutterWebOptions options;
|
||||
final ThermionViewer viewer;
|
||||
|
||||
const ThermionWidgetWeb(
|
||||
{super.key, this.options = const ThermionFlutterWebOptions.empty(), required this.viewer});
|
||||
{super.key,
|
||||
this.options = const ThermionFlutterWebOptions(),
|
||||
required this.viewer});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _ThermionWidgetWebState();
|
||||
}
|
||||
|
||||
class _ThermionWidgetWebState extends State<ThermionWidgetWeb> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_requestFrame();
|
||||
}
|
||||
|
||||
DateTime lastRender = DateTime.now();
|
||||
|
||||
void _requestFrame() async {
|
||||
Pointer? stackPtr;
|
||||
WidgetsBinding.instance.scheduleFrameCallback((d) async {
|
||||
if (stackPtr != null) {
|
||||
stackRestore(stackPtr!);
|
||||
stackPtr = null;
|
||||
}
|
||||
|
||||
var elapsed = DateTime.now().microsecondsSinceEpoch -
|
||||
lastRender.microsecondsSinceEpoch;
|
||||
// if (elapsed > 1667) {
|
||||
lastRender = DateTime.now();
|
||||
if (widget.viewer.rendering) {
|
||||
await FilamentApp.instance!.requestFrame();
|
||||
}
|
||||
// }
|
||||
stackPtr = stackSave();
|
||||
_requestFrame();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// if (_texture == null || _resizing) {
|
||||
// return widget.initial ?? Container(color: Colors.red);
|
||||
// }
|
||||
// return ResizeObserver(
|
||||
// onResized: _resizeTexture,
|
||||
// child: ThermionWidgetWeb(
|
||||
// options: widget.options as ThermionFlutterWebOptions?));
|
||||
|
||||
if (options?.importCanvasAsWidget == true) {
|
||||
return _ImageCopyingWidget();
|
||||
if (widget.options.importCanvasAsWidget) {
|
||||
return _ImageCopyingWidget(viewer: widget.viewer);
|
||||
// return _PlatformView(
|
||||
// viewer: widget.viewer,
|
||||
// );
|
||||
}
|
||||
return Container(color: const Color(0x00000000));
|
||||
}
|
||||
}
|
||||
|
||||
class _PlatformView extends StatefulWidget {
|
||||
final ThermionViewer viewer;
|
||||
|
||||
const _PlatformView({super.key, required this.viewer});
|
||||
@override
|
||||
State<StatefulWidget> createState() => _PlatformViewState();
|
||||
}
|
||||
|
||||
class _PlatformViewState extends State<_PlatformView> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
ui_web.platformViewRegistry.registerViewFactory(
|
||||
'imported-canvas',
|
||||
(int viewId, {Object? params}) {
|
||||
var canvas = web.document.getElementById("thermion_canvas");
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
var renderBox = this.context.findRenderObject() as RenderBox?;
|
||||
|
||||
_resize(Size(0, 0), renderBox!.size);
|
||||
});
|
||||
|
||||
return canvas! as Object;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _resize(Size oldSize, Size newSize) {
|
||||
var width = newSize.width.toInt();
|
||||
var height = newSize.height.toInt();
|
||||
ThermionFlutterWebPlugin.instance
|
||||
.resizeCanvas(newSize.width, newSize.height);
|
||||
widget.viewer.setViewport(width, height);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResizeObserver(
|
||||
onResized: _resize,
|
||||
child: HtmlElementView(
|
||||
viewType: 'imported-canvas',
|
||||
onPlatformViewCreated: (i) {},
|
||||
creationParams: <String, Object?>{
|
||||
'key': 'someValue',
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
class _ImageCopyingWidget extends StatefulWidget {
|
||||
final ThermionViewer viewer;
|
||||
|
||||
const _ImageCopyingWidget({super.key, required this.viewer});
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _ImageCopyingWidgetState();
|
||||
@@ -39,34 +123,82 @@ class _ImageCopyingWidget extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ImageCopyingWidgetState extends State<_ImageCopyingWidget> {
|
||||
final _logger = Logger("_ImageCopyingWidgetState");
|
||||
late final _logger = Logger(this.runtimeType.toString());
|
||||
late web.HTMLCanvasElement canvas;
|
||||
ui.Image? _img;
|
||||
double width = 0;
|
||||
double height = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
capture();
|
||||
canvas =
|
||||
web.document.getElementById("thermion_canvas") as web.HTMLCanvasElement;
|
||||
WidgetsBinding.instance.addPostFrameCallback((t) {
|
||||
_refresh(Duration.zero);
|
||||
});
|
||||
}
|
||||
|
||||
Future capture() async {
|
||||
void _refresh(Duration _) async {
|
||||
try {
|
||||
final ImageBitmap newSource = await promiseToFuture<ImageBitmap>(
|
||||
window.createImageBitmap(
|
||||
document.getElementById("canvas") as HTMLCanvasElement));
|
||||
_img = await ui_web.createImageFromImageBitmap(newSource);
|
||||
setState(() {});
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
capture();
|
||||
});
|
||||
final rb = this.context.findRenderObject() as RenderBox?;
|
||||
|
||||
if (_resizing || rb == null || rb.size.isEmpty) {
|
||||
setState(() {});
|
||||
return;
|
||||
}
|
||||
|
||||
if (canvas.width != rb.size.width || canvas.height != rb.size.height) {
|
||||
ThermionFlutterWebPlugin.instance
|
||||
.resizeCanvas(rb.size.width, rb.size.height);
|
||||
await widget.viewer
|
||||
.setViewport(rb.size.width.ceil(), rb.size.height.ceil())
|
||||
.timeout(Duration(seconds: 1));
|
||||
}
|
||||
|
||||
width = canvas.width * web.window.devicePixelRatio;
|
||||
height = canvas.height * web.window.devicePixelRatio;
|
||||
_img = await ui_web.createImageFromTextureSource(canvas,
|
||||
width: width.ceil(), height: height.ceil(), transferOwnership: true);
|
||||
|
||||
_request++;
|
||||
} catch (err) {
|
||||
_logger.severe(err);
|
||||
} finally {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
setState(() {});
|
||||
});
|
||||
WidgetsBinding.instance.scheduleFrameCallback(_refresh);
|
||||
}
|
||||
}
|
||||
|
||||
int _request = 0;
|
||||
|
||||
bool _resizing = false;
|
||||
Timer? _resizeTimer;
|
||||
|
||||
void _resize(Size oldSize, Size newSize) {
|
||||
_resizeTimer?.cancel();
|
||||
_resizing = true;
|
||||
_resizeTimer = Timer(Duration(milliseconds: 100), () {
|
||||
_resizing = false;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RawImage(image: _img!);
|
||||
if (_img == null) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
return ResizeObserver(
|
||||
onResized: _resize,
|
||||
child: RawImage(
|
||||
key: Key(_request.toString()),
|
||||
width: width,
|
||||
height: height,
|
||||
image: _img!,
|
||||
filterQuality: FilterQuality.high,
|
||||
isAntiAlias: false,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@ class _ViewerWidgetState extends State<ViewerWidget> {
|
||||
}
|
||||
|
||||
void didUpdateWidget(ViewerWidget oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.manipulatorType != widget.manipulatorType) {
|
||||
_setViewportWidget();
|
||||
setState(() {});
|
||||
|
||||
Reference in New Issue
Block a user