原文:一头雾水的"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. Python机器学习入门

    # NumPy Python科学计算基础包 import numpy as np # 导入numpy库并起别名为npnumpy_array = np.array([[1,3,5],[2,4,6]])p ...

  2. Luncene介绍

    1.Luncene介绍 案例:实现一个文件的搜索功能,通过关键字搜索文件,凡是文件名或文件内容包括关键字文件都需要找出来.还可以根据中文词语进行查询,并且需要支持多个条件查询.Lucene可以解决 数 ...

  3. 解决Windows10或者其他版本Windows Update报错的问题

    最近更新系统,发现报错0x80248014,系统版本为redstone2(创意者更新). 总结发现,只要是windows各个版本自动更新报错的,如0x80开头的一系列错误,都可以通过如下步骤解决: 手 ...

  4. JavaScript中数组的增删改查以及应用方式

    数组的增加方法 1.push()方法向数组中末尾添加一个元素,原数组改变 var arr=[1,2,3,4]; var arr1=arr.push(6); console.log(arr);//打印出 ...

  5. JavaScipt中的Math.ceil() 、Math.floor() 、Math.round()、Math.pow() 三个函数的理解

    以前一直会三个函数的使用产生混淆,现在通过对三个函数的原型定义的理解,其实很容易记住三个函数. 现在做一个总结: 1. Math.ceil()用作向上取整. 2. Math.floor()用作向下取整 ...

  6. 「PKUSC2018」神仙的游戏

    题目链接 比如说上面\(|S|\)为12的字符串,我们欲求出\(f(9)\)的值,那么上面相同颜色的字符必须两两能够匹配.也就是说,同种颜色的字符集里不能同时出现0和1.如果只考虑同种颜色集里相邻的两 ...

  7. 协程与concurent.furtrue实现线程池与进程池

    1concurent.furtrue实现线程池与进程池 2协程 1concurent.furtrue实现线程池与进程池 实现进程池 #进程池 from concurrent.futures impor ...

  8. SQL update select结合语句详解及应用

    QL update select语句 最常用的update语法是: 1 2 UPDATE TABLE_NAME SET column_name1 = VALUE WHRER column_name2 ...

  9. Kafka远程调试简单记录

    Kafka启动脚本: ./kafka-server-start.sh -daemon ../config/server.properties 最终翻阅脚本可以确定是调用kafka-run-class. ...

  10. javascript获取指定区间范围随机数的方法

    每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code //获取指定区间范围随机数,包括lowerValue和upperValue funct ...