Pre-FrameAnimation帧动画:

ColorChangingExample颜色帧变化示例

clipboard.png

实现效果:此方框实现每帧改变一次颜色

  1. Rectangle.Fill填充纯色画刷SolidColorBrush,
  2. 后台代码实现CompositionTarget.Rendering事件,在每帧中更新纯色画刷的初始化的随机颜色。
private readonly Random _rand = new Random();

public SimpleExample()
{
    CompositionTarget.Rendering += UpdateColor;
}

private void UpdateColor(object sender, EventArgs e)
{
    // Set a random color
    animatedBrush.Color = Color.FromRgb((byte) _rand.Next(255),
        (byte) _rand.Next(255),
        (byte) _rand.Next(255));
}

FollowExample一直跟随鼠标的方框

clipboard.png
在Page每帧呈现的方法中获取当前方框的坐标与当前鼠标坐标差量值,设置每帧方框往鼠标点移动一点点距离。形成方框慢慢跟随到鼠标的效果。

  1. 设置字段:最后一次鼠标位置_lastMousePosition及方框需要变化位置_rectangleVelocity值。
  2. 初始化Page时,在静态类CompositionTarget.Rendering事件中附加每帧处理的方法。同时对鼠标移动事件附加处理方法:获取当前鼠标相对容器的坐标。
  3. 在每帧处理方法中,设置获取方框在Canvas的左上坐标值及与鼠标点的差量值。设置移动缓存系数,计算每帧移向的距离。
  4. 每帧重新设置方框在Canvas的位置。

public partial class FollowExample : Page
{

private Point _lastMousePosition = new Point(0, 0);
private Vector _rectangleVelocity = new Vector(0, 0);

public FollowExample()
{
    CompositionTarget.Rendering += UpdateRectangle;
    PreviewMouseMove += UpdateLastMousePosition;
}

private void UpdateRectangle(object sender, EventArgs e)
{
    var location = new Point(Canvas.GetLeft(followRectangle), Canvas.GetTop(followRectangle));

    //find vector toward mouse location
    var toMouse = _lastMousePosition - location;

    //add a force toward the mouse to the rectangles velocity
    var followForce = 0.01;
    _rectangleVelocity += toMouse*followForce;

    //dampen the velocity to add stability
    var drag = 0.8;
    _rectangleVelocity *= drag;

    //update the new location from the velocity
    location += _rectangleVelocity;

    //set new position
    Canvas.SetLeft(followRectangle, location.X);
    Canvas.SetTop(followRectangle, location.Y);
}

private void UpdateLastMousePosition(object sender, MouseEventArgs e)
{
    _lastMousePosition = e.GetPosition(containerCanvas);
}

}

FrameIndependentFollowExample帧自主跟随示例

clipboard.png

实现效果:

  1. 红框有惯性跟随鼠标,相比上一个示例区别是,跟随鼠标速度快,但是冲过鼠标偏移也大,在鼠标移动到突然静止时,红框会在鼠标摇摆慢慢回复到静止点。

代码此处增加一个字段:时间间隔值TimeSpan _lastRender,目的是获取帧间隔时间值。其他同上

public partial class FrameIndependentFollowExample : Page
{
    private Point _lastMousePosition = new Point(0, 0);
    //timing variables
    private TimeSpan _lastRender;
    private Vector _rectangleVelocity = new Vector(0, 0);

    public FrameIndependentFollowExample()
    {
        InitializeComponent();
        _lastRender = TimeSpan.FromTicks(DateTime.Now.Ticks);
        CompositionTarget.Rendering += UpdateRectangle;
        PreviewMouseMove += UpdateLastMousePosition;
    }
    
private void UpdateLastMousePosition(object sender, MouseEventArgs e)
{
    _lastMousePosition = e.GetPosition(containerCanvas);
}

此处与上例变化主要是帧间位移差量的计算,主要与参数(帧间时间值)有关。

  1. followForce为移向同一方向鼠标的(力量)速度参数,值越大移动速度越快。
  2. drag为惯性来回幅度值,当为1时,永远也不会静止,红框在鼠标点来回晃动。>1,红框跑掉,越接近0时,幅度为0.
  3. delta抑制移向鼠标速度参数,与followForce一起决定移向鼠标的速度。不过作用度有点差异,因为中间有一个幅度乘系数。
private void UpdateRectangle(object sender, EventArgs e)
{
    var location = new Point(Canvas.GetLeft(followRectangle), Canvas.GetTop(followRectangle));

    //find vector toward mouse location
    var toMouse = _lastMousePosition - location;

    //add a force toward the mouse to the rectangles velocity
    var followForce = 0.01;
    _rectangleVelocity += toMouse*followForce;

    //dampen the velocity to add stability
    var drag = 0.8;
    _rectangleVelocity *= drag;

    //update the new location from the velocity
    location += _rectangleVelocity;            

    //set new position
    Canvas.SetLeft(followRectangle, location.X);
    Canvas.SetTop(followRectangle, location.Y);
}

ReusableFollow可重用的跟随方框

clipboard.png

实现效果:
1.跟随鼠标方框为3个,这些都继承同一个Canvas,都是先摇摆跟随功能效果。
xaml代码:

<Canvas Background="transparent">
    <perFrameAnimation:FollowMouseCanvas Canvas.Left="0" Canvas.Top="0" Background="red" Width="50" Height="50">
      <!-- could put more content that will also follow mouse here-->
    </perFrameAnimation:FollowMouseCanvas>
    <perFrameAnimation:FollowMouseCanvas Canvas.Left="300" Canvas.Top="0" Background="green" Width="50" Height="50">
      <!-- could put more content that will also follow mouse here-->
    </perFrameAnimation:FollowMouseCanvas>
    <perFrameAnimation:FollowMouseCanvas Canvas.Left="0" Canvas.Top="300" Background="blue" Width="50" Height="50">
      <!-- could put more content that will also follow mouse here-->
    </perFrameAnimation:FollowMouseCanvas>
  </Canvas>

继承Canvas,实现摇摆跟随功能:

  1. 多了个字段:Canvas _parentCanvas;作为获取上级容器鼠标点的一个引用。其他与上例同理。
  2. 最下面代码获取鼠标在容器中坐标时做了方框背景色差异判断,以区分方框的不同位置。
  3. 注释的一行代码为源码,原意为绿色系蓝色方框过一段时间才能出现,因为位移距离太大了,原因是第一帧TimeSpan.FromTicks(DateTime.Now.Ticks)值过大
public class FollowMouseCanvas : Canvas
{
    private TimeSpan _lastRender;
    private Canvas _parentCanvas;
    private Point _parentLastMousePosition = new Point(0, 0);
    private Vector _velocity = new Vector(0, 0);

    public FollowMouseCanvas()
    {
        //_lastRender = TimeSpan.FromTicks(DateTime.Now.Ticks);//注释掉是因为初始值太大,最后location差值过大而一开始不会显示绿色与蓝色方框。
        CompositionTarget.Rendering += UpdatePosition;
    }

    private void UpdatePosition(object sender, EventArgs e)
    {
        var renderingArgs = (RenderingEventArgs) e;

        var deltaTime = (renderingArgs.RenderingTime - _lastRender).TotalSeconds;
        _lastRender = renderingArgs.RenderingTime;

        if (_parentCanvas == null)
        {
            _parentCanvas = VisualTreeHelper.GetParent(this) as Canvas;
            if (_parentCanvas == null)
            {
                //parent isnt canvas so just abort trying to follow mouse
                CompositionTarget.Rendering -= UpdatePosition;
            }
            else
            {
                //parent is canvas, so track mouse position and time
                _parentCanvas.PreviewMouseMove += UpdateLastMousePosition;
            }
        }
        

        //get location
        var location = new Point(GetLeft(this), GetTop(this));

        //check for NaN's and replace with 0,0
        if (double.IsNaN(location.X) || double.IsNaN(location.Y))
            location = new Point(0, 0);

        //find vector toward mouse location
        var toMouse = _parentLastMousePosition - location;

        //add a force toward the mouse to the rectangles velocity
        var followForce = 1.0;
        _velocity += toMouse*followForce;

        //dampen the velocity to add stability
        var drag = 0.95;
        _velocity *= drag;

        //update the new location from the velocity
        location += _velocity*deltaTime;

        //set new position
        SetLeft(this, location.X);
        SetTop(this, location.Y);
    }

    private void UpdateLastMousePosition(object sender, MouseEventArgs e)
    {
        if (_parentCanvas == null)
            return;
        if (this.Background==Brushes.Blue)
        {
            _parentLastMousePosition.X = e.GetPosition(_parentCanvas).X+100;
            _parentLastMousePosition.Y = e.GetPosition(_parentCanvas).Y;
        }
        else if (this.Background== Brushes.Green)
        {
            _parentLastMousePosition.X = e.GetPosition(_parentCanvas).X - 100;
            _parentLastMousePosition.Y = e.GetPosition(_parentCanvas).Y;
        }
        else
        {
            _parentLastMousePosition = e.GetPosition(_parentCanvas);
            
        }            
    }
}

ParticleEffets

clipboard.png

见第2章

扩展:

clipboard.png

CompositionTarget类:表示您的应用程序的显示图面。

  1. WPF 动画引擎为创建基于帧的动画提供了许多功能。 但是,在有些应用程序方案中,您需要根据每个帧控制呈现。 使用 CompositionTarget 对象,可以基于每个帧回调来创建自定义动画。
  2. CompositionTarget.Rendering事件

HwndTarget 类:表示到支持可视化撰写的窗口句柄的绑定。

  1. HwndTarget.RootVisual 属性:获取或设置由此窗口承载的页面的根可视化对象。
  2. HwndTarget.RenderMode 属性:获取或设置由此 HwndTarget 引用的窗口的呈现模式。

VisualTarget 类:提供用于跨越线程边界将一个可视化树连接到另一个可视化树的功能。

RenderingEventArgs 类:Rendering 事件的必需参数。

  1. RenderingEventArgs.RenderingTime 获取将呈现动画的下一个帧的估计目标时间。
  2. 每帧间的间隔时长,与呈现实时帧数的倒数反映出

李志玮
22 声望34 粉丝

求索~~


引用和评论

0 条评论