搜索
首页web前端html教程canvas差分函数的妙用

canvas差分函数的妙用

Nov 07, 2016 pm 05:31 PM
canvas

在这篇文章中我们就分析这种效果是如何实现的,如果你对源码比较懵逼,相信看完解析就会恍然大悟。先上效果图:

3686196226-57a94a892fcd0_articlex.gif

1.原理分析

相比与上篇文章中简陋的水波动画的效果,本文的动画效果不仅能够和鼠标进行交互,而且波浪的形成更加自然,更加符合物理规律。整个动画的形成过程就如动图中所展示的那样,在液面的位置点击鼠标,此处的液面就会有一个比较大的起伏,然后此处的震动会向两边传播,随着能量的衰减,后面的震动幅度会越来越下,最后能量衰减到零,页面趋于平静。听上去是不是很玄乎,感觉很高深!毛主席告诉我们千万不要被物体的表面现象所迷惑(谁知道是谁说的呢o(^▽^)o)。下面我们就来一步一步的分析,这其中的原理。

首先,在静止状态下我们可以看到整个液面就相当于是个矩形。而当我们点击液面的位置时,这个矩形就发生了相应的变化。但其实并不是整个矩形都发生了变化,而只是矩形的上边发生了变化。那是如何做到仅仅让矩形的上边发生变化的呢?秘诀就在矩形的上边并不是简单的从左边的点lineTo()到右边的点。而是由很多的点lineTo()组成。这样讲可能不太好理解,看图说话:

892670031-581ee02140c15_articlex.png

在上部我们设置了很多的点,这些点的纵坐标都是一样的,只是在水平方向相隔一定的间距。这样在静止的状态下,我们就可以它看见与普通的矩形别无二致。而改变这些点的位置时我们就能同时改变矩形的形状,从而形成不同的效果。

2.差分方程

说到差分方程也许很多人会头疼,不过也没本法,疼就疼会吧!这个知识点在高数里讲微分方程那一节,如果不明白,就算了吧!记住下面的用法也不错,不过为了逼格我们还是简单的介绍下。

在数学上,递推关系(recurrence relation),也就是差分方程(difference equation),是一种递推地定义一个序列的方程式:序列的每一项目是定义为前一项的函数。某些简单定义的递推关系式可能会表现出非常复杂的(混沌的)性质,他们属于数学中的非线性分析领域。

记住一点,序列的每一项是定义为前一项的函数,我们用的就是这个原理。他的图像如果用matalab来绘制就是下面这样:

892670031-581ee02140c15_articlex.png

只关注原函数,红色的那条曲线就可以了,是不是特别像水波。我们要做的就是让那一堆点按照这样的波形去排列。

3.代码实现

1.准备工作

下面就到了大家最喜欢的代码时间。首先,我们创建一个点类Vertexes, 它的作用就是定义并更新那一堆点,代码在vertex.js中,如下:

function Vertex(x,y,baseY){
        this.baseY = baseY;         //基线
        this.x = x;                 //点的坐标
        this.y = y;            
        this.vy = 0;                //竖直方向的速度
        this.targetY = 0;           //目标位置
        this.friction = 0.15;       //摩擦力
        this.deceleration = 0.95;   //减速
    }
//y坐标更新
Vertex.prototype.updateY = function(diffVal){
        this.targetY = diffVal + this.baseY;   //改变目标位置
        this.vy += (this.targetY - this.y);       //速度
        this.vy *= this.deceleration;
        this.y += this.vy * this.friction;     //改变坐标竖直方向的位置
    }

我们要用这个函数去创建那一堆点。回到我们的主文件index.js中。我们先初始化一些要用的东西:

var canvas = document.getElementById('canvas'),
    ctx = canvas.getContext('2d'),
    W = window.innerWidth;
    H = window.innerHeight;

    canvas.width = W;
    canvas.height = H;

var color1 = "#6ca0f6",    //矩形1的颜色
    color2 = "#367aec";   //矩形2的颜色
    
var vertexes = [],    //顶点坐标
    verNum = 250,     //顶点数
    diffPt = [],      //差分值

然后,创建点并把它push进vertexes中,同时也创建相应数量的差分值,同样把它放到diffPt数组中,这样每个点都有了对应的差分值。

for(var i=0; i<verNum; i++){
    vertexes[i] = new Vertex(W/(verNum-1)*i, H/2, H/2);
    diffPt[i] = 0;                                         //初始值都为0
}

结果是,每个顶点的y坐标都在(H/2)的高度,水平坐标每隔一定的间隔取一个点。在这里是每隔4.5个像素取一个点,这与你canvas的宽度和点的数目有关。这样我们就把点创建完成了,来绘制一下看看效果。

892670031-581ee02140c15_articlex.png

代码如下:

function draw(){
        
        //矩形1
        ctx.save()
        ctx.fillStyle = color1;
        ctx.beginPath();
        ctx.moveTo(0, H);
        ctx.lineTo(vertexes[0].x, vertexes[0].y);
        for(var i=1; i<vertexes.length; i++){
            ctx.lineTo(vertexes[i].x, vertexes[i].y);
        }
        ctx.lineTo(W,H);
        ctx.lineTo(0,H);
        ctx.fill();
        ctx.restore();
        
        //矩形2
        ctx.save();
        ctx.fillStyle = color2;
        ctx.beginPath();
        ctx.moveTo(0, H);
        ctx.lineTo(vertexes[0].x, vertexes[0].y+5);
        for(var i=1; i<vertexes.length; i++){
            ctx.lineTo(vertexes[i].x, vertexes[i].y+5);
        }
        ctx.lineTo(W, H);
        ctx.lineTo(0, H);
        ctx.fill();
        ctx.restore();
}

就像你看到的那样此时我们的液面完全是静止的(因为没更新点嘛)。之所以要绘制两个矩形,你看看效果图就明白了,只是为了更好看,你完全可以绘制第三层,第四层。下面我们就来更新这些点的坐标。

2.核心代码

点的更新我们放在了update函数中。首先,我们设置一个初始的震荡点,缓冲变量和初始差分值。

var vPos = 125;  //震荡点
var dd = 15;     //缓冲
var autoDiff = 1000;  //初始差分值

这里的震荡点就是我们的起震位置,意思是vertexes中的第125号点开始起震,它对应的差分值就是autoDiff。它的改变会引起其他点的变化,从而达到更新其他差分值的效果。

function update(){
        autoDiff -= autoDiff*0.9;        //1
        diffPt[vPos] = autoDiff;         

        //左侧
        for(var i=vPos-1; i>0; i--){     //2
            var d = vPos-i;
            if(d > dd){
                d=dd;
            }
            diffPt[i]-=(diffPt[i] - diffPt[i+1])*(1-0.01*d);
        }
        //右侧
        for(var i=vPos+1; i<verNum; i++){   //3
            var d = i-vPos;
            if(d>dd){
                d=dd;
            }
            diffPt[i] -= (diffPt[i] - diffPt[i-1])*(1-0.01*d);
        }

        //更新Y坐标
        for(var i=0; i<vertexes.length; i++){  //4
            vertexes[i].updateY(diffPt[i]);
        }
    }

现在我们对上面的部分做详细解释:
代码1: 我们设置了起震位置的差分偏移量为autoDiff=100,注意autoDiff -= autoDiff*0.9;, 也就是说它的值每一帧都会变化。

代码2:为起震位置的左边,主要关注diffPt[i]-=(diffPt[i] - diffPt[i+1])*(1-0.01*d);这一行。i的起始位置为124,默认差分值为0。稍作简单推算,你会发现,经过更新后第124号点的差分值为99,同理第123号为97.02。以此类推,我们就可以得到第一帧的所有点的差分值。右边同理。

代码4:在得到第一帧的差分值后就该调用每个点的更新函数了,并且传入计算好的差分值。形成的效果如下图所示

892670031-581ee02140c15_articlex.png

看一下updateY函数,我们把目标位置targetY设置为差分值diffVal和基线baseY的和。然后,通过距离计算需要运动的速度vy,最后将速度作用于点的纵坐标。这一段是不是与弹性动画缓动动画那一节很相似呢?

在缓冲系数dd的作用下,两侧的波会在扩散的过程中越来越小,最后趋近于0.我们也是通过这个变量去控制液体的粘度系数,达到粘稠度高的物体扩散的越缓慢并且起伏比较低,粘稠度低的物体扩散迅速但起伏大的效果。

随后,因为autoDiff的不断衰减,不同幅值波形的叠加形成波浪效果,最终衰减到0.液面也就趋于平静了。

现在,我们把update()和draw()放入动画循环中你就会看到水波起伏然后趋于平静的效果。

(function drawframe(){
        ctx.clearRect(0, 0, W, H);
        window.requestAnimationFrame(drawframe, canvas);
        update()
        draw();
    })()

3.鼠标交互

上面的代码已经实现了波浪动画的效果,但是震荡完成后就平静了,不会再发生震荡的效果。这一步我们就来实现点哪,哪震的效果。实现的思路很简单:水波之所以区域平静是因为起震位置的差分值不断衰减的结果,我们只需要在点击鼠标的位置重设autoDiff就可以了。此外,起震点的位置也要变成鼠标点击的位置。代码如下:

canvas.addEventListener(&#39;mousedown&#39;, function(e){
        var mouse = {x:null, y:null};

        if(e.pageX||e.pageY){
            mouse.x = e.pageX;
            mouse.y = e.pageY;
        }else{
            mouse.x = e.clientX + document.body.scrollLeft +document.documentElement.scrollLeft;
            mouse.y = e.clientY + document.body.scrollTop +document.documentElement.scrollTop;
        }

        //重设差分值
        if(mouse.y>(H/2-50) && mouse.y<(H/2 +50)){
            autoDiff = 1000;
            vPos = 1 + Math.floor((verNum - 2) * mouse.x / W);
            diffPt[vPos] = autoDiff;
        }

        console.log(mouse.x, mouse.y)

    }, false)

在获取鼠标位置这里应该注意一点,我们没有减去canvas的偏移量,这是因为在这里canvas做的是全屏设置。所以,如果你的画布并不是全屏大小,建议你使用我们的utils.js文件中的方法captureMouse来获取鼠标的坐标。

另外在判断鼠标是否点击在了液面上,我们设定了一个比较宽的范围,上下共100px。这样做的目的是让用户很容易就能触发这个事件,而不是只在页面那唯一的一个值上才能触发。这种做法相信你以前做过,对于比较小的物体我们会遮罩一个大一些的透明物体,然后在该物体上做事件的触发,便于用户操作。


声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
举一个带有属性的HTML标签的示例。举一个带有属性的HTML标签的示例。May 16, 2025 am 12:02 AM

HTML标签和属性的使用方法包括:1.基本用法:使用标签如和,通过属性如src和href添加必要信息。2.高级用法:使用data-*自定义属性实现复杂交互。3.避免常见错误:确保属性值用引号包围。4.性能优化:保持简洁,使用标准属性和CSS类名,确保图像有alt属性。掌握这些将提升网页开发技能。

HTML标签和HTML属性有什么区别?HTML标签和HTML属性有什么区别?May 14, 2025 am 12:01 AM

HTMLtagsdefinethestructureofawebpage,whileattributesaddfunctionalityanddetails.1)Tagslike,,andoutlinethecontent'splacement.2)Attributessuchassrc,class,andstyleenhancetagsbyspecifyingimagesources,styling,andmore,improvingfunctionalityandappearance.

HTML的未来:进化和趋势HTML的未来:进化和趋势May 13, 2025 am 12:01 AM

HTML的未来将朝着更加语义化、功能化和模块化的方向发展。1)语义化将使标签更明确地描述内容,提升SEO和无障碍访问。2)功能化将引入新元素和属性,满足用户需求。3)模块化将支持组件化开发,提高代码复用性。

为什么HTML属性对Web开发很重要?为什么HTML属性对Web开发很重要?May 12, 2025 am 12:01 AM

htmlattributesarecrucialinwebdevelopment forcontrollingBehavior,外观和功能

Alt属性的目的是什么?为什么重要?Alt属性的目的是什么?为什么重要?May 11, 2025 am 12:01 AM

alt属性是HTML中标签的重要部分,用于提供图片的替代文本。1.当图片无法加载时,alt属性中的文本会显示,提升用户体验。2.屏幕阅读器使用alt属性帮助视障用户理解图片内容。3.搜索引擎索引alt属性中的文本,提高网页的SEO排名。

HTML,CSS和JavaScript:示例和实际应用HTML,CSS和JavaScript:示例和实际应用May 09, 2025 am 12:01 AM

HTML、CSS和JavaScript在网页开发中的作用分别是:1.HTML用于构建网页结构;2.CSS用于美化网页外观;3.JavaScript用于实现动态交互。通过标签、样式和脚本,这三者共同构筑了现代网页的核心功能。

如何在标签上设置lang属性?为什么这很重要?如何在标签上设置lang属性?为什么这很重要?May 08, 2025 am 12:03 AM

设置标签的lang属性是优化网页可访问性和SEO的关键步骤。1)在标签中设置lang属性,如。2)在多语言内容中,为不同语言部分设置lang属性,如。3)使用符合ISO639-1标准的语言代码,如"en"、"fr"、"zh"等。正确设置lang属性可以提高网页的可访问性和搜索引擎排名。

HTML属性的目的是什么?HTML属性的目的是什么?May 07, 2025 am 12:01 AM

htmlattributeseresene forenhancingwebelements'functionalityandAppearance.TheyAdDinformationTodeFineBehavior,外观和互动,使网站互动,响应式,visalalyAppealing.AttributesLikutesLikeSlikEslikesrc,href,href,href,类,类型,类型,和dissabledtransfransformformformformformformformformformformformformformformforment

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中