Skip to content

Instantly share code, notes, and snippets.

@GZShi
Last active December 20, 2015 19:19
Show Gist options
  • Save GZShi/6182705 to your computer and use it in GitHub Desktop.
Save GZShi/6182705 to your computer and use it in GitHub Desktop.
绘制贝塞尔曲线
(function(){
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
var mouse = {
x: 0,
y: 0,
fx: 0,
fy: 0,
left: false,
down: false
};
function factorial(n) {
if(n == 0)
return 1;
return n < 0 ? n * factorial(n + 1) : n * factorial(n-1);
}
// 二项式系数
function bc(n, k) {
if(n < k || k < 0)
return 1;
else
return factorial(n)/Math.imul(factorial(k), factorial(n-k));
}
function point(x, y) {
return {x: x, y: y};
}
var Bezier = (function(){
function Bezier() {
this.p = [];
this.delta = 100;
this.pointColor = 'rgba(210, 210, 200, 0.6)';
this.lineColor = 'rgb(180, 34, 22)';
this.selected = -1;
}
Bezier.prototype.drawPoint = function(ctx) {
if(this.p.length < 1)
return ;
ctx.fillStyle = this.pointColor;
for(var i = 0, len = this.p.length; i < len; ++i) {
ctx.beginPath();
ctx.arc(this.p[i].x, this.p[i].y, 5, 0, Math.PI*2, true);
ctx.closePath();
ctx.fill();
}
ctx.beginPath();
ctx.strokeStyle = 'gray';
ctx.moveTo(this.p[0].x, this.p[0].y);
for(var i = 0, len = this.p.length; i < len; ++i) {
ctx.lineTo(this.p[i].x, this.p[i].y);
}
ctx.stroke();
}
Bezier.prototype.drawLine = function(ctx) {
if(this.p.length < 2)
return ;
var cx = this.p[0].x;
var cy = this.p[0].y;
var n = this.p.length;
var temp = 0;
var t = 0;
ctx.strokeStyle = this.lineColor;
ctx.beginPath();
ctx.moveTo(cx, cy);
var a = [];
for(var i = 0; i <= this.delta; ++i) {
cx = cy = 0;
t = i / this.delta;
for(var j = 0; j < n; ++j) {
temp = bc(n-1, j) * Math.pow(1-t, n-j-1) * Math.pow(t, j);
cx += temp * this.p[j].x;
cy += temp * this.p[j].y;
}
ctx.lineTo(cx, cy);
}
ctx.stroke();
}
Bezier.prototype.draw = function(ctx) {
this.drawPoint(ctx);
this.drawLine(ctx);
}
Bezier.prototype.addPoint = function(x, y) {
this.p.push(point(x, y));
};
Bezier.prototype.update = function(fx, fy, x, y) {
if(this.selected == -1) {
for(var i = 0, len = this.p.length; i < len; ++i) {
if(Math.abs(fx - this.p[i].x) < 5 && Math.abs(fy - this.p[i].y) < 3) {
this.selected = i;
break;
}
}
return;
}
this.p[this.selected].x = x;
this.p[this.selected].y = y;
};
return Bezier;
})();
var b = new Bezier();
canvas.oncontextmenu = function(event) { event.preventDefault();}
canvas.onmousedown = function(event){
mouse.x = mouse.fx = event.offsetX;
mouse.y = mouse.fy = event.offsetY;
if(event.button == 0) {
mouse.left = true;
mouse.down = true;
}
else if(event.button == 2)
b.addPoint(event.offsetX, event.offsetY);
event.preventDefault();
};
canvas.onmousemove = function(evnet) {
if(mouse.down) {
mouse.x = evnet.offsetX;
mouse.y = event.offsetY;
}
event.preventDefault();
};
canvas.onmouseup = function(event) {
mouse.x = mouse.fx = -100;
mouse.y = mouse.fy = -100;
mouse.down = false;
event.preventDefault();
}
function animate(ctx, bezier, mouse) {
var id = -1;
function frame() {
ctx.clearRect(0, 0, 2000, 2000);
ctx.fillStyle = 'rgba(220, 220, 220, 0.6)';
for(var i = 0; i < 80; ++i) {
ctx.fillRect(i*20, 0, 1, 1000);
ctx.fillRect(0, i*20, 1000, 1);
}
ctx.fill();
ctx.fillStyle = 'gray';
ctx.fillText("left click and drag to move point", 10, 20);
ctx.fillText("right click to create a new point",10, 40);
bezier.draw(ctx);
ctx.beginPath();
if(mouse.down) {
if(mouse.left) {
bezier.update(mouse.fx, mouse.fy, mouse.x, mouse.y);
}
} else {
bezier.selected = -1;
}
if(false) {
clearInterval(id);
id = -1;
}
}
id = setInterval(frame, 1000/60);
}
animate(ctx, b, mouse);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment