2013-03-27 14 views
56

Sono un totale n00b con HTML5 e sto lavorando con lo canvas per il rendering di forme, colori e testo. Nella mia app, ho un adattatore della vista che crea una tela in modo dinamico e la riempie di contenuto. Funziona molto bene, tranne che il mio testo è reso molto sfocato/sfocato/allungato. Ho visto molti altri post sul perché la definizione della larghezza e l'altezza in CSS causeranno questo problema, ma io definisco tutto in javascript.Come posso correggere il testo sfocato nel mio canvas HTML5?

Il relativo codice (vista Fiddle):

HTML

<div id="layout-content"></div> 

Javascript

var width = 500;//FIXME:size.w; 
var height = 500;//FIXME:size.h; 

var canvas = document.createElement("canvas"); 
//canvas.className="singleUserCanvas"; 
canvas.width=width; 
canvas.height=height; 
canvas.border = "3px solid #999999"; 
canvas.bgcolor = "#999999"; 
canvas.margin = "(0, 2%, 0, 2%)"; 

var context = canvas.getContext("2d"); 

////////////////// 
//// SHAPES //// 
////////////////// 

var left = 0; 

//draw zone 1 rect 
context.fillStyle = "#8bacbe"; 
context.fillRect(0, (canvas.height*5/6)+1, canvas.width*1.5/8.5, canvas.height*1/6); 

left = left + canvas.width*1.5/8.5; 

//draw zone 2 rect 
context.fillStyle = "#ffe381"; 
context.fillRect(left+1, (canvas.height*5/6)+1, canvas.width*2.75/8.5, canvas.height*1/6); 

left = left + canvas.width*2.75/8.5 + 1; 

//draw zone 3 rect 
context.fillStyle = "#fbbd36"; 
context.fillRect(left+1, (canvas.height*5/6)+1, canvas.width*1.25/8.5, canvas.height*1/6); 

left = left + canvas.width*1.25/8.5; 

//draw target zone rect 
context.fillStyle = "#004880"; 
context.fillRect(left+1, (canvas.height*5/6)+1, canvas.width*0.25/8.5, canvas.height*1/6); 

left = left + canvas.width*0.25/8.5; 

//draw zone 4 rect 
context.fillStyle = "#f8961d"; 
context.fillRect(left+1, (canvas.height*5/6)+1, canvas.width*1.25/8.5, canvas.height*1/6); 

left = left + canvas.width*1.25/8.5 + 1; 

//draw zone 5 rect 
context.fillStyle = "#8a1002"; 
context.fillRect(left+1, (canvas.height*5/6)+1, canvas.width-left, canvas.height*1/6); 

//////////////// 
//// TEXT //// 
//////////////// 

//user name 
context.fillStyle = "black"; 
context.font = "bold 18px sans-serif"; 
context.textAlign = 'right'; 
context.fillText("User Name", canvas.width, canvas.height*.05); 

//AT: 
context.font = "bold 12px sans-serif"; 
context.fillText("AT: 140", canvas.width, canvas.height*.1); 

//AB: 
context.fillText("AB: 94", canvas.width, canvas.height*.15); 

//this part is done after the callback from the view adapter, but is relevant here to add the view back into the layout. 
var parent = document.getElementById("layout-content"); 
parent.appendChild(canvas); 

I risultati sto vedendo (in Safari) sono molto più distorto rispetto a quello mostrato nel Fid dle:

miniera

Rendered output in Safari

Fiddle

Rendered output on JSFiddle

Che cosa sto facendo in modo errato? Ho bisogno di una tela separata per ogni elemento di testo? È il carattere? Devo prima definire la tela nel layout HTML5? C'è un errore di battitura? Mi sono perso.

+0

Sembra che tu non chiami "clearRect". – David

+1

Questo polyfill corregge le operazioni di base sulla tela con i browser HiDPI che non si aggiornano automaticamente (al momento safari è l'unico) ... https://github.com/jondavidjohn/hidpi-canvas-polyfill – jondavidjohn

risposta

97

L'elemento tela corre indipendente dal dispositivo o rapporto pixel del monitor.

Su iPad 3+, questo rapporto è 2. Ciò significa in sostanza che il tuo canvas con larghezza di 1000 px ora dovrà riempire 2000 px per adattarlo alla larghezza dichiarata sul display dell'iPad. Fortunatamente per noi, questo viene fatto automaticamente dal browser. D'altra parte, questo è anche il motivo per cui vedi meno definizione su immagini e elementi di tela che sono stati fatti per adattarsi direttamente alla loro area visibile. Poiché la tua tela sa solo come riempire 1000px ma gli viene chiesto di disegnare a 2000px, il browser deve ora compilare in modo intelligente gli spazi vuoti tra i pixel per visualizzare l'elemento alle sue dimensioni appropriate.

consiglio vivamente di leggere this article da HTML5Rocks che spiega più in dettaglio come creare elementi ad alta definizione.

tl; dr? Ecco un esempio (sulla base del tut sopra) che uso nei miei progetti per sputare fuori una tela con la giusta risoluzione:

var PIXEL_RATIO = (function() { 
    var ctx = document.createElement("canvas").getContext("2d"), 
     dpr = window.devicePixelRatio || 1, 
     bsr = ctx.webkitBackingStorePixelRatio || 
       ctx.mozBackingStorePixelRatio || 
       ctx.msBackingStorePixelRatio || 
       ctx.oBackingStorePixelRatio || 
       ctx.backingStorePixelRatio || 1; 

    return dpr/bsr; 
})(); 


createHiDPICanvas = function(w, h, ratio) { 
    if (!ratio) { ratio = PIXEL_RATIO; } 
    var can = document.createElement("canvas"); 
    can.width = w * ratio; 
    can.height = h * ratio; 
    can.style.width = w + "px"; 
    can.style.height = h + "px"; 
    can.getContext("2d").setTransform(ratio, 0, 0, ratio, 0, 0); 
    return can; 
} 

//Create canvas with the device resolution. 
var myCanvas = createHiDPICanvas(500, 250); 

//Create canvas with a custom resolution. 
var myCustomCanvas = createHiDPICanvas(500, 200, 4); 

Spero che questo aiuti!

+0

Questo ha funzionato alla grande! Grazie per l'aiuto. – Phil

+1

Ho pensato che valesse la pena menzionare che il mio metodo createHiDPI() è in qualche modo poco conosciuto. DPI è un termine per la sola stampa e PPI è un acronimo più corretto in quanto i monitor visualizzano le immagini usando pixel anziché punti. – MyNameIsKo

+2

Si noti che questo rapporto può effettivamente cambiare durante la vita della pagina. Ad esempio, se trascino una finestra di Chrome da un vecchio monitor esterno "standard" res a una schermata di retina incorporata di un macbook, il codice calcolerà un rapporto diverso. Solo una FYI se prevedi di mettere in cache questo valore. (esterno era il rapporto 1, retina schermo 2 nel caso fosse curioso) – Aardvark

19

Risolto!

ho deciso di vedere cosa cambiare l'altezza larghezza e attributi ho impostato in javascript per vedere come che ha colpito le dimensioni della tela - e non ha fatto. Cambia la risoluzione.

Per ottenere il risultato che volevo, avevano anche impostare l'attributo canvas.style.width, che cambia la dimensione fisica del canvas:

canvas.width=1000;//horizontal resolution (?) - increase for better looking text 
canvas.height=500;//vertical resolution (?) - increase for better looking text 
canvas.style.width=width;//actual width of canvas 
canvas.style.height=height;//actual height of canvas 
+1

Per risultati ideali non dovresti tocca gli attributi di stile della tela e controlla solo la dimensione con gli attributi di larghezza e altezza della tela stessa. In questo modo ti assicuri che un pixel sulla tela sia uguale a un pixel sullo schermo. – Philipp

+5

Non sono d'accordo. La modifica degli attributi style.width/height è esattamente come si crea un canvas HiDPI. – MyNameIsKo

+2

Nella tua risposta, imposta canvas.width a 1000 e canvas.style.width a metà a 500. Funziona ma solo per un dispositivo con un rapporto pixel di 2. Per qualsiasi cosa al di sotto di questo, come il tuo monitor desktop, il canvas è ora attingendo a pixel non necessari. Per percentuali più alte sei ora di nuovo a posto dove hai iniziato con un asset/elemento sfocato a bassa ris. Un altro aspetto a cui Philipp sembrava alludere è che tutto ciò che si disegna nel proprio contesto deve ora essere attirato sulla larghezza/altezza raddoppiata, anche se viene visualizzato a metà di tale valore. La soluzione a questo è di impostare il contesto della tela per raddoppiare. – MyNameIsKo

2

Ridimensiono elemento canvas tramite css per occupare tutta la larghezza dell'elemento padre. Ho notato che la larghezza e l'altezza del mio elemento non sono ridimensionate. Stavo cercando il modo migliore per impostare la dimensione che dovrebbe essere.

canvas.width = canvas.clientWidth; 
canvas.height = canvas.clientHeight; 

Questo modo semplice di impostare la tua tela perfettamente, non importa quale schermata si utilizzerà.

1

Ho adattato leggermente il codice MyNameIsKo sotto canvg (libreria jg di SVG to Canvas). Sono stato confuso per un po 'e ho passato un po' di tempo per questo. Spero che questo aiuti qualcuno.

HTML

<div id="chart"><canvas></canvas><svg>Your SVG here</svg></div> 

Javascript

window.onload = function() { 

var PIXEL_RATIO = (function() { 
    var ctx = document.createElement("canvas").getContext("2d"), 
     dpr = window.devicePixelRatio || 1, 
     bsr = ctx.webkitBackingStorePixelRatio || 
       ctx.mozBackingStorePixelRatio || 
       ctx.msBackingStorePixelRatio || 
       ctx.oBackingStorePixelRatio || 
       ctx.backingStorePixelRatio || 1; 

    return dpr/bsr; 
})(); 

setHiDPICanvas = function(canvas, w, h, ratio) { 
    if (!ratio) { ratio = PIXEL_RATIO; } 
    var can = canvas; 
    can.width = w * ratio; 
    can.height = h * ratio; 
    can.style.width = w + "px"; 
    can.style.height = h + "px"; 
    can.getContext("2d").setTransform(ratio, 0, 0, ratio, 0, 0); 
} 

var svg = document.querySelector('#chart svg'), 
    canvas = document.querySelector('#chart canvas'); 

var svgSize = svg.getBoundingClientRect(); 
var w = svgSize.width, h = svgSize.height; 
setHiDPICanvas(canvas, w, h); 

var svgString = (new XMLSerializer).serializeToString(svg); 
var ctx = canvas.getContext('2d'); 
ctx.drawSvg(svgString, 0, 0, w, h); 

} 
0

Questo 100% risolto per me:

var canvas = document.getElementById('canvas'); 
canvas.width = canvas.getBoundingClientRect().width; 
canvas.height = canvas.getBoundingClientRect().height; 

(è vicino alla soluzione di Adam Mankowski).