ベジェ曲線、もうx回。 t==x の近似値を求めて返す
仕事中でも、3次方程式の解法を妄想するも、撃沈。
誤差1/10000でも、平均数回程度で収束するので、黙認。
これから、遅れを取り戻す!を組み込む。
// ベジェ曲線 (4点限定) function QBezierCurve_create (x2, y2 , x3, y3) { var step; var err = 0.0001; x2 *= 3; y2 *= 3; x3 *= 3; y3 *= 3; return function (t) { if (t < 0 || 1 < t) throw new Error(t); var p = step || t; var a, b, c, d, x, s; do { a = 1 - p; b = a * a; c = p * p; d = c * p; x = x2 * b * p + x3 * a * c + d; s = t - x; p += s *.5; } while (err < Math.abs(s)); step = p; return y2 * b * p + y3 * a * c + d; }; }
これでOK?
<!DOCTYPE html> <title>遅延時間を考えて移動する</title> <meta charset="UTF-8"> <style> canvas { background-color:black;} </style> <p> <canvas id="hoge" width="300" height="300"></canvas> </p> <script> //_______________ // CANVAS を利用して、点と線を簡単に描くもの (function () { function Graffiti (canvas) { this.ctx = canvas.getContext ('2d'); } function line (/*[x0, y0], [x1, y1], ...*/) { var index = 0; var x = arguments[index][0]; var y = arguments[index][1]; var arg; var ctx = this.ctx; ctx.beginPath (); ctx.moveTo (x, y); while (arg = arguments[++index]) { if ('b' === arg) { ctx.closePath (); break; } if ('bx' === arg) { ctx.closePath (); ctx.fillRect (); break; } ctx.lineTo (arg[0], arg[1]); } ctx.stroke (); return this; } function pset (x, y, c) { var ctx = this.ctx; if (c instanceof Array) { ctx.fillStyle = 'rgb(' + c[0] + ',' + c[1] +',' + c[2] + ')'; } ctx.fillRect (x, y, 1, 1); return this; } function create (canvas) { if (! canvas.getContext) return null; return new Graffiti (canvas); } //__________ Graffiti.prototype.line = line; Graffiti.prototype.pset = pset; Graffiti.create = create; this.Graffiti = Graffiti; })(); //_______________ // ベジェ曲線 (4点限定) function cubic_bezier (x2, y2 , x3, y3) { var step; var err = 0.0001; x2 *= 3; y2 *= 3; x3 *= 3; y3 *= 3; return function (t) { if (t < 0 || 1 < t) throw new Error(t); var p = step || t; var a, b, c, d, x, s; do { a = 1 - p; b = a * a; c = p * p; d = c * p; x = x2 * b * p + x3 * a * c + d; s = t - x; p += s *.5; } while (err < Math.abs(s)); step = p; return y2 * b * p + y3 * a * c + d; }; } if ('undefined' === typeof this.requestAnimationFrame) { this.requestAnimationFrame = this.requestAnimationFrame || this.webkitRequestAnimationFrame|| this.mozRequestAnimationFrame || this.oRequestAnimationFrame || this.msRequestAnimationFrame || function (callback, that) { var tmpFunc = function () { var timestamp = +(new Date); //(new Date).getTime (); callback (timestamp); //callback.call (that, timestamp); }; setTimeout (tmpFunc, Math.floor (1000/60)); }; } (function () { //_________________ // アニメーションの遅延を感知しながら補う function CompensateInterval (cbFunc, duration, timingFunc, delay, maxCompensate, interval, through) { this.cbFunc = cbFunc; // コールバック this.duration = duration; // 作動時間 this.timingFunc = timingFunc; // ベジェ曲線の関数 this.delay = delay; // ms 秒後に開始 this.maxCompensate = maxCompensate; //最大補間回数 this.interval = interval; // 間隔 this.through = through; // 遅延が発生したならコールバックを呼ばない (default: true) this.step = 1 / (duration / interval); this.count = null; this.startTime = null; this.timeCount = null; //ベジェ曲線に渡す引数(time) } function loop (timeStamp) { var max = this.maxCompensate; var delay = (timeStamp - this.startTime) - this.interval * this.count; var r; while (0 <= delay) { if (max === this.maxCompensate || ! this.through) { //遅延中&スルーするなら何もしない r = this.timingFunc (this.timeCount); this.cbFunc (r, this.timeCount); } this.timeCount += this.step; if (1 <= this.timeCount) return 1, 1; this.count += 1; if (0 === max++) //打ち切り break; delay -= this.interval; } return requestAnimationFrame (loop.bind (this)); } function CompensateInterval_init () { this.count = 0; this.timeCount = 0; this.startTime = (new Date).getTime (); loop.call (this); } function CompensateInterval_start () { if (this.delay) setTimeout (CompensateInterval_init.bind (this), this.delay); else CompensateInterval_init.call (this); } function CompensateInterval_create (cbFunc, duration, timingFunc, delay, maxCompensate, interval, through) { if (3 > arguments.length) throw new Error; switch (typeof timingFunc) { case 'function' : break; case 'string' : switch (timingFunc) { case 'ease' : timingFunc = cubic_bezier (0.25, 0.1, 0.25, 1.0); break; case 'linear' : timingFunc = cubic_bezier (0.0, 0.0, 1.0, 1.0); break; case 'ease-in' : timingFunc = cubic_bezier (0.42, 0, 1.0, 1.0); break; case 'ease-out' : timingFunc = cubic_bezier (0, 0, 0.58, 1.0); break; default : throw new Error ('Not know name'); } break; default : throw new Error ('Syntax error'); } return new CompensateInterval (cbFunc, duration, timingFunc, delay || 0, maxCompensate || 3, interval || 1000 / 60, through || true); } CompensateInterval.prototype.start = CompensateInterval_start; CompensateInterval.create = CompensateInterval_create; this.CompensateInterval = CompensateInterval; }) (); //___________________________________________ var canvas = Graffiti.create (document.querySelector ('#hoge')); function hoge (y, c) { var scl = 300; console.log(c.toFixed(4) + " | "+ y +"|"+ (new Date() - time) + "ms"); canvas.pset (c*scl, scl-y*scl, [255, 0, 0]); } var time = new Date; var fuga = CompensateInterval.create (hoge, 1000, 'linear', 0, 4, 100, false); fuga.start(); </script>