Skip to content

Instantly share code, notes, and snippets.

@dested
Created November 19, 2016 21:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dested/83adfc1afc684a7ed65b86ea01259661 to your computer and use it in GitHub Desktop.
Save dested/83adfc1afc684a7ed65b86ea01259661 to your computer and use it in GitHub Desktop.
<html>
<head>
<title>Bezier</title>
</head>
<body>
<canvas id="canvas" width="1500" height="1200"></canvas>
<input type="range" value="0.5" oninput="updateBezier(this.value)" min="0" max="1" step="0.005" style="position:absolute; width:98%; left:0; top:0;" />
<script type="text/javascript">
function Point(x, y) {
this.x = x;
this.y = y;
}
function Line(p1, p2) {
this.Point1 = p1;
this.Point2 = p2;
}
Line.prototype.lerp = function(f) {
return new Point(lerp(this.Point1.x, this.Point2.x, f), lerp(this.Point1.y, this.Point2.y, f));
}
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var circleRadius = 10;
var rangeStep = 0.005;
var scale = 2;
var currentAnimateOffset = 0.5;
var p1 = new Point(250, 300);
var p2 = new Point(350, 300);
var p1Handle = new Point(100, 50);
var p2Handle = new Point(500, 50);
var bezierPoints = [];
var l1, l2, l3;
function calculateBezier() {
l1 = new Line(p1, p1Handle);
l2 = new Line(p1Handle, p2Handle);
l3 = new Line(p2Handle, p2);
bezierPoints = [];
for (var f = 0; f <= 1; f += rangeStep) {
bezierPoints.push(calcBezierLerp(f));
};
}
function calcBezierLerp(f) {
var pl1 = l1.lerp(f);
var pl2 = l2.lerp(f);
var pl3 = l3.lerp(f);
var bpl1 = new Line(pl1, pl2);
var bpl2 = new Line(pl2, pl3);
var cpl1 = bpl1.lerp(f);
var cpl2 = bpl2.lerp(f);
var dpl = new Line(cpl1, cpl2);
var p = dpl.lerp(f);
return {
pl1: pl1,
pl2: pl2,
pl3: pl3,
bpl1: bpl1,
bpl2: bpl2,
cpl1: cpl1,
cpl2: cpl2,
dpl: dpl,
p: p
};
}
function draw() {
canvas.width = canvas.width;
context.save();
context.scale(scale, scale)
context.save();
context.lineWidth = 5;
context.beginPath();
context.moveTo(bezierPoints[0].p.x, bezierPoints[0].p.y);
for (var i = 1; i < bezierPoints.length; i++) {
context.lineTo(bezierPoints[i].p.x, bezierPoints[i].p.y);
};
context.stroke();
context.restore();
context.save();
context.beginPath();
context.moveTo(l1.Point1.x, l1.Point1.y);
context.lineTo(l1.Point2.x, l1.Point2.y);
context.stroke();
context.restore();
context.save();
context.setLineDash([4, 4])
context.beginPath();
context.moveTo(l2.Point1.x, l2.Point1.y);
context.lineTo(l2.Point2.x, l2.Point2.y);
context.stroke();
context.restore();
context.save();
context.beginPath();
context.moveTo(l3.Point1.x, l3.Point1.y);
context.lineTo(l3.Point2.x, l3.Point2.y);
context.stroke();
context.restore();
context.save();
drawCircle(p1Handle, circleRadius, 5);
drawCircle(p2Handle, circleRadius, 5);
drawCircle(p1, circleRadius, 5);
drawCircle(p2, circleRadius, 5);
context.restore();
context.restore();
drawLerp(currentAnimateOffset);
}
function drawLine(point1, point2, width) {
context.save();
context.lineWidth = width;
context.beginPath();
context.moveTo(point1.x, point1.y);
context.lineTo(point2.x, point2.y);
context.stroke();
context.restore();
}
function drawLerp(f) {
context.save();
context.scale(scale, scale);
var bp = bezierPoints[(1 / rangeStep * f) | 0];
drawLine(bp.pl1, bp.pl2, 1);
drawLine(bp.pl2, bp.pl3, 1);
drawCircle(bp.pl1, 5, 3);
drawCircle(bp.pl2, 5, 3);
drawCircle(bp.pl3, 5, 3);
drawLine(bp.cpl1, bp.cpl2, 1);
drawCircle(bp.cpl1, 3, 2);
drawCircle(bp.cpl2, 3, 2);
drawCircle(bp.p, 6, 1, 'red');
context.restore();
}
function drawCircle(point, radius, lineWidth, color) {
var centerX = point.x;
var centerY = point.y;
color = color || 'green';
context.save();
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = color;
context.fill();
context.lineWidth = lineWidth;
context.strokeStyle = '#003300';
context.stroke();
context.restore();
}
function lerp(a, b, f) {
return a + f * (b - a);
}
function pointInCircle(point, centerOfCircle, radius) {
var x = point.x;
var y = point.y;
var centerX = centerOfCircle.x;
var centerY = centerOfCircle.y;
return Math.pow((x - centerX), 2) + Math.pow((y - centerY), 2) <= Math.pow(radius, 2);
}
var selectPoint;
canvas.onmousedown = function(e) {
var ePoint = new Point(e.x / scale, e.y / scale);
if (pointInCircle(ePoint, p1, circleRadius)) {
selectPoint = p1;
} else if (pointInCircle(ePoint, p2, circleRadius)) {
selectPoint = p2;
} else if (pointInCircle(ePoint, p1Handle, circleRadius)) {
selectPoint = p1Handle;
} else if (pointInCircle(ePoint, p2Handle, circleRadius)) {
selectPoint = p2Handle;
}
}
canvas.onmouseup = function(e) {
selectPoint = undefined;
}
canvas.onmousemove = function(e) {
if (!selectPoint) return;
var ePoint = new Point(e.x / scale, e.y / scale);
selectPoint.x = ePoint.x;
selectPoint.y = ePoint.y;
calculateBezier();
draw();
return false;
}
function updateBezier(f) {
currentAnimateOffset = f;
draw();
}
calculateBezier();
draw();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment