<button id="random">Мне повезёт!</button>
<button id="clear">Очистить</button>
<canvas id="myCanvas"></canvas>
body,html{
height:100%;
}
var canvas = document.querySelector("#myCanvas");
var ctx = canvas.getContext("2d");
var ctxW,ctxH,currentAnimId = 0;
canvas.width = ctxW = document.body.offsetWidth;
canvas.height = ctxH =document.body.offsetHeight;
var dots = [];
document.querySelector("#clear").addEventListener("click",()=>{
dots = [];
runAnimation();
});
document.querySelector("#random").addEventListener("click",()=>{//Рандомно расставить точки
var rand = (min,max)=>{ return Math.floor(min + max *Math.random())};
var n = rand(5,10);
dots = [];
while(n--)
dots.push([rand(0,canvas.width),rand(0,canvas.height)]);
runAnimation();
});
document.querySelector("#random").dispatchEvent(new Event("click"));
canvas.addEventListener("mousedown",function(e){
dots.push([e.offsetX,e.offsetY]);
runAnimation();
});
function runAnimation(){
currentAnimId++;
let animId = currentAnimId;
var speedAnim = 10;
let iTo = 0,tTo = 0;
(function anim(){
var step = draw(iTo,tTo,speedAnim);
tTo+=step;
if(tTo>1){
tTo = 0;
iTo++;
}
if(iTo >= dots.length - 1){
draw(dots.length - 2,1,speedAnim);
return;
}
if(currentAnimId == animId)
setTimeout(anim,1000/60);
})();
}
function draw(iTo, tTo,speed){
var resultStep = 1;
ctx.clearRect(0,0,ctxW,ctxH);
ctx.fillText("Кликай по канвасу",10,10)
ctx.lineWidth = 2;
for(var i = 0; i < dots.length;i++){
ctx.fillStyle = i>=iTo+1 && !(iTo == dots.length-2 && tTo == 1)?"red":"blue";
ctx.beginPath();
ctx.arc(dots[i][0],dots[i][1],10,0,Math.PI*2);
ctx.closePath();
ctx.fill();
}
if(dots.length >= 2){
for(var i = 0; i <= iTo;i++){
var inxLast = i > 0?i-1:0;//Индекс предыдущей точки в массиве точек
var inxNext = i < dots.length - 2? i+2 :i+1; //Индекс следующей точки в массиве точек
//Вычисляем промежуточные точки между двумя точками
var control = calcControlPoints(dots[inxLast][0],dots[inxLast][1],
dots[i][0],dots[i][1],
dots[i+1][0],dots[i+1][1],
dots[inxNext][0],dots[inxNext][1]);
resultStep = (1/bezierCurveLength(
dots[i][0],dots[i][1],
control.cx1,control.cy1,
control.cx2,control.cy2,
dots[i+1][0],dots[i+1][1])) * speed;
var TmpTTo = i == iTo?tTo:1;
let t = 0;
let lastDot = null;
let isBreak = false;
while(true){
var tmp = getCordOnCurve(t,dots[i][0],dots[i][1],control.cx1,control.cy1,
control.cx2,control.cy2,
dots[i+1][0],dots[i+1][1]);
if(lastDot)
ctx.lineTo(tmp.x,tmp.y);
else
ctx.moveTo(tmp.x,tmp.y);
lastDot = tmp;
if(isBreak)break;
t+=resultStep;
if(t > TmpTTo){
t = TmpTTo;
isBreak = true;
}
}
}
ctx.stroke();
}
return resultStep;
}
draw();
//Вычисляем точки на кривой безье по формуле 4х точеченой кривой
//P = (1−t)3P1 + 3(1−t)2tP2 +3(1−t)t2P3 + t3P4
//Подробнее https://learn.javascript.ru/bezier-curve
function getCordOnCurve(t,x0,y0,x1,y1,x2,y2,x3,y3){//t[0-1]
var x = Math.pow(1-t,3)*x0 + 3* Math.pow(1-t,2)*t*x1 + 3 * (1-t) * Math.pow(t,2)* x2 + Math.pow(t,3)*x3;
var y = Math.pow(1-t,3)*y0 + 3* Math.pow(1-t,2)*t*y1 + 3 * (1-t) * Math.pow(t,2)* y2 + Math.pow(t,3)*y3;
return {x:x,y:y};
}
function calcControlPoints(x0,y0,x1,y1,x2,y2,x3,y3){
//Вычисляем промежуточные точки между двумя точками, по методу Maxim Shemanarev Interpolation with Bezier Curves
//Подробнее http://agg.sourceforge.net/antigrain.com/research/bezier_interpolation/index.html
var xc1 = (x0 + x1) / 2.0;
var yc1 = (y0 + y1) / 2.0;
var xc2 = (x1 + x2) / 2.0;
var yc2 = (y1 + y2) / 2.0;
var xc3 = (x2 + x3) / 2.0;
var yc3 = (y2 + y3) / 2.0;
var len1 = Math.sqrt((x1-x0) * (x1-x0) + (y1-y0) * (y1-y0));
var len2 = Math.sqrt((x2-x1) * (x2-x1) + (y2-y1) * (y2-y1));
var len3 = Math.sqrt((x3-x2) * (x3-x2) + (y3-y2) * (y3-y2));
var k1 = len1 / (len1 + len2);
var k2 = len2 / (len2 + len3);
var xm1 = xc1 + (xc2 - xc1) * k1;
var ym1 = yc1 + (yc2 - yc1) * k1;
var xm2 = xc2 + (xc3 - xc2) * k2;
var ym2 = yc2 + (yc3 - yc2) * k2;
var smooth_value = 1;//КОЭФИЦИЕНТ СГЛАЖИВАНИЯ ОТ 0 ДО 1
ctrl1_x = xm1 + (xc2 - xm1) * smooth_value + x1 - xm1;
ctrl1_y = ym1 + (yc2 - ym1) * smooth_value + y1 - ym1;
ctrl2_x = xm2 + (xc2 - xm2) * smooth_value + x2 - xm2;
ctrl2_y = ym2 + (yc2 - ym2) * smooth_value + y2 - ym2;
return {cx1:ctrl1_x, cy1: ctrl1_y, cx2:ctrl2_x, cy2:ctrl2_y};
}
//Получение длинны кривой
function bezierCurveLength(x1, y1, x2, y2, x3, y3, x4, y4) {
const n = 100; // Количество сегментов для интегрирования
let length = 0;
let px = x1;
let py = y1;
for (let i = 1; i <= n; i++) {
const t = i / n;
const cx = (3 * x2 - 3 * x1) * t * t * t + (3 * x1 - 6 * x2 + 3 * x3) * t * t + (3 * x2 - 3 * x3) * t + x4 - x1;
const cy = (3 * y2 - 3 * y1) * t * t * t + (3 * y1 - 6 * y2 + 3 * y3) * t * t + (3 * y2 - 3 * y3) * t + y4 - y1;
length += Math.sqrt((cx - px) * (cx - px) + (cy - py) * (cy - py));
px = cx;
py = cy;
}
return length;
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.