Disegno di testo ruotato su una canvas HTML5

Parte di un’applicazione Web che sto sviluppando mi richiede di creare grafici a barre per visualizzare varie informazioni. Ho pensato che, se il browser dell’utente fosse in grado, li disegnerei usando l’elemento canvas HTML5. Non ho problemi a disegnare linee e barre per i miei grafici, ma quando si tratta di etichettare gli assi, le barre o le linee che ho incontrato in un intoppo. Come faccio a disegnare il testo ruotato su un elemento canvas in modo che si allinei con l’elemento che sta etichettando? Un paio di esempi includono:

  • Ruota il testo di 90 gradi in senso antiorario per etichettare l’asse y
  • Ruota il testo di 90 gradi in senso antiorario per etichettare le barre su un grafico a barre verticale
  • Ruota il testo di una quantità arbitraria per etichettare le linee su un grafico a linee

Qualsiasi suggerimento sarebbe apprezzato.

Come altri hanno già accennato, probabilmente vorrai cercare di riutilizzare una soluzione grafica esistente, ma girare il testo non è troppo difficile. Il bit un po ‘confuso (per me) è che si ruota tutto il contesto e poi si disegna su di esso:

ctx.rotate(Math.PI*2/(i*6)); 

L’ angolo è in radianti . Il codice è tratto da questo esempio , che credo sia stato realizzato per la parte relativa alle trasformazioni del tutorial su canvas MDC .

Si prega di vedere la risposta qui sotto per una soluzione più completa.

Pubblicare questo nel tentativo di aiutare gli altri con problemi simili. Ho risolto questo problema con un approccio in cinque fasi: salvare il contesto, tradurre il contesto, ruotare il contesto, disegnare il testo e ripristinare il contesto al suo stato salvato.

Penso alle traduzioni e le trasformo al contesto come a manipolare la griglia di coordinate sovrapposta alla canvas. Per impostazione predefinita, l’origine (0,0) inizia nell’angolo superiore sinistro dell’area di disegno. X aumenta da sinistra a destra, Y aumenta da cima a fondo. Se fai una “L” con l’indice e il pollice sulla mano sinistra e tieni la mano davanti a te con il pollice rivolto verso il basso, il pollice indicherà la direzione di aumentare Y e l’indice indicherà nella direzione di aumentare X. So che è elementare, ma lo trovo utile quando penso a traduzioni e rotazioni. Ecco perché:

Quando si traduce il contesto, si sposta l’origine della griglia delle coordinate in una nuova posizione nell’area di disegno. Quando ruoti il ​​contesto, pensa di ruotare la “L” che hai fatto con la mano sinistra in senso orario, la quantità indicata dall’angolo specificato in radianti sull’origine. Quando si tratti di testo o riempimento di testo, specificare le coordinate in relazione agli assi appena allineati. Per orientare il testo in modo che sia leggibile dal basso verso l’alto, dovresti tradurre in una posizione sotto la quale vuoi iniziare le etichette, ruotare di -90 gradi e riempire o tracciare il testo, sfalsando ogni etichetta lungo l’asse x ruotato. Qualcosa di simile dovrebbe funzionare:

  context.save(); context.translate(newx, newy); context.rotate(-Math.PI/2); context.textAlign = "center"; context.fillText("Your Label Here", labelXposition, 0); context.restore(); 

.restore () riporta il contesto allo stato che aveva quando hai chiamato .save () – utile per riportare le cose in “normale”.


Mentre questo è una sorta di follow-up della risposta precedente, aggiunge un po ‘(si spera).

Principalmente quello che voglio chiarire è che di solito pensiamo di disegnare cose come draw a rectangle at 10, 3 .

Quindi, se ci pensiamo in questo modo: move origin to 10, 3 , quindi draw rectangle at 0, 0 . Quindi tutto ciò che dobbiamo fare è aggiungere una rotazione in mezzo.

Un altro punto importante è l’allineamento del testo. È più semplice disegnare il testo a 0, 0, quindi utilizzare l’allineamento corretto può consentirci di farlo senza misurare la larghezza del testo.

Dovremmo comunque spostare il testo di un importo per ottenerlo centrato verticalmente, e sfortunatamente la canvas non ha un grande supporto per l’altezza della linea, quindi è una supposizione e controlla cosa (correggimi se c’è qualcosa di meglio).

Ho creato 3 esempi che forniscono un punto e un testo con 3 allineamenti, per mostrare quale sia il punto reale sullo schermo in cui il carattere andrà.

inserisci la descrizione dell'immagine qui

 var font, lineHeight, x, y; x = 100; y = 100; font = 20; lineHeight = 15; // this is guess and check as far as I know this.context.font = font + 'px Arial'; // Right Aligned this.context.save(); this.context.translate(x, y); this.context.rotate(-Math.PI / 4); this.context.textAlign = 'right'; this.context.fillText('right', 0, lineHeight / 2); this.context.restore(); this.context.fillStyle = 'red'; this.context.fillRect(x, y, 2, 2); // Center this.context.fillStyle = 'black'; x = 150; y = 100; this.context.save(); this.context.translate(x, y); this.context.rotate(-Math.PI / 4); this.context.textAlign = 'center'; this.context.fillText('center', 0, lineHeight / 2); this.context.restore(); this.context.fillStyle = 'red'; this.context.fillRect(x, y, 2, 2); // Left this.context.fillStyle = 'black'; x = 200; y = 100; this.context.save(); this.context.translate(x, y); this.context.rotate(-Math.PI / 4); this.context.textAlign = 'left'; this.context.fillText('left', 0, lineHeight / 2); this.context.restore(); this.context.fillStyle = 'red'; this.context.fillRect(x, y, 2, 2); 

La riga this.context.fillText('right', 0, lineHeight / 2); è fondamentalmente 0, 0 , tranne che ci spostiamo leggermente perché il testo sia centrato vicino al punto

Ecco un’alternativa HTML5 all’homebrew: http://www.rgraph.net/ Potresti essere in grado di decodificare i loro metodi ….

Potresti anche considerare qualcosa come Flot ( http://code.google.com/p/flot/ ) o GCharts: ( http://www.maxb.net/scripts/jgcharts/include/demo/#1 ) Non è abbastanza cool, ma completamente retrocompatibile e spaventoso facile da implementare.

Funkodebat ha pubblicato un’ottima soluzione a cui ho fatto riferimento più volte. Tuttavia, mi ritrovo a scrivere il mio modello di lavoro ogni volta che ne ho bisogno. Quindi, ecco il mio modello di lavoro … con una certa chiarezza aggiunta.

Prima di tutto, l’altezza del testo è uguale alla dimensione del carattere del pixel . Ora, questo era qualcosa che ho letto qualche tempo fa, e ha funzionato nei miei calcoli. Non sono sicuro che funzioni con tutti i tipi di carattere, ma sembra funzionare con Arial, sans-serif.

Inoltre, per assicurarti di inserire tutto il testo nella tua canvas (e non tagliare le code della tua “p”), devi impostare context.textBaseline * .

Vedrai nel codice che stiamo ruotando il testo sul suo centro. Per fare ciò, abbiamo bisogno di impostare context.textAlign = “center” e context.textBaseline in fondo, altrimenti tagliamo parti del nostro testo.

Perché ridimensionare la canvas? Di solito ho una canvas che non viene aggiunta alla pagina. Lo uso per disegnare tutto il mio testo ruotato, quindi lo disegno su un’altra canvas che visualizzo. Ad esempio, è ansible utilizzare questa area di disegno per disegnare tutte le etichette di un grafico (una per una) e disegnare la canvas nascosta sulla canvas del grafico in cui è necessario l’etichetta ( context.drawImage(hiddenCanvas, 0, 0); ).

NOTA IMPORTANTE: imposta il carattere prima di misurare il testo e applica nuovamente tutti gli stili al contesto dopo aver ridimensionato la canvas. Il contesto di una canvas viene completamente ripristinato quando il canvas viene ridimensionato.

Spero che questo ti aiuti!

 var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); var font, text, x, y; text = "Mississippi"; //Set font size before measuring font = 20; ctx.font = font + 'px Arial, sans-serif'; //Get width of text var metrics = ctx.measureText(text); //Set canvas dimensions c.width = font;//The height of the text. The text will be sideways. c.height = metrics.width;//The measured width of the text //After a canvas resize, the context is reset. Set the font size again ctx.font = font + 'px Arial'; //Set the drawing coordinates x = font/2; y = metrics.width/2; //Style ctx.fillStyle = 'black'; ctx.textAlign = 'center'; ctx.textBaseline = "bottom"; //Rotate the canvas and draw the text ctx.save(); ctx.translate(x, y); ctx.rotate(-Math.PI / 2); ctx.fillText(text, 0, font / 2); ctx.restore();