Chapter4 附录 贝兹曲线

前言

什麽是贝兹曲线?它能创造一连串平滑的曲线,被应用在PS和AI中的钢笔、以及常见的CSS Animation,换句话说,你学会了贝兹曲线,就大概懂一半网页动画了,它厉害的地方在於,用一个很简单并直觉的方式来设定动画,加上其可视化的特性,在上述例子中都能很轻松的运用。

像是在这个网站用滑鼠拉动控制杆後,就可以轻易地拿到参数,把它复制到CSS Animation的设定中,就能轻松产生动画。

参考资料

起初是没打算拿贝兹曲线来作文章的,CSS已经有框架可以用了,为什麽我们还要用JS从底层实作一遍呢?这个论点大致上没错,不过,昨晚睡前我不经意看到了这部影片:The Beauty of Bézier Curves

这部影片,是我目前为止看过讲的最简单易懂的贝兹曲线,包含很完整的纲要和学习步骤,当然,里面还是包含了一些数学,但重要的是,它有谈到如何透过简化的公式实现美丽的贝兹曲线,并且,我们的眼光可以不局限於CSS的框架,透过多条贝兹曲线的连接,可以轻松实现昨天所说的「寻路问题」,并打破原先的随机性不可预测性

虽然是推荐大家看原版英文的影片(有CC字幕),享受质感,不过还是提供大家英文苦手的选择:bilibibli的翻译版本

https://ithelp.ithome.com.tw/upload/images/20211002/20135197Z6GNK9JQjR.jpg

三次方贝兹曲线 Cubic Bezier Curves

这边我们设计一个向量物件,来描述贝兹曲线的四个基本点,名称和教学中采用一致的(p0, p1, p2 ,p3)

function vector(x, y){
    this.x = x;
    this.y = y;
}
function bezierCurves(x0, y0, x1, y1, x2, y2, x3, y3){
    this.p0 = new vector(x0, y0);
    this.p1 = new vector(x1, y1);
    this.p2 = new vector(x2, y2);
    this.p3 = new vector(x3, y3);
    this.timestamp = Date.now();
    this.lifeTime = 2;
}

然後把路径的公式描绘出来,为了能清楚呈现多项式中的次方项,采用Math.pow的写法,而不是t x t x t,一样是在原型上添加方法

bezierCurves.prototype.Path = function(){
    let P = function(a, b){
        return Math.pow(a, b);
    }
    return {'x': x0 * (-1 * P(t ,3) + 3 * P(t ,2) - 3 * P(t ,1) + 1 )
                +x1 * ( 3 * P(t ,3) - 6 * P(t ,2) + 3 * P(t ,1))
                +x2 * (-3 * P(t ,3) + 3 * P(t ,2))
                +x3 * ( 1 * P(t ,3)),
            'y': y0 * (-1 * P(t ,3) + 3 * P(t ,2) - 3 * P(t ,1) + 1 )
                +y1 * ( 3 * P(t ,3) - 6 * P(t ,2) + 3 * P(t ,1))
                +y2 * (-3 * P(t ,3) + 3 * P(t ,2))
                +y3 * ( 1 * P(t ,3))}
}

然後我们试着让一条新的贝兹曲线接上旧的,一样是设计animeList跑回圈时所呼叫的NextFrame方法:

bezierCurves.prototype.NextFrame = function(){
    // 计算下一侦的位置(0~1)
    let dT = (Date.now() - this.timestamp) / 1000 / this.lifeTime;
    if(dT <= 1){
        let point = bezierCurves.prototype.Path(dT);
        context.save();
        context.translate(pointX, pointY);
        context.drawImage(mouseImg, -mouseImg.width/2, -mouseImg.height/2);
        context.restore();
    }
    else{
        // 制作一个闭环
        let newObject = new bezierCurves(this.p3.x, this.p3.y,
                                         this.p3.x * 2 - this.p2.x,
                                         this.p3.y * 2 - this.p2.y,
                                         this.p0.x * 2 - this.p1.x,
                                         this.p0.y * 2 - this.p1.y,
                                         this.p0.x, this.p0.y);
        let index = animeList.indexOf(this);
        delete animeList[index];
        animeList[index] = newObject;
    }
}

有点快睡着了,打code打到度估,今天就先这样了不好意思><,这几天有空再回来补demo吧!

其他:
https://ithelp.ithome.com.tw/upload/images/20211002/20135197Ihbibm5GRz.jpg

bezierCurves.prototype.Velocity = function(){
    let P = function(a, b){
        return Math.pow(a, b);
    }
    return {'x': x0 * (-3 * P(t ,2) +  6 * P(t ,1) - 3 )
                +x1 * ( 9 * P(t ,2) - 12 * P(t ,1) + 3 )
                +x2 * (-9 * P(t ,2) +  6 * P(t ,1))
                +x3 * ( 3 * P(t ,2)),
            'y': y0 * (-3 * P(t ,2) +  6 * P(t ,1) - 3 )
                +y1 * ( 9 * P(t ,2) - 12 * P(t ,1) + 3 )
                +y2 * (-9 * P(t ,2) +  6 * P(t ,1))
                +y3 * ( 3 * P(t ,2))}
}
bezierCurves.prototype.Acceleration = function(){
    let P = function(a, b){
        return Math.pow(a, b);
    }
    return {'x': x0 * ( -6 * P(t ,1) +  6 )
                +x1 * ( 18 * P(t ,1) - 12 )
                +x2 * (-18 * P(t ,1) +  6 )
                +x3 * (  6 * P(t ,1)),
            'y': y0 * ( -6 * P(t ,1) +  6 )
                +y1 * ( 18 * P(t ,1) - 12 )
                +y2 * (-18 * P(t ,1) +  6 )
                +y3 * (  6 * P(t ,1))}
}
bezierCurves.prototype.Jerk = function(){
    let P = function(a, b){
        return Math.pow(a, b);
    }
    return {'x': x0 * ( -6 )
                +x1 * ( 18 )
                +x2 * (-18 )
                +x3 * (  6 ),
            'y': y0 * ( -6 )
                +y1 * ( 18 )
                +y2 * (-18 )
                +y3 * (  6 )}
}

<<:  Day16- 让页面不再被上下左右上下左右

>>:  16 - Logs - 挖掘系统内部发生的状况 (4/4) - 透过 Filebeat 收集 Infrastructure 中各种服务的细节资讯

Day18 Loops(Ⅴ)

今天再举一个for回圈的例子,找出1~100的偶数。 Ans:从一开始所以一开始int i=1,然後...

LeetCode 双刀流:700. Search in a Binary Search Tree

700. Search in a Binary Search Tree 昨天提到链结串列(Link...

Day 15. 来了解Data Persistence in Unity

我的文章系列快要变成每天30分钟,学一点新的东西了。 一样是继续跟着教程走,Implement da...

android studio 30天学习笔记-day 8-基本介绍rxjava2

RxJava2是一套处理非同步(asynchronous)事件的library,这个library是...

Day-23 Toast

本篇的主角是Toast, 但这边的Toast并非吐司的意思, 而是显示讯息, 常用於提示或警示使用者...