2013-06-13 14 views
20

Muszę przenieść mały prostokąt na ścieżce. Prostokąt przesuwa się po kliknięciu wewnątrz płótna.Chcę zrobić animację obiektu wzdłuż określonej ścieżki

Nie jestem w stanie animować go, ponieważ obiekt przeskakuje do wymaganego punktu.

Kod ten można znaleźć pod numerem Fiddle.

HTML

<canvas id="myCanvas" width=578 height=200></canvas> 

CSS

#myCanvas { 
    width:578px; 
    height:200px; 
    border:2px thin; 
} 

JavaScript

var myRectangle = { 
    x: 100, 
    y: 20, 
    width: 25, 
    height: 10, 
    borderWidth: 1 
}; 

$(document).ready(function() { 
    $('#myCanvas').css("border", "2px solid black"); 
    var canvas = document.getElementById('myCanvas'); 
    var context = canvas.getContext('2d'); 
    var cntxt = canvas.getContext('2d'); 
    drawPath(context); 
    drawRect(myRectangle, cntxt); 

    $('#myCanvas').click(function() { 
     function animate(myRectangle, canvas, cntxt, startTime) { 
      var time = (new Date()).getTime() - startTime; 
      var linearSpeed = 10; 
      var newX = Math.round(Math.sqrt((100 * 100) + (160 * 160))); 
      if (newX < canvas.width - myRectangle.width - myRectangle.borderWidth/2) { 

       myRectangle.x = newX; 

      } 


      context.clearRect(0, 0, canvas.width, canvas.height); 
      drawPath(context); 
      drawRect(myRectangle, cntxt); 

      // request new frame 
      requestAnimFrame(function() { 
       animate(myRectangle, canvas, cntxt, startTime); 
      }); 
     } 
     drawRect(myRectangle, cntxt); 
     myRectangle.x = 100; 
     myRectangle.y = 121; 
     setTimeout(function() { 
      var startTime = (new Date()).getTime(); 
      animate(myRectangle, canvas, cntxt, startTime); 
     }, 1000); 

    }); 
}); 

$(document).keypress(function (e) { 
    if (e.which == 13) { 


     $('#myCanvas').click(); 

    } 
}); 

function drawRect(myRectangle, cntxt) { 

    cntxt.beginPath(); 
    cntxt.rect(myRectangle.x, myRectangle.y, myRectangle.width, myRectangle.height); 
    cntxt.fillStyle = 'cyan'; 
    cntxt.fill(); 

    cntxt.strokeStyle = 'black'; 
    cntxt.stroke(); 
}; 

function drawPath(context) { 

    context.beginPath(); 
    context.moveTo(100, 20); 

    // line 1 
    context.lineTo(200, 160); 
    // quadratic curve 
    context.quadraticCurveTo(230, 200, 250, 120); 

    // bezier curve 
    context.bezierCurveTo(290, -40, 300, 200, 400, 150); 

    // line 2 
    context.lineTo(500, 90); 
    context.lineWidth = 5; 
    context.strokeStyle = 'blue'; 
    context.stroke(); 
}; 
+0

Jeśli chcesz uczynić go z ram ty można użyć silnika gry visual-js - Składnik ołówka: https://codepen.io/zlatnaspirala/full/jrzNko, jest wiele innych funkcji ... –

Odpowiedz

49

Oto jak przenieść obiekt wzdłuż określonej ścieżki

enter image description here

Animacja dotyczy ruchu w czasie. Więc dla każdej "klatki" twojej animacji musisz znać współrzędne XY gdzie narysować twój ruchomy obiekt (prostokąt).

Ten kod przyjmuje wartość procentową (od 0,00 do 1,00) i zwraca współrzędną XY, która jest tą wartością procentową wzdłuż segmentu ścieżki. Na przykład:

  • 0.00 zwróci XY na początku linii (lub krzywej).
  • 0.50 zwróci XY w środku linii (lub krzywej).
  • 1.00 spowoduje zwrócenie XY na końcu linii (lub krzywej).

Oto kod, aby uzyskać XY na określony procent wzdłuż linii:

// line: percent is 0-1 
function getLineXYatPercent(startPt,endPt,percent) { 
    var dx = endPt.x-startPt.x; 
    var dy = endPt.y-startPt.y; 
    var X = startPt.x + dx*percent; 
    var Y = startPt.y + dy*percent; 
    return({x:X,y:Y}); 
} 

Oto kod, aby uzyskać XY na określony procent wzdłuż krzywej Béziera drugiego stopnia:

// quadratic bezier: percent is 0-1 
function getQuadraticBezierXYatPercent(startPt,controlPt,endPt,percent) { 
    var x = Math.pow(1-percent,2) * startPt.x + 2 * (1-percent) * percent * controlPt.x + Math.pow(percent,2) * endPt.x; 
    var y = Math.pow(1-percent,2) * startPt.y + 2 * (1-percent) * percent * controlPt.y + Math.pow(percent,2) * endPt.y; 
    return({x:x,y:y}); 
} 

Oto kod, aby uzyskać XY w określonej procentowo wzdłuż krzywej Beziera: sześcienny

// cubic bezier percent is 0-1 
function getCubicBezierXYatPercent(startPt,controlPt1,controlPt2,endPt,percent){ 
    var x=CubicN(percent,startPt.x,controlPt1.x,controlPt2.x,endPt.x); 
    var y=CubicN(percent,startPt.y,controlPt1.y,controlPt2.y,endPt.y); 
    return({x:x,y:y}); 
} 

// cubic helper formula at percent distance 
function CubicN(pct, a,b,c,d) { 
    var t2 = pct * pct; 
    var t3 = t2 * pct; 
    return a + (-a * 3 + pct * (3 * a - a * pct)) * pct 
    + (3 * b + pct * (-6 * b + b * 3 * pct)) * pct 
    + (c * 3 - c * 3 * pct) * t2 
    + d * t3; 
} 

A oto jak je połączyć aby animować różne segmenty ścieżki

// calculate the XY where the tracking will be drawn 

if(pathPercent<25){ 
    var line1percent=pathPercent/24; 
    xy=getLineXYatPercent({x:100,y:20},{x:200,y:160},line1percent); 
} 
else if(pathPercent<50){ 
    var quadPercent=(pathPercent-25)/24 
    xy=getQuadraticBezierXYatPercent({x:200,y:160},{x:230,y:200},{x:250,y:120},quadPercent); 
} 
else if(pathPercent<75){ 
    var cubicPercent=(pathPercent-50)/24 
    xy=getCubicBezierXYatPercent({x:250,y:120},{x:290,y:-40},{x:300,y:200},{x:400,y:150},cubicPercent); 
} 
else { 
    var line2percent=(pathPercent-75)/25 
    xy=getLineXYatPercent({x:400,y:150},{x:500,y:90},line2percent); 
} 

// draw the tracking rectangle 
drawRect(xy); 

Oto kod roboczych i ryba: http://jsfiddle.net/m1erickson/LumMX/

<!doctype html> 
<html lang="en"> 
<head> 

    <style> 
     body{ background-color: ivory; } 
     canvas{border:1px solid red;} 
    </style> 

    <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" /> 
    <script src="http://code.jquery.com/jquery-1.9.1.js"></script> 
    <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script> 

    <script> 

    $(function() { 

     var canvas=document.getElementById("canvas"); 
     var ctx=canvas.getContext("2d"); 

     // set starting values 
     var fps = 60; 
     var percent=0 
     var direction=1; 

     // start the animation 
     animate(); 

     function animate() { 

      // set the animation position (0-100) 
      percent+=direction; 
      if(percent<0){ percent=0; direction=1; }; 
      if(percent>100){ percent=100; direction=-1; }; 

      draw(percent); 

      // request another frame 
      setTimeout(function() { 
       requestAnimationFrame(animate); 
      }, 1000/fps); 
     } 


     // draw the current frame based on sliderValue 
     function draw(sliderValue){ 

      // redraw path 
      ctx.clearRect(0,0,canvas.width,canvas.height); 
      ctx.lineWidth = 5; 

      ctx.beginPath(); 
      ctx.moveTo(100, 20); 
      ctx.lineTo(200, 160); 
      ctx.strokeStyle = 'red'; 
      ctx.stroke(); 

      ctx.beginPath(); 
      ctx.moveTo(200, 160); 
      ctx.quadraticCurveTo(230, 200, 250, 120); 
      ctx.strokeStyle = 'green'; 
      ctx.stroke(); 

      ctx.beginPath(); 
      ctx.moveTo(250,120); 
      ctx.bezierCurveTo(290, -40, 300, 200, 400, 150); 
      ctx.strokeStyle = 'blue'; 
      ctx.stroke(); 

      ctx.beginPath(); 
      ctx.moveTo(400, 150); 
      ctx.lineTo(500, 90); 
      ctx.strokeStyle = 'gold'; 
      ctx.stroke(); 

      // draw the tracking rectangle 
      var xy; 

      if(sliderValue<25){ 
       var percent=sliderValue/24; 
       xy=getLineXYatPercent({x:100,y:20},{x:200,y:160},percent); 
      } 
      else if(sliderValue<50){ 
       var percent=(sliderValue-25)/24 
       xy=getQuadraticBezierXYatPercent({x:200,y:160},{x:230,y:200},{x:250,y:120},percent); 
      } 
      else if(sliderValue<75){ 
       var percent=(sliderValue-50)/24 
       xy=getCubicBezierXYatPercent({x:250,y:120},{x:290,y:-40},{x:300,y:200},{x:400,y:150},percent); 
      } 
      else { 
       var percent=(sliderValue-75)/25 
       xy=getLineXYatPercent({x:400,y:150},{x:500,y:90},percent); 
      } 
      drawRect(xy,"red"); 

     } 


     // draw tracking rect at xy 
     function drawRect(point,color){ 
      ctx.fillStyle="cyan"; 
      ctx.strokeStyle="gray"; 
      ctx.lineWidth=3; 
      ctx.beginPath(); 
      ctx.rect(point.x-13,point.y-8,25,15); 
      ctx.fill(); 
      ctx.stroke(); 
     } 

     // draw tracking dot at xy 
     function drawDot(point,color){ 
      ctx.fillStyle=color; 
      ctx.strokeStyle="black"; 
      ctx.lineWidth=3; 
      ctx.beginPath(); 
      ctx.arc(point.x,point.y,8,0,Math.PI*2,false); 
      ctx.closePath(); 
      ctx.fill(); 
      ctx.stroke(); 
     } 

     // line: percent is 0-1 
     function getLineXYatPercent(startPt,endPt,percent) { 
      var dx = endPt.x-startPt.x; 
      var dy = endPt.y-startPt.y; 
      var X = startPt.x + dx*percent; 
      var Y = startPt.y + dy*percent; 
      return({x:X,y:Y}); 
     } 

     // quadratic bezier: percent is 0-1 
     function getQuadraticBezierXYatPercent(startPt,controlPt,endPt,percent) { 
      var x = Math.pow(1-percent,2) * startPt.x + 2 * (1-percent) * percent * controlPt.x + Math.pow(percent,2) * endPt.x; 
      var y = Math.pow(1-percent,2) * startPt.y + 2 * (1-percent) * percent * controlPt.y + Math.pow(percent,2) * endPt.y; 
      return({x:x,y:y}); 
     } 

     // cubic bezier percent is 0-1 
     function getCubicBezierXYatPercent(startPt,controlPt1,controlPt2,endPt,percent){ 
      var x=CubicN(percent,startPt.x,controlPt1.x,controlPt2.x,endPt.x); 
      var y=CubicN(percent,startPt.y,controlPt1.y,controlPt2.y,endPt.y); 
      return({x:x,y:y}); 
     } 

     // cubic helper formula at percent distance 
     function CubicN(pct, a,b,c,d) { 
      var t2 = pct * pct; 
      var t3 = t2 * pct; 
      return a + (-a * 3 + pct * (3 * a - a * pct)) * pct 
      + (3 * b + pct * (-6 * b + b * 3 * pct)) * pct 
      + (c * 3 - c * 3 * pct) * t2 
      + d * t3; 
     } 


    }); // end $(function(){}); 

    </script> 
</head> 
<body> 
    <canvas id="canvas" width=600 height=300></canvas> 
</body> 
</html> 
+0

ten kod nie działa na js fiddle. – Chandni

+0

OOPS! Zapomniałem o żądaniu Shimera animacji, który jest wymagany w firefoxie. Naprawiłem Fiddle'a i teraz powinno działać poprawnie. – markE

+0

dzięki! to działa dobrze – Chandni

16

Jeśli masz zamiar użyć wbudowanej Krzywe Beziera na płótnie, nadal musielibyście zrobić to samemu.

Możesz użyć tej implementacji cardinal spline i mieć wszystkie wyliczone punkty dla ciebie obliczone.

Przykład użycia jest to trochę kiełbasa-mobile poruszający się wzdłuż stoku (generowane z powyższym kardynał spline):

Slope demo

Full demo here (cut-and-copy jak należy).

Główną rzeczą, której potrzebujesz, to gdy masz tablicę punktów, aby znaleźć dwa punkty, których chcesz użyć do obiektu. To daje nam kąt obiektu:

cPoints = quantX(pointsFromCardinalSpline); //see below 

//get points from array (dx = current array position) 
x1 = cPoints[dx]; 
y1 = cPoints[dx + 1]; 

//get end-points from array (dlt=length, must be an even number) 
x2 = cPoints[dx + dlt]; 
y2 = cPoints[dx + dlt + 1]; 

Aby uniknąć sięgania po bardziej stromych zboczach możemy przeliczyć długość w oparciu o kąt. Aby uzyskać przybliżoną kąt używamy pierwotnego punktu końcowego, aby uzyskać kąt, a następnie obliczamy nową długość linii w zależności od długości chciał i ten kąt:

var dg = getLineAngle(x1, y1, x2, y2); 
var l = ((((lineToAngle(x1, y2, dlt, dg).x - x1)/2) |0) * 2); 

x2 = cPoints[dx + l]; 
y2 = cPoints[dx + l + 1]; 

Teraz możemy wykreślić „samochód” i nachylenie poprzez odjęcie jego pionowej wysokości od pozycji y.

To, co można zauważyć, polega na tym, że "samochód" porusza się ze zmienną prędkością. Wynika to z interpolacji kardynalnego splajnu.

Możemy go wygładzić, aby prędkość wyglądała bardziej równomiernie przez kwantyzację osi X. Wciąż nie będzie to idealne, ponieważ na stromych zboczach odległość y pomiędzy punktami będzie większa niż na płaskiej powierzchni - potrzebowalibyśmy kwantyzacji kwadratowej, ale w tym celu robimy tylko oś X.

To daje nam nową tablicę z nowych punktów dla każdego x-position:

function quantX(pts) { 

    var min = 99999999, 
     max = -99999999, 
     x, y, i, p = pts.length, 
     res = []; 

    //find min and max of x axis 
    for (i = 0; i < pts.length - 1; i += 2) { 
     if (pts[i] > max) max = pts[i]; 
     if (pts[i] < min) min = pts[i]; 
    } 
    max = max - min; 

    //this will quantize non-existng points 
    function _getY(x) { 

     var t = p, 
      ptX1, ptX2, ptY1, ptY2, f, y; 

     for (; t >= 0; t -= 2) { 
      ptX1 = pts[t]; 
      ptY1 = pts[t + 1]; 

      if (x >= ptX1) { 
       //p = t + 2; 

       ptX2 = pts[t + 2]; 
       ptY2 = pts[t + 3]; 

       f = (ptY2 - ptY1)/(ptX2 - ptX1); 
       y = (ptX1 - x) * f; 

       return ptY1 - y; 
      } 
     } 
    } 

    //generate new array per-pixel on the x-axis 
    //note: will not work if curve suddenly goes backwards 
    for (i = 0; i < max; i++) { 
     res.push(i); 
     res.push(_getY(i)); 
    } 
    return res; 
} 

Dwie pozostałe funkcje czego potrzebujemy to jeden obliczenie kąta dla linii, a jeden obliczania punktów końcowych w oparciu od kąta i długości:

function getLineAngle(x1, y1, x2, y2) { 
    var dx = x2 - x1, 
     dy = y2 - y1, 
     th = Math.atan2(dy, dx); 

    return th * 180/Math.PI; 
} 

function lineToAngle(x1, y1, length, angle) { 

    angle *= Math.PI/180; 

    var x2 = x1 + length * Math.cos(angle), 
     y2 = y1 + length * Math.sin(angle); 

    return {x: x2, y: y2}; 
} 
+0

+1 Podoba mi się rotacja kątowa samochodu. – markE

+0

Jak zrealizować to w java? –

Powiązane problemy