原文:一头雾水的"Follow The Pointer"

一头雾水的"Follow The Pointer"
                                                                                                     周银辉

Microsoft Expression Blend中有一个示例程序"Follow The Pointer", 看程序演示会觉得很酷,看代码(点击下载)会觉得一头雾水, 不过现在我便借此介绍一下WPF中的CompositionTarget 以及该示例中设计到了一些物理知识.

一, CompositionTarget
虽然我们拥有Storyboard(故事板)以及Microsoft Expression Blend为WPF动画制作提供强有力的支持,但你会发现这是不够的,也许你希望能通过代码来控制动画中的每一帧,以便显示更加复杂和视觉效果更好的动画, 那么你就得了解CompositionTarget 类.

CompositionTarget 类用于呈现你的应用程序显示表面, 每次绘制时其都会触发其Rendering事件, 其绘制次数取决于你计算机的FPS.
我们可以自定义Rendering事件的处理器来进行一些自定义的绘制或其他工作(注意其会根据计算机的FPS来不停地调用改事件处理器,比如每秒60次,所以不应该在这里进行复杂费时的操作) 比如:

CompositionTarget.Rendering += delegate
{
    this.UpdateColor();
};

这里有一个简单的例子说明CompositionTarget的用法,你可以点击这里下载

二, "Follow The Pointer"中的物理知识
编写视觉效果稍稍好一点的动画时不使用数学或物理知识几乎是不可能的. "Follow The Pointer"示例中用到了力,速度,加速度以及流动摩擦(其变形形式).
动画的简单的流程: 当用户移动鼠标时,程序会计算可移动控件(以下称"小方块",就是跟随鼠标的那个控件)的位置到鼠标位置所形成的向量, 并将该向量作为施加到小方块上的作用力.该作用力会使小方块朝鼠标所在位置移动.假设小方块处于空气(或水等)环境中,小方块的移动会产生流摩擦力,该摩擦力与速度成正比,其会使小方块减速并最终停止下来.
具体如何表现这些物理知识请参考下面的两段代码
这里是原示例代码:

private void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            // Current position of mouse pointer relative to the movable control.
            // The Mouse class is defined in the System.Windows.Input namespace.
            Point mousePos = Mouse.GetPosition(this.MovableControl); 

            TimeSpan currentTime = this.stopwatch.Elapsed;
            double elapsedTime = (currentTime - this.prevTime).TotalSeconds;
            this.prevTime = currentTime;

            // The vector to the mouse pointer represents the force currently acting on the movable control.
            Vector force = new Vector(mousePos.X, mousePos.Y);

            // The smaller the value of damping, the more iterations it takes to approach the mouse pointer, thus allowing velocity to grow larger.
            force = (force * this.SpringSlider.Value - this.velocity * this.DampingSlider.Value) * elapsedTime;

            // The current force causes an acceleration (a change in velocity).
            this.velocity += force;

            // If the eye won't notice any further motion then don't animate on this iteration.
            if (velocity.Length < epsilon)
                return;

            // Distance equals speed times time.
            this.translation.X += this.velocity.X * elapsedTime; 
            this.translation.Y += this.velocity.Y * elapsedTime;
        }

这里是改写与简化后的示例代码:

void CompositionTarget_Rendering(object sender, EventArgs e)
        {

            //上次绘制到此时的时间间隔
            TimeSpan currentTime = this.stopwatch.Elapsed;
            double elapsedTime = (currentTime - this.prevTime).TotalSeconds;           
            this.prevTime = currentTime;

            //鼠标相对于小方块的位置
            Point mouseLoc = Mouse.GetPosition(this.rectangle1);
            //由于改相当位置是相对于小方块左上角的,将其纠正到相当于小方块中心
            mouseLoc.Offset(-this.rectangle1.ActualWidth / 2, -this.rectangle1.ActualHeight / 2);


            //使用鼠标相对于小方块的位置作为外力
            Vector force = new Vector(mouseLoc.X, mouseLoc.Y);

            //流动摩擦力系数假设为该值
            double coefficient = 5;

            //假设小方块质量为1,则加速度为a = force/1;
            //那么在elapsedTime内其速度的变化量为a*elapsedTime
            //由于流动摩擦力与速度成正比,那么流动摩擦力为coefficient * this.velocity
            //所以速度的变化为(force * 200 - coefficient * this.velocity) * elapsedTime
            //这里为了演示中小方块的速度更快一点,我们将外力扩大了200倍
            Vector velocityDelta = (force * 200 - coefficient * this.velocity) * elapsedTime;

            //当前速度
            this.velocity += velocityDelta;

            //小方块的新位置
            this.translation.X += this.velocity.X * elapsedTime;
            this.translation.Y += this.velocity.Y * elapsedTime;
            
        }
       

下载Demo以及源代码

一头雾水的"Follow The Pointer"的更多相关文章

  1. What is the difference between a Clustered and Non Clustered Index?

    A clustered index determines the order in which the rows of a table are stored on disk. If a table h ...

  2. Discovering the Computer Science Behind Postgres Indexes

    This is the last in a series of Postgres posts that Pat Shaughnessy wrote based on his presentation ...

  3. Enhancing the Scalability of Memcached

    原文地址: https://software.intel.com/en-us/articles/enhancing-the-scalability-of-memcached-0 1 Introduct ...

  4. 用arm-linux-gcc v4.3.4交叉编译Qt4.8.3

    1.解压缩 #tar zxvf  qt-everywhere-opensource-src-4.8.3.tar.gz 2. configure #mkdir buildarm-static #cd b ...

  5. Golang源码探索(三) GC的实现原理

    Golang从1.5开始引入了三色GC, 经过多次改进, 当前的1.9版本的GC停顿时间已经可以做到极短. 停顿时间的减少意味着"最大响应时间"的缩短, 这也让go更适合编写网络服 ...

  6. Understanding the Objective-C Runtime

    Wednesday, January 20, 2010 Understanding the Objective-C Runtime The Objective-C Runtime is one of ...

  7. Golang源码探索(三) GC的实现原理(转)

    Golang从1.5开始引入了三色GC, 经过多次改进, 当前的1.9版本的GC停顿时间已经可以做到极短.停顿时间的减少意味着"最大响应时间"的缩短, 这也让go更适合编写网络服务 ...

  8. golang----GC的实现原理

    Golang从1.5开始引入了三色GC, 经过多次改进, 当前的1.9版本的GC停顿时间已经可以做到极短.停顿时间的减少意味着"最大响应时间"的缩短, 这也让go更适合编写网络服务 ...

  9. Python C/C++ 拓展使用接口库(build-in) ctypes 使用手册

    Python C/C++ 拓展使用接口库(build-in) ctypes 使用手册 ctypes 是一个Python 标准库中的一个库.为了实现调用 DLL,或者共享库等C数据类型而设计.它可以把这 ...

随机推荐

  1. 给你一个全自动的屏幕适配方案(基于SW方案)!—— 解放你和UI的双手

    Calces系列相关文章:Calces自动实现Android组件化模块构建 前言 屏幕适配一直是移动端开发热议的问题,但是适配方案往往在实际开发的时候会和UI提供的设计稿冲突.本文主要是基于官方推荐的 ...

  2. couldn't locate lint-gradle-api-26.1.2.jar for flutter project

    Could not find com.android.tools.lint:lint-gradle:26.1.2 当我尝试构建发行版本APK 时导致报这种错误,无法发行,针对自己的项目作出了相关修改, ...

  3. 命令行选项 - Mozilla 产品与私有技术 | MDN - Google Chrome

    命令行选项 在本文章中 语法规则 使用命令行选项 示例 用户配置档 -CreateProfile profile_name -CreateProfile "profile_name prof ...

  4. python第六十三天-- 第十一周作业

    题目:基于RabbitMQ rpc实现的主机管理 需求: 可以对指定机器异步的执行多个命令例子:>>:run "df -h" --hosts 192.168.3.55 ...

  5. python第四十二天 socket ---ssh

    用scoket 写一个简版的ssh 服务端: #!usr/bin/env python #-*-coding:utf-8-*- # Author calmyan import socket,os s= ...

  6. sql 删除默认索引,对象 依赖于 列,由于一个或多个对象访问此列

    declare @name varchar(50)select  @name =b.name from sysobjects b join syscolumns aon b.id = a.cdefau ...

  7. Spring MVC 的工作原理

    引自:https://www.cnblogs.com/xiaoxi/p/6164383.html SpringMVC的工作原理图: SpringMVC流程 1.  用户发送请求至前端控制器Dispat ...

  8. 使用Gitkraken进行其他Git操作

    使用Gitkraken进行其他Git操作 查看某次 commit 的文件改动 使用 Gitkraken 能非常方便的看到任意一次的 commit 对项目文件的改动. 具体操作是:在树状分支图上单击某个 ...

  9. activiti获取可回退的节点

    在处理流程回退时,需要获取某个节点当前可以回退到的节点,简单分析下: 1. 只支持回退到userTask. 2. 如果流程流转过某节点时生成了多个任务,从其中某一个任务回退到该节点后,不处理另外的任务 ...

  10. node_01_自定义模块(先创建package.json)

    package.json必须是json格式 你必须确保所有的字符串,包括属性名,都是使用双引号而不是单引号 { "name": "163", "ver ...