canvas实现贝塞尔曲线运动

正题前的逼逼赖赖:
上一篇实现了绘制出理想的贝塞尔曲线获取三次贝塞尔曲线的点坐标(canvas),然后就要实现曲线运动了。

效果图

贝塞尔曲线运动效果

注意点
  • 找到贝塞尔曲线上的点的坐标,用到了计算公式
    B(t) = (1-t) [(1-t)2P0+ 2 (1-t) tP1 + t2P2] + t [(1-t)2P1+ 2 (1-t) tP2 + t2P3],0 <= t <= 1
    其中可以控制t的值来实现一个运动的效果,关于这个计算有很多大佬的博客有些。找找就有了
  • 要绘制的如果是规则的形状就就不提了,如果绘制的是图片,记得让ui不要透明边也切进去。
  • 控制贝塞尔曲线的形状,尽量不要让两个物体”相撞了“
代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style type="text/css">
        body {
            background: black;
        }

        #canvas {
            position: absolute;
            top: 0;
            left: 0;
            /* width: 600px; */
            /* height: 600px; */
        }

        #line {
            position: absolute;
            top: 0;
            left: 0;
            /* width: 600px; */
            /* height: 600px; */
        }
    </style>
</head>

<body>

    <canvas id="canvas" width="1080" height="1000"></canvas>
    <canvas id="line" width="1080" height="1000"></canvas>
    <script>
        function Curve(lineList, rate, wraper, loop, url) {
            this.line_list = lineList,//贝塞尔点数组
                this.rate = rate,//控制运动速度
                this.t = 0,//比例值
                this.loop = loop || false;//是否循环运动
	            this.url = url;//图片地址
	            this.ctx = wraper.getContext('2d');
	            //this.location_list = [];//这个是用来做点击事件的,判断是否点击到物体,但是懒得做了
        }
        //绘制贝塞尔曲线,用来观察运动是否符合
        Curve.prototype.drawline = function () {
            var ctx = this.ctx;
            for (var i = 0; i < pointarr.length; i++) {
                var point = pointarr[i];
                ctx.beginPath();
                ctx.moveTo(point.begin.x, point.begin.y);
                ctx.bezierCurveTo(point.control1.x, point.control1.y, point.control2.x, point.control2.y, point.end.x, point.end.y);
                ctx.strokeStyle = "#2578b0";
                ctx.lineWidth = 2;
                ctx.stroke();
            }

        }
        //绘制某一时刻的点
        Curve.prototype.drawPoint = function drawPoint(point,t, obj) {
            // var t = this.t;
            var ctx = this.ctx
            //根据三次贝塞尔曲线的公式,获取某时刻t[0-1]的贝塞尔曲线上的点            
            let x = point.begin.x * Math.pow((1 - t), 3) + 3 * t * point.control1.x * Math.pow((1 - t), 2) + 3 * point.control2.x * Math.pow(t, 2) * (1 - t) + point.end.x * Math.pow(t, 3);
            let y = point.begin.y * Math.pow((1 - t), 3) + 3 * t * point.control1.y * Math.pow((1 - t), 2) + 3 * point.control2.y * Math.pow(t, 2) * (1 - t) + point.end.y * Math.pow(t, 3);
            //移出画布后,重新开始

            //清除上一次的drawimage,可以会擦掉‘相撞’的物体,因此尽量控制
            ctx.clearRect(obj.x, obj.y, this.image.width / 5, this.image.height / 5);
            // ctx.beginPath();

            // 设置图片的位置,居中
            x = x - this.image.width / 5 / 2;
            y = y - this.image.height / 5 / 2
            //这边需要控制图片的大小,等比缩放
            ctx.drawImage(this.image, 0, 0, this.image.width, this.image.height, x, y, this.image.width / 5, this.image.height / 5)

            //显示坐标
            // ctx.font = 'bold 12px Arial';
            // ctx.textAlign = 'left';
            // ctx.textBaseline = 'bottom';
            // ctx.fillStyle = 'white';
            // ctx.fillText("x:" + parseInt(x), x, y - 24);
            // ctx.fillText("y:" + parseInt(y), x, y - 12);
            // ctx.closePath();
            return { x, y }
        }
        //加载图片
        Curve.prototype.loadImage = function (fn) {
            var image = new Image();
            image.src = this.url;
            var _this = this;
            image.onload = function () {
                _this.image = this;
                if (fn) {
                    fn();
                }
            }
        }
        //单个物体的运动
        Curve.prototype.animate = function () {
            let i = 0;
            var _this = this;
            let p = new Promise((resolve, reject) => {
                console.log('加载图片')
                _this.loadImage(resolve);
            })
            p.then(function () {
                var obj = { x: 0, y: 0 };
                //var k = _this.location_list.length;
                let t = _this.t;
                console.log('加载成功')
                let tid = setInterval(() => {
                    var point = _this.line_list[i];
                    obj = _this.drawPoint(point,t, obj);
                    t += _this.rate;
                    if (t > 1) {
                        t = 0;
                        i++;
                        if (i > (_this.line_list.length - 1)) {
                            if (_this.loop) {//如果循环,将当前访问的数组索引值重置为0
                                i = 0;
                            } else {//不循环就清理画布,并清理定时器
                                console.log(i)
                                _this.ctx.clearRect(obj.x, obj.y, _this.image.width / 5, _this.image.height / 5);

                                clearInterval(tid);
                            }
                        }
                    }
                }, 20);

            })
        }
        //绘制多个物体运动
        Curve.prototype.animate_line = function () {
            this.animate()
            setInterval(() => {
                this.animate()
            }, 3000)
        }
        //假装这个是理想的贝塞尔曲线点
        var pointarr = [
            {
                begin: {
                    x: 500,
                    y: 0
                },
                control1: {
                    x: 0,
                    y: 75
                },
                control2: {
                    x: 1000,
                    y: 225
                },
                end: {
                    x: 500,
                    y: 300
                }
            }, {
                begin: {
                    x: 500,
                    y: 300
                },
                control1: {
                    x: 50,
                    y: 375
                },
                control2: {
                    x: 1100,
                    y: 525
                },
                end: {
                    x: 500,
                    y: 600
                }
            }, {
                begin: {
                    x: 500,
                    y: 600
                },
                control1: {
                    x: 360,
                    y: 675
                },
                control2: {
                    x: 1800,
                    y: 825
                },
                end: {
                    x: 500,
                    y: 900
                }
            }
        ]
        var canvas1 = document.getElementById('line');
        var canvas2 = document.getElementById('canvas')
        var c1 = new Curve(pointarr, 0.03, canvas1)
        // c1.drawline()
        var c2 = new Curve(pointarr, 0.005, canvas2, false, './appcharacter/1/ (1).png')
        c2.animate_line()
    </script>
</body>

</html>
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值