英文:
Flutter: How to scale a video to fill screen while keeping aspect ratio?
问题
I am trying to scale (zoom) a video to full screen, so there are no white borders while maintaining the aspect ratio of the video. I want it to overflow, I don't mind that some of the height or width is clipped.
But I struggle to produce code that works across all kind of different devices.
This is the code of which I expect it should have the intended behavior:
double getScale() {
double videoHeight = _controller.value.size.height;
double videoWidth = _controller.value.size.width;
double physicalHeight = window.physicalSize.height;
double physicalWidth = window.physicalSize.width;
double xScaleNeeded = physicalWidth / videoWidth;
double yScaleNeeded = physicalHeight / videoHeight;
double scale=1.0;
// Take the axis that needs the biggest scale increase to fill the screen in that axis
if(xScaleNeeded > yScaleNeeded){
scale = xScaleNeeded;
} else{
scale = yScaleNeeded;
}
return scale;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: _controller.value.isInitialized
? Transform.scale(
scale: getScale(),
child: AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: GestureDetector(onTap: tappedVideo, child: VideoPlayer(_controller)),
),
)
: errorPlayback
? Center(
child: Column(
children: const [
Icon(Icons.error),
Text("Sorry, can't load the video :("),
],
))
: const Center(child: CircularProgressIndicator()),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: playerControls(),
);
}
This code produces:
Nexus 10 emulator: overflow on x and y axis
Nexus 7 emulator: no overflow but white bars on left and right side
Pixel 6 Pro emulator: overflow in both x and y axis
Samsung Galaxy S22 physical device: white bars on left and right side
The video and physical screen sizes are in pixels and seem to be right for the different devices. I use the VideoPlayer package from pub.dev, version 2.6.0
I also tried using ClipRect to cut a bit of the video in the axis that is too big, but I run into a lot of different problems in that approach as well.
All help is welcome thanks
英文:
I am trying to scale (zoom) a video to full screen, so there are no white borders while maintaining the aspect ratio of the video. I want it to overflow, I don't mind that some of the height or width is clipped.
But I struggle to produce code that works across all kind of different devices.
This is the code of which I expect it should have the intended behavior:
double getScale() {
double videoHeight = _controller.value.size.height;
double videoWidth = _controller.value.size.width;
double physicalHeight = window.physicalSize.height;
double physicalWidth = window.physicalSize.width;
double xScaleNeeded = physicalWidth / videoWidth;
double yScaleNeeded = physicalHeight / videoHeight;
double scale=1.0;
// Take the axis that needs the biggest scale increase to fill the screen in that axis
if(xScaleNeeded > yScaleNeeded){
scale = xScaleNeeded;
} else{
scale = yScaleNeeded;
}
return scale;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: _controller.value.isInitialized
? Transform.scale(
scale: getScale(),
child: AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: GestureDetector(onTap: tappedVideo, child: VideoPlayer(_controller)),
),
)
: errorPlayback
? Center(
child: Column(
children: const [
Icon(Icons.error),
Text("Sorry, can't load the video :("),
],
))
: const Center(child: CircularProgressIndicator()),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: playerControls(),
);
}
This code produces:
Nexus 10 emulator: overflow on x and y axis
Nexus 7 emulator: no overflow but white bars on left and right side
Pixel 6 Pro emulator: overflow in both x and y axis
Samsung Galaxy S22 physical device: white bars on left and right side
The video and physical screen sizes are in pixels and seem to be right for the different devices. I use the VideoPlayer package from pub.dev, version 2.6.0
I also tried using ClipRect to cut a bit of the video in the axis that is to big but I run into a lot of different problems in that approach as well.
All help is welcome thanks
答案1
得分: 1
我建议使用ScaleTransition
和包裹在其周围的GestureDetector
来设置。您的小部件中需要这些属性。
late AnimationController _scaleVideoAnimationController;
Animation<double> _scaleVideoAnimation = const AlwaysStoppedAnimation<double>(1.0);
double _lastZoomGestureScale = 1.0;
初始化动画控制器。
void setScale() { // 一旦知道视频和屏幕尺寸,可以调用此函数。
final newTargetScale = screenSize.width /
(videoSize.width * screenSize.height / videoSize.height);
_scaleVideoAnimation =
Tween<double>(begin: 1.0, end: newValue).animate(CurvedAnimation(
parent: _scaleVideoAnimationController,
curve: Curves.easeInOut,
));
}
然后将您的VideoController包裹在GestureController中。
GestureDetector(
onTap: () {}, // 触发播放/暂停
onScaleUpdate: (details) {
_lastZoomGestureScale = details.scale;
},
onScaleEnd: (details) {
if (_lastZoomGestureScale > 1.0) {
setState(() {
// 放大
_scaleVideoAnimationController.forward();
});
} else if (_lastZoomGestureScale < 1.0) {
setState(() {
// 缩小
_scaleVideoAnimationController.reverse();
});
}
_lastZoomGestureScale = 1.0;
},
child: ScaleTransition(
scale: _scaleAnimation,
child: AspectRatio(aspectRatio: 16 / 9, child: <your_videoPlayer>)
)
);
您可以在这里的Github上找到此示例,我在那里使用VLCPlayer实现了类似的功能。不过,该机制对于任何视频播放小部件都应该适用。
英文:
I suggest a setup using a ScaleTransition
with a GestureDetector
wrapped around it. You will need these properties in your widget.
late AnimationController _scaleVideoAnimationController;
Animation<double> _scaleVideoAnimation = const AlwaysStoppedAnimation<double>(1.0);
double _lastZoomGestureScale = 1.0;
Initialize the animation controller.
void setScale() { // You can call this as soon as video and screen dimensions are known.
final newTargetScale = screenSize.width /
(videoSize.width * screenSize.height / videoSize.height);
_scaleVideoAnimation =
Tween<double>(begin: 1.0, end: newValue).animate(CurvedAnimation(
parent: _scaleVideoAnimationController,
curve: Curves.easeInOut,
));
}
And wrap your VideoController inside a GestureController.
GestureDetector(
onTap: () {}, // Trigger play/pause
onScaleUpdate: (details) {
_lastZoomGestureScale = details.scale;
},
onScaleEnd: (details) {
if (_lastZoomGestureScale > 1.0) {
setState(() {
// Zoom in
_scaleVideoAnimationController.forward();
});
} else if (_lastZoomGestureScale < 1.0) {
setState(() {
// Zoom out
_scaleVideoAnimationController.reverse();
});
}
_lastZoomGestureScale = 1.0;
},
child: ScaleTransition(
scale: _scaleAnimation,
child: AspectRatio(aspectRatio: 16 / 9, child: <your_videoPlayer>)
)
);
You can find a sample of this here on Github, where I implemented something similar with a VLCPlayer. The mechanism should work for any video playing widget though.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论