Disegna il percorso della canvas HTML5 / Javascript nel tempo

Diciamo che ho un percorso:

var context = canvas.getContext('2d'); context.beginPath(); context.moveTo(100, 20); context.lineTo(200, 160); context.quadraticCurveTo(230, 200, 250, 120); context.bezierCurveTo(290, -40, 300, 200, 400, 150); context.lineTo(500, 90); context.lineWidth = 5; context.strokeStyle = 'blue'; context.stroke(); 

Questo stampa il percorso tutto in una volta:

Percorso reso

Come posso dividere il percorso in sottocavi di una determinata lunghezza? Ad esempio: context.splitCurrentPathIntoSubPath(0, 0.75) dovrebbe restituire solo i primi 3/4 del percorso.

Mi piacerebbe usarlo per realizzare un’animazione. Se c’è un metodo più semplice, è anche il benvenuto.

Mi sono imbattuto in un problema simile durante l’animazione di archi SVG con D3.js. La mia soluzione prende in prestito da questo. Non è il più intuitivo, ma è comunemente usato nelle animazioni di D3. Richiede un’attenta impostazione dell’offset del cruscotto e della lunghezza della linea. Trucchi CSS fornisce una buona spiegazione della tecnica qui che consiglio vivamente di leggere prima di visualizzare il mio codice .

Ho modificato il JSFiddle sopra con questa tecnica implementata per la tua linea qui . Nota che funzionerà anche se la linea torna su se stessa.

Una nota sulla lunghezza della linea:

Questa implementazione richiede che tu conosca la lunghezza approssimativa della tua linea in modo che tu possa impostare la lunghezza var come maggiore di essa. Per le curve bezier e quadratic questo è difficile ma può comunque essere fatto ( questa domanda SO sembra promettente ). Per la mia demo ho usato prove ed errori per scoprire che il tuo era di circa 608px. Impostando la lunghezza su 10000 probabilmente assicurerai che le tue linee siano sempre disegnate correttamente, ma al costo di avere un numero infinito di chiamate inutili chiamate ogni millisecondo. La linea di fondo è: se ti interessano le prestazioni, scopri le cose di formula più bezier; se non lo fai, imposta questa variabile in alto.

Codice:

HTML

   webgl couldn't be started   

JavaScript

 canvasHolder = document.getElementById( 'canvas' ); context = canvasHolder.getContext('2d'); context.fillStyle = 'white'; var w = canvasHolder.width, h = canvasHolder.height; context.fillRect( 0, 0, w, h); //set the direction the line draws in //1->ltr | -1->rtl var dir = -1; //IMPORTANT: this must be set to greater than the length //of the line var length = 608; //the speed of the line draw var speed = 1; var progress = 0; var lineInterval; //Go! context.globalCompositeOperation='copy'; drawLine(); function drawLine() { //this clears itself once the line is drawn lineInterval = setInterval(updateLine, 1); } function updateLine() { //define the line defineLine(); if(progressleft var dir = dir || -1 context.setLineDash([length]); context.lineDashOffset = dir*(frac+length); context.stroke(); } 

Questa è la mia soluzione, in pratica disegna un rettangolo sopra il tuo percorso, quindi ogni aggiornamento di fotogramma sposta il rettangolo di 1 posizione X, quindi lentamente la casella verrà spostata sopra e lontano dal percorso e sembrerà che stai disegnando un percorso animato,

L’ho salvato su jsfiddle per te 🙂 ed ecco il codice stand alone

 window.addEventListener( "load", firstLoaded, false); then = Date.now(); setInterval(main, 1); // Execute as fast as possible var cube_x_position = 0; function main() { context.beginPath(); context.moveTo(100, 20); context.lineTo(200, 160); context.quadraticCurveTo(230, 200, 250, 120); context.bezierCurveTo(290, -40, 300, 200, 400, 150); context.lineTo(500, 90); context.lineWidth = 5; context.strokeStyle = 'blue'; context.stroke(); context.fillRect(cube_x_position, 0, canvasHolder.width, canvasHolder.height); if(cube_x_position < canvasHolder.width) { cube_x_position += 1; } } function firstLoaded() { canvasHolder = document.getElementById( 'canvas' ); context = canvasHolder.getContext('2d'); context.fillStyle = "#AAAAAA"; context.fillRect( 0, 0, 500, 500); } 

Una dimostrazione che disegna un percorso complesso utilizzando punti uniformsmente distanziati:

http://jsfiddle.net/m1erickson/2fodu9pa/

Una panoramica di Velocità uniforms

“Velocità” è definita come distanza per unità di tempo.

“Velocità uniforms” percorre quindi una distanza specifica costante per unità di tempo.

Quindi, spostarsi lungo il percorso a 2 pixel per 1/60 di secondo sarebbe un esempio di movimento a una velocità uniforms.

Per percorrere 2 pixel devi calcolare un punto lungo il tuo percorso che è 2 pixel dal tuo ultimo punto.

Disegnare in modo incrementale un percorso che contiene linee e curve a una velocità uniforms richiede centinaia di piccoli calcoli.

Ecco come determinare una serie di punti che sono distanziati in modo uniforms lungo il percorso:

  • Dividi il tuo percorso nei loro segmenti: linea, curva quadratica, curva di Bézier, linea.

  • Calcola molti (oltre 300) punti lungo ciascun segmento usando la formula matematica che definisce ogni segmento (vedi le formule sotto) e metti quei punti in una matrice.

  • Camminare sequenzialmente lungo ciascun punto e calcolare la distanza tra i punti (vedere la formula sotto).

  • Mantenere un totale della distanza accumulata percorsa lungo i punti.

  • Quando il punto corrente percorso raggiunge la lunghezza specificata, salvare quel punto in un secondo array.

Quindi per animare il percorso in modo incrementale è ansible creare un ciclo di animazione che disegna una linea per ogni successivo punto nel secondo array.

Nota: se si mantiene la distanza specificata sufficientemente piccola (ad es. 1-2 pixel), le linee disegnate appaiono curve se necessario.

Ecco la Formula che supporta questo metodo:

Calcola punti lungo la linea:

 // T is an interval between 0.00 and 1.00 // To divide a Line into 300 parts you would call the function 300 times // with T increasing 1.00/300 each time function getLineXYatPercent(startPt,endPt,T) { var dx = endPt.x-startPt.x; var dy = endPt.y-startPt.y; var X = startPt.x + dx*T; var Y = startPt.y + dy*T; return( {x:X,y:Y} ); } 

Calcola punti lungo la curva quadratica:

 // T is an interval between 0.00 and 1.00 // To divide a Quadratic Curve into 300 parts you would call the function 300 times // with T increasing 1.00/300 each time function getQuadraticBezierXYatT(startPt,controlPt,endPt,T) { var x = Math.pow(1-T,2) * startPt.x + 2 * (1-T) * T * controlPt.x + Math.pow(T,2) * endPt.x; var y = Math.pow(1-T,2) * startPt.y + 2 * (1-T) * T * controlPt.y + Math.pow(T,2) * endPt.y; return( {x:x,y:y} ); } 

Calcola punti lungo la curva di Bezier:

 // T is an interval between 0.00 and 1.00 // To divide a BezierCurve into 300 parts you would call the function 300 times // with T increasing 1.00/300 each time function getCubicBezierXYatT(startPt,controlPt1,controlPt2,endPt,T){ var x=CubicN(T,startPt.x,controlPt1.x,controlPt2.x,endPt.x); var y=CubicN(T,startPt.y,controlPt1.y,controlPt2.y,endPt.y); return({x:x,y:y}); } // cubic helper formula at T distance function CubicN(T, a,b,c,d) { var t2 = T * T; var t3 = t2 * T; return a + (-a * 3 + T * (3 * a - a * T)) * T + (3 * b + T * (-6 * b + b * 3 * T)) * T + (c * 3 - c * 3 * T) * t2 + d * t3; } 

Distanza tra 2 punti:

 var dx=point2.x-point1.x; var dy=point2.y-point1.y; var distance=Math.sqrt(dx*dx+dy*dy); 

Buona fortuna con il vostro progetto!