【问题标题】:Surface development: Translate/Rotate/Scale items without ScatterView表面开发:没有 ScatterView 的平移/旋转/缩放项目
【发布时间】:2010-12-24 18:14:24
【问题描述】:

是否可以在没有 ScatterView 的情况下转换/旋转/缩放项目?我想操作可以位于其他元素之上的项目,例如按钮、列表或应该是静态的自定义控件。当我将它们添加到 ScatterView 时,它们都变成了 ScatterViewItems,这不是我们想要的效果。

【问题讨论】:

    标签: wpf rotation scale pixelsense translate-animation


    【解决方案1】:

    扩展一点马克的回答...

    是的,您可以使用操作和惯性 API 来完成此操作,请参阅此 overview page

    不久前,我创建了自己的非常基本的 scatterview 控件,它基本上完成了 scatterview 的功能,但有以下限制:

    • 只有一个孩子,所以它更像是一个边框
    • 没有像 SV 那样的子项的默认视觉外观或特殊行为

    我在开发这个时遇到的一个问题是你必须让你的容器控件占据一个比实际子控件更大的区域。否则,当手指在被操纵对象之外时,您将无法可靠地捕获接触事件。我所做的是让我的容器全屏(1024x768)并且是透明的,它适用于我的情况。

    对于操作本身,您使用Affine2DManipulationProcessor class 的实例。此类要求您开始操作,然后它将不断为您提供 delta 事件 (Affine2DManipulationDelta)。

    如果您希望您的操作在用户松开手指后具有更真实的行为,您将使用Affine2DInertiaProcessor class,它的工作方式与操作处理器类似。基本设置是,一旦操作处理器完成(用户松开手指),您就告诉惯性处理器启动。

    让我们看一些代码:) 这是我在容器类中的设置方法:

    private void InitializeManipulationInertiaProcessors()
    {
        manipulationProcessor = new Affine2DManipulationProcessor(
          Affine2DManipulations.TranslateY | Affine2DManipulations.TranslateX |
          Affine2DManipulations.Rotate | Affine2DManipulations.Scale, 
          this);
        manipulationProcessor.Affine2DManipulationCompleted += new EventHandler<Affine2DOperationCompletedEventArgs>(processor_Affine2DManipulationCompleted);
        manipulationProcessor.Affine2DManipulationDelta += new EventHandler<Affine2DOperationDeltaEventArgs>(processor_Affine2DManipulationDelta);
        inertiaProcessor = new Affine2DInertiaProcessor();
        inertiaProcessor.Affine2DInertiaDelta += new EventHandler<Affine2DOperationDeltaEventArgs>(processor_Affine2DManipulationDelta);
    }
    

    首先,我在容器类中捕获 ContactDown:

    protected override void OnContactDown(ContactEventArgs e)
    {
        base.OnContactDown(e);
        e.Contact.Capture(this);
        // Tell the manipulation processor what contact to track and it will 
        // start sending manipulation delta events based on user motion.
        manipulationProcessor.BeginTrack(e.Contact);
        e.Handled = true;
    }
    

    就是这样,现在坐下来让操纵处理器完成它的工作。每当它有新数据时,它都会引发 delta 事件(在用户移动手指时发生数次/秒)。请注意,作为处理器的消费者,您可以对这些值执行某些操作。它只会告诉您诸如“用户已应用 X 度旋转”或“用户移动手指 X、Y 像素”之类的信息。然后,您通常所做的是将这些值转发给 rendertransforms,以实际向用户展示发生了什么。

    在我的例子中,我的子对象具有三个硬编码的渲染转换:“翻译”、“旋转”和“缩放”,我使用来自处理器的值进行更新。我还做了一些边界检查,以确保对象没有移出表面或缩放过大或过小:

    /// <summary>
    /// This is called whenever the manipulator or the inertia processor has calculated a new position
    /// </summary>
    /// <param name="sender">The processor who caused the change</param>
    /// <param name="e">Event arguments containing the calculations</param>
    void processor_Affine2DManipulationDelta(object sender, Affine2DOperationDeltaEventArgs e)
    {            
        var x = translate.X + e.Delta.X;
        var y = translate.Y + e.Delta.Y;
        if (sender is Affine2DManipulationProcessor)
        {
            // Make sure we don't move outside the screen
            // Inertia processor does this automatically so only adjust if sender is manipulation processor
            y = Math.Max(0, Math.Min(y, this.ActualHeight - box.RenderSize.Height));
            x = Math.Max(0, Math.Min(x, this.ActualWidth - box.RenderSize.Width));
        }
        translate.X = x;
        translate.Y = y;
        rotate.Angle += e.RotationDelta;
        var newScale = scale.ScaleX * e.ScaleDelta;
        Console.WriteLine("Scale delta: " + e.ScaleDelta + " Rotate delta: " + e.RotationDelta);
        newScale = Math.Max(0.3, Math.Min(newScale, 3));
        scale.ScaleY = scale.ScaleX = newScale;
    }
    

    这里需要注意的一点是,操作和惯性处理器都对增量事件使用相同的回调

    最后一块拼图是当用户松开手指并且我想启动惯性处理器时:

    /// <summary>
    /// This is called when the manipulator has completed (i.e. user released contacts) and we let inertia take over movement to make a natural slow down
    /// </summary>
    void processor_Affine2DManipulationCompleted(object sender, Affine2DOperationCompletedEventArgs e)
    {
        inertiaProcessor.InitialOrigin = e.ManipulationOrigin;
    
        // Set the deceleration rates. Smaller number means less friction (i.e. longer time before it stops)
        inertiaProcessor.DesiredAngularDeceleration = .0010;
        inertiaProcessor.DesiredDeceleration = .0010;
        inertiaProcessor.DesiredExpansionDeceleration = .0010;
        inertiaProcessor.Bounds = new Thickness(0, 0, this.ActualWidth, this.ActualHeight);
        inertiaProcessor.ElasticMargin = new Thickness(20);
    
        // Set the initial values.
        inertiaProcessor.InitialVelocity = e.Velocity;
        inertiaProcessor.InitialExpansionVelocity = e.ExpansionVelocity;
        inertiaProcessor.InitialAngularVelocity = e.AngularVelocity;
    
        // Start the inertia.
        inertiaProcessor.Begin();
    }
    

    【讨论】:

      【解决方案2】:

      是的,您可以使用 API 附带的 ManipulationProcessor

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-11-28
        • 1970-01-01
        • 1970-01-01
        • 2010-10-09
        • 2015-02-27
        • 2019-02-05
        • 1970-01-01
        相关资源
        最近更新 更多