C# UWP:基于ScrollViewer计算变换

C# UWP:基于ScrollViewer计算变换,c#,xaml,windows-10,uwp,win2d,C#,Xaml,Windows 10,Uwp,Win2d,我有一个windows通用应用程序,其中我正在使用DirectX渲染场景。我想使用Scrollviewer,因此我在Scrollviewer后面渲染我的场景,并希望基于Scrollviewer计算场景变换。到目前为止,它运行良好,尤其是在翻译和滚动方面。但当我放大时,场景会在两种特殊情况下跳跃: 场景有足够的空间,并且居中,现在需要滚动 相反的方向 我或多或少使用了以下代码: float zoom = scrollViewer.ZoomFactor; float inverseZoom = 1

我有一个windows通用应用程序,其中我正在使用DirectX渲染场景。我想使用Scrollviewer,因此我在Scrollviewer后面渲染我的场景,并希望基于Scrollviewer计算场景变换。到目前为止,它运行良好,尤其是在翻译和滚动方面。但当我放大时,场景会在两种特殊情况下跳跃:

  • 场景有足够的空间,并且居中,现在需要滚动
  • 相反的方向
  • 我或多或少使用了以下代码:

    float zoom = scrollViewer.ZoomFactor;
    
    float inverseZoom = 1f / scrollViewer.ZoomFactor;
    
    float scaledContentW = Document.Size.X * scrollViewer.ZoomFactor;
    float scaledContentH = Document.Size.Y * scrollViewer.ZoomFactor;
    
    float translateX;
    float translateY;
    
    if (scaledContentW < scrollViewer.ViewportWidth)
    {
        translateX = ((float)scrollViewer.ViewportWidth * inverseZoom - Document.Size.X) * 0.5f;
    }
    else
    {
        translateX = -inverseZoom * (float)scrollViewer.HorizontalOffset;
    }
    
    if (scaledContentH < scrollViewer.ViewportHeight)
    {
        translateY = ((float)scrollViewer.ViewportHeight * inverseZoom - Document.Size.Y) * 0.5f;
    }
    else
    {
        translateY = -inverseZoom * (float)scrollViewer.VerticalOffset;
    }
    
    float visibleX = inverseZoom * (float)scrollViewer.HorizontalOffset;
    float visibleY = inverseZoom * (float)scrollViewer.VerticalOffset; ;
    
    float visibleW = Math.Min(Document.Size.X, inverseZoom * (float)scrollViewer.ViewportWidth);
    float visibleH = Math.Min(Document.Size.Y, inverseZoom * (float)scrollViewer.ViewportHeight);
    
    Rect2 visibleRect = new Rect2(visibleX, visibleY, visibleW, visibleH);
    
    transform =
        Matrix3x2.CreateTranslation(
            translateX,
            translateY) *
        Matrix3x2.CreateScale(zoom);
    
    float zoom=scrollViewer.ZoomFactor;
    float inverseZoom=1f/scrollViewer.ZoomFactor;
    float scaledContentW=Document.Size.X*scrollViewer.ZoomFactor;
    float scaledContentH=Document.Size.Y*scrollViewer.ZoomFactor;
    浮动translateX;
    浮动平移;
    if(scaledContentW
    您可以在此处获得一个示例:

    为了确保我的眼睛没有被打断,我在放大,并将平移和缩放值写入了一个文件。你可以在这里看到:

    各列的含义如下:

    第1列:变换矩阵的计算缩放值(M11)=ScrollViewer.ZoomFactor 第2列:矩阵的计算x偏移量(见上文) 第3列:矩阵*向量(500500)结果的x值,此处:Column 1*500+Column 2

    你看,矩阵值看起来不错,但是当应用转换时,你会在几毫秒内得到向右的小跳跃。一种理论是,由于滚动条变得可见,视口可能会发生变化。但事实并非如此。我还在这里尝试了固定值,使滚动条可见,甚至为scrollviewer创建了一个完全没有滚动条的自定义模板


    顺便说一句:这是一篇交叉文章,我在这里也问了一个问题:

    你看到了这种行为,因为当你缩放的大小大于ScrollViewer的大小时,缩放的中心点会移动。要解决此问题,只需订阅
    ScrollViewer
    LayoutUpdate
    事件,并在处理程序中手动将其内容保留在中心

        private void ScrollViewer_LayoutUpdated(object sender, object e)
        {
            this.ScrollViewer.ChangeView(this.ScrollViewer.ScrollableWidth / 2, this.ScrollViewer.ScrollableHeight / 2, this.ScrollViewer.ZoomFactor, true);
        }
    
    这将修复Win2D中两个抽屉的
    矩形
    s上的跳跃运动


    更新 为了证明我的观点,这种跳跃行为最有可能是由于内容大小超过滚动查看器的大小时,出现异常的translate x和/或y值变化。因此,我编写了一些代码,将这些值记录在屏幕上,如下所示-

    ...
    this.Test1.Text += ((float)translateX).ToString() + " ";
    this.Test2.Text += ((float)translateY).ToString() + " ";
    
    session.Transform =
        Matrix3x2.CreateTranslation(
            (float)translateX,
            (float)translateY) *
        Matrix3x2.CreateScale((float)zoom);
    

    现在看上图中的数字。我所做的就是试着放大,直到惊险的场景发生。是否查看高亮显示的“平移y”值?它略高于其先前的值,这与下降趋势相反


    因此,要解决此问题,您需要能够跳过这些由
    ScrollViewer

    引起的异常值。使用这种方法,平移不再起作用,您无法缩放到鼠标位置。@SebastianStehle如果要平移,则仅当发生缩放时才执行ChangeView;如果要缩放到鼠标位置,则获取鼠标点并根据鼠标点位置替换ChangeView的前两个参数。我不明白,为什么要手动缩放?我不想实现自定义滚动。我想使用scrollviewer的默认行为,并在它下面转换一个层。除了从“if”切换到“else”时向右(或向上)跳转一次之外,这项功能还可以正常工作,如下所述:我已经解释过了。。。这是ScrollViewer的本质。当内容变得比自身更大时,其中心点将稍微移动。如何解决这个问题?请看我答案的最后一段。你的计算是正确的。否则它甚至不会工作。在sv内放置一个xaml矩形并缩放它。你会看到它也跳了一点。因为中心点并不总是sv的中间。所以你的第二个点几乎是正确的,除了sv的值没有错,只是在一些情况下由于中心点的变化而稍微偏离。