Предисловие
В свободное время решил заняться интересным делом. В голову пришла идея написать небольшой фреймворк для canvas (хоть и велосипед, но тоже интересно). Дело дошло до кривых Безье.
Решил как-то приукрасить встроенное решение, но у него есть один недостаток. Проблема в том, что функции квадратичной и кубической кривой выдают уже готовый результат, не подлежащий редактированию. Мне же требовалось обрисовывать кривую постепенно или обрисовать только часть кривой, не говоря уже о том, что хотелось обрисовывать кривые большего порядка, нежели 2-3 степени.
Начал гуглить. Нашел интересную статью на Хабре. Но мои знания математики так и не позволили преобразовать её к своим нуждам, к тому же там используются таймеры, из-за которых остановить анимацию вовремя не получится.
Начинаю писать с нуля.
Все оказалось не так уж и страшно. Суть алгоритма де Костельжо сама по себе проста, дак зачем же использовать столь мудреную формулу? Вот я и не буду.
Вкратце расскажу алгоритм, а потом пример кода.
Функция, рассчитывающая кривую, возвращает только точку по смещению (то бишь по расстоянию от начала кривой в процентах). Постепенно увеличивая смещение получаем все новые и новые точки. Когда захотите можно и остановить алгоритм, красота! И не нужна куча таймеров, всего один setInteval который постепенно запросит и обрисует точки.
Функция getPointOnCurve рекурсивно вычисляет отрезки из переданных ей точек.
Вычислив, по смещению находит новые точки на этих отрезках, которые передает дальше самой себе. Продолжая до тех пор, пока не будет найдена точка на кривой, которую она вернет.
//все нужные функции хранятся в объекте formula
formula.getPointOnCurve = function(shift,points){
if(points.length == 2){
return this.getPointOnLine(shift,points);
}
var pointsPP = [];
for(var i = 1;i < points.length;i++){
//функция getPointOnLine вычисляет точку на отрезке, по смещению. она рассмотрена ниже
pointsPP.push(this.getPointOnLine(shift,[
points[i - 1],
points[i]
]));
}
return this.getPointOnCurve(shift,pointsPP);
};
Вспомогательная функция getPointOnLine:
formula.getPointOnLine = function(shift,points){
//подразумевается, что в points передан массив вида: [[x0,y0],[x1,y1]]
return [
(points[1][0] - points[0][0]) * (shift / 100) + points[0][0],
(points[1][1] - points[0][1]) * (shift / 100) + points[0][1]
];
};
А вот и сама отрисовка:
//создаем массив с контрольными точками
var points = [
[10,50],
[40,-40],
[190,180],
[40,-60],
[80,130],
[10,50]
];//так выглядит лепесток
var shift = 0;
var step = 1;//переменная shift будет изменяться с шагом step
var timer = setInterval(function(){
context.beginPath();
context.moveTo(points[0][0],points[0][1]);
if(shift > 101){
shift = 101;
}
for(var i = 0;i <= shift;i += step){
var coord = formula.getPointOnCurve(i,points);
context.lineTo(coord[0],coord[1]);
}
context.closePath();
if(shift <= 100){
shift += step;
}
},fps)
Ну и, конечно же, пример.
Надеюсь, кому-нибудь помог, так как сам долго не мог разобраться.
Автор: takovoy