>  기사  >  웹 프론트엔드  >  캔버스 차이 기능의 놀라운 사용

캔버스 차이 기능의 놀라운 사용

高洛峰
高洛峰원래의
2016-11-07 17:31:341753검색

이 기사에서는 이 효과가 어떻게 달성되는지 분석할 것입니다. 소스 코드에 대해 헷갈린다면 분석을 읽고 나면 갑자기 이해될 것이라고 믿습니다. 먼저 렌더링:

캔버스 차이 기능의 놀라운 사용

1. 원리 분석

이전 글의 단순 물결 애니메이션 효과와 비교하면, 이 애니메이션 효과는 기사는 마우스와 상호 작용할 수 있을 뿐만 아니라 파동 형성이 더 자연스럽고 물리적 법칙에 더 부합합니다. 전체 애니메이션의 형성 과정은 애니메이션에 표시된 것과 같습니다. 액체 표면의 위치에서 마우스를 클릭하면 여기의 액체 표면이 상대적으로 크게 변동하고 여기의 진동이 양쪽으로 퍼집니다. 에너지 감쇠, 후속 진동 진폭은 점점 더 낮아지고 최종적으로 에너지는 0으로 감소하고 페이지는 조용해집니다. 아주 신비로운 느낌이 들지 않나요? 마오쩌둥 주석은 물체의 표면 현상에 속지 말라고 했습니다. 아래에서는 원리를 단계별로 분석해 보겠습니다.

우선, 정적 상태에서는 액체 표면 전체가 직사각형과 동일하다는 것을 알 수 있습니다. 액체 표면의 위치를 ​​클릭하면 그에 따라 직사각형이 변경됩니다. 그러나 실제로는 직사각형 전체가 변경된 것이 아니라 직사각형의 위쪽 가장자리만 변경되었습니다. 그렇다면 직사각형의 위쪽 가장자리만 변경하려면 어떻게 해야 할까요? 비밀은 직사각형의 상단 가장자리가 단순히 왼쪽 지점에서 오른쪽 지점까지 lineTo()가 아니라는 것입니다. 대신에 많은 점 lineTo()로 구성됩니다. 그림을 보면 이해하기 어려울 수 있습니다.

캔버스 차이 기능의 놀라운 사용

위쪽에는 이 점들의 좌표가 모두 많이 설정되어 있습니다. 수평 방향으로 일정 거리만큼 분리되어 있습니다. 이렇게 정지된 상태에서는 일반적인 직사각형과 다르지 않다는 것을 알 수 있습니다. 이 점의 위치를 ​​변경하면 직사각형의 모양도 동시에 변경되어 다른 효과를 낼 수 있습니다.

2. 차등방정식

차차방정식을 고민하는 분들이 많은데 방법이 없으니 그냥 놔두세요! 이 지식 포인트는 고급 수학의 미분 방정식에 관한 섹션에 있습니다. 이해가 안 되면 잊어버리세요! 다음 사용법도 기억해두시면 좋지만, 스타일을 위해 간략하게 소개하겠습니다.

수학에서 차이 방정식이라고도 알려진 반복 관계는 수열을 재귀적으로 정의하는 방정식입니다. 수열의 각 항목은 이전 항목 함수로 정의됩니다. 일부 간단하게 정의된 반복 관계는 매우 복잡한(혼돈) 특성을 나타낼 수 있으며 수학의 비선형 분석 분야에 속합니다.

순서의 각 항목은 이전 항목의 기능으로 정의된다는 점을 기억하세요. 이것이 우리가 사용하는 원칙입니다. 그의 이미지를 matalab으로 그리면 다음과 같습니다.

캔버스 차이 기능의 놀라운 사용

원래 기능인 빨간색 곡선에만 집중하면 물결처럼 보이죠? 우리가 해야 할 일은 그러한 파형에 따라 점들의 묶음을 배열하는 것입니다.

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的宽度和点的数目有关。这样我们就把点创建完成了,来绘制一下看看效果。

캔버스 차이 기능의 놀라운 사용

代码如下:

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:在得到第一帧的差分值后就该调用每个点的更新函数了,并且传入计算好的差分值。形成的效果如下图所示

캔버스 차이 기능의 놀라운 사용

看一下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으로 문의하세요.