表面开发:无需ScatterView即可翻译/旋转/缩放项目

时间:2009-12-09 09:12:08

标签: wpf rotation scale pixelsense translate-animation

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

2 个答案:

答案 0 :(得分:4)

对Mark的答案进行了扩展...

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

前段时间我创建了自己的非常基本的散点图控件,基本上执行了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以实际向用户显示发生了什么。

在我的例子中,我的子对象有三个硬编码的rendertransforms:“translate”,“rotate”和“scale”,我用处理器的值更新。我还做了一些边界检查,以确保对象不会移动到表面之外或缩放太大或太小:

/// <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;
}

这里要注意的一点是,操纵和惯性处理器都使用相同的回调来进行delta事件。

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

/// <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();
}

答案 1 :(得分:0)

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