Web source: geometry-agnostic viewport==box model — viewport locked to element box, Stretch=Fill, no dead space
This commit is contained in:
+31
-21
@@ -15,20 +15,6 @@ namespace ytLive.Services;
|
||||
|
||||
public sealed class WebView2Manager : IDisposable
|
||||
{
|
||||
// Eliminates the wrapper padding/whitespace between the box and the page:
|
||||
// clears UA margins/paddings, forces html/body to fill the viewport and
|
||||
// anchors content at (0,0) so the rendered frame matches the widget's size.
|
||||
private const string CssResetInjection =
|
||||
"(function(){" +
|
||||
"var s=document.createElement('style');" +
|
||||
"s.textContent='html,body{margin:0!important;padding:0!important;" +
|
||||
"width:100%!important;height:100%!important;overflow:hidden!important;" +
|
||||
"background:transparent!important}';" +
|
||||
"(document.head||document.documentElement).appendChild(s);" +
|
||||
"if(document.documentElement)document.documentElement.style.background='transparent';" +
|
||||
"if(document.body)document.body.style.background='transparent';" +
|
||||
"})();";
|
||||
|
||||
private readonly Panel _hostPanel;
|
||||
private readonly Dispatcher _dispatcher;
|
||||
private readonly Dictionary<string, WebSourceSession> _sessions = new();
|
||||
@@ -54,13 +40,14 @@ public sealed class WebView2Manager : IDisposable
|
||||
{
|
||||
if (_sessions.ContainsKey(source.Id)) return;
|
||||
|
||||
// OBS pattern: the browser viewport is the canvas size (1920×1080),
|
||||
// never the element box. The page renders full at its designed size;
|
||||
// the preview scales the captured frame independently (Stretch=Uniform).
|
||||
// Geometry-agnostic OBS model: the browser viewport IS the element box.
|
||||
// The page renders at exactly Source.Width × Source.Height, so the
|
||||
// captured frame and the selection rect are always the same rectangle —
|
||||
// no fixed render size, no aspect assumptions, no dead space or padding.
|
||||
var webView = new WebView2
|
||||
{
|
||||
Width = 1920,
|
||||
Height = 1080,
|
||||
Width = Math.Max(1, source.Width),
|
||||
Height = Math.Max(1, source.Height),
|
||||
DefaultBackgroundColor = System.Drawing.Color.Transparent,
|
||||
Visibility = System.Windows.Visibility.Visible,
|
||||
};
|
||||
@@ -82,6 +69,22 @@ public sealed class WebView2Manager : IDisposable
|
||||
|
||||
_sessions[source.Id] = session;
|
||||
|
||||
// Interop sync: keep the viewport locked to the element box so the
|
||||
// selection rect always bounds the actual rendered content.
|
||||
source.PropertyChanged += (_, e) =>
|
||||
{
|
||||
if (e.PropertyName is not (nameof(Source.Width) or nameof(Source.Height))) return;
|
||||
if (session.Disposed) return;
|
||||
if (Math.Abs(session.Control.Width - source.Width) > 0.5 ||
|
||||
Math.Abs(session.Control.Height - source.Height) > 0.5)
|
||||
{
|
||||
session.Control.Width = Math.Max(1, source.Width);
|
||||
session.Control.Height = Math.Max(1, source.Height);
|
||||
if (session.Initialized)
|
||||
_ = CaptureFrame(source.Id);
|
||||
}
|
||||
};
|
||||
|
||||
timer.Tick += (_, _) =>
|
||||
{
|
||||
if (!session.Disposed && session.Initialized)
|
||||
@@ -122,11 +125,18 @@ public sealed class WebView2Manager : IDisposable
|
||||
|
||||
var cws = session.Control.CoreWebView2!;
|
||||
|
||||
await cws.AddScriptToExecuteOnDocumentCreatedAsync(CssResetInjection);
|
||||
cws.NavigationStarting += (_, _) =>
|
||||
{
|
||||
_ = cws.ExecuteScriptAsync(
|
||||
"document.documentElement.style.background='transparent';" +
|
||||
"document.body.style.background='transparent';");
|
||||
};
|
||||
|
||||
cws.NavigationCompleted += (_, _) =>
|
||||
{
|
||||
_ = cws.ExecuteScriptAsync(CssResetInjection);
|
||||
_ = cws.ExecuteScriptAsync(
|
||||
"document.documentElement.style.background='transparent';" +
|
||||
"document.body.style.background='transparent';");
|
||||
_ = CaptureFrame(source.Id);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user