canvas实现贝塞尔曲线运动

tech2025-06-24  7

正题前的逼逼赖赖: 上一篇实现了绘制出理想的贝塞尔曲线获取三次贝塞尔曲线的点坐标(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)