135 votes

Comment réparer un texte flou dans mon canevas HTML5 ?

Je suis un total n00b con HTML5 et je travaille avec le canvas pour rendre des formes, des couleurs et du texte. Dans mon application, j'ai un adaptateur visuel qui crée un canevas de façon dynamique et le remplit de contenu. Cela fonctionne très bien, sauf que mon texte est rendu de manière très floue/floue/étirée. J'ai vu beaucoup d'autres messages expliquant pourquoi la définition de l'option largeur y hauteur en CSS causera ce problème, mais je définis tout cela en javascript .

Le code correspondant (vue Violon ):

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);

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

Les résultats que je vois (en Safari ) sont beaucoup plus asymétriques que ce que montre le violon :

Mine

Rendered output in Safari

Violon

Rendered output on JSFiddle

Qu'est-ce que je fais de manière incorrecte ? Ai-je besoin d'un canevas distinct pour chaque élément de texte ? S'agit-il de la police ? Dois-je d'abord définir le canevas dans la mise en page HTML5 ? Y a-t-il une faute de frappe ? Je suis perdu.

202voto

MyNameIsKo Points 313

L'élément de toile fonctionne indépendamment du rapport de pixels de l'appareil ou de l'écran.

Sur l'iPad 3+, ce rapport est de 2. Cela signifie essentiellement que votre canevas de 1000px de largeur doit maintenant remplir 2000px pour correspondre à sa largeur indiquée sur l'écran de l'iPad. Heureusement pour nous, cette opération est effectuée automatiquement par le navigateur. D'un autre côté, c'est aussi la raison pour laquelle les images et les éléments de la toile qui ont été conçus pour s'adapter directement à leur zone visible sont moins bien définis. Comme votre canevas ne sait remplir que 1000px mais qu'on lui demande de dessiner à 2000px, le navigateur doit maintenant remplir intelligemment les espaces entre les pixels pour afficher l'élément à sa taille correcte.

Je vous recommande vivement de lire cet article de HTML5Rocks qui explique plus en détail comment créer des éléments en haute définition.

tl;dr ? Voici un exemple (basé sur le tutoriel ci-dessus) que j'utilise dans mes propres projets pour produire une toile avec la bonne résolution :

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);

J'espère que cela vous aidera !

37voto

Toastrackenigma Points 55

Bien que la réponse de @MonNomIsKo fonctionne toujours, elle est un peu dépassée maintenant en 2020, et peut être améliorée :

function createHiPPICanvas(w, h) {
    let ratio = window.devicePixelRatio;
    let cv = document.createElement("canvas");
    cv.width = w * ratio;
    cv.height = h * ratio;
    cv.style.width = w + "px";
    cv.style.height = h + "px";
    cv.getContext("2d").scale(ratio, ratio);
    return cv;
}

En général, nous apportons les améliorations suivantes :

  • Nous retirons le backingStorePixelRatio car elles ne sont pas vraiment implémentées dans les navigateurs de manière importante (en fait, seul Safari renvoie autre chose que des références undefined et cette version fonctionne toujours parfaitement dans Safari) ;
  • Nous remplaçons tout ce code de ratio par window.devicePixelRatio , qui bénéficie d'un soutien fantastique
  • Cela signifie également que nous déclarons une propriété globale de moins --- hourra !
  • Nous pouvons également supprimer le || 1 le repli sur window.devicePixelRatio car c'est inutile : tous les navigateurs qui ne prennent pas en charge cette propriété ne prennent pas en charge l'accès à l'Internet. .setTransform o .scale non plus, donc cette fonction ne fonctionnera pas sur eux, qu'il s'agisse d'une solution de repli ou non ;
  • Nous pouvons remplacer .setTransform por .scale Il n'est pas nécessaire d'utiliser une matrice de transformation, car la transmission d'une largeur et d'une hauteur est un peu plus intuitive que celle d'une matrice de transformation.
  • La fonction a été renommée de createHiDPICanvas a createHiPPICanvas . Comme @MyNameIsKo le mentionne dans les commentaires de sa réponse, DPI (Dots per Inch) est un terme d'impression (les imprimantes créent des images à partir de minuscules points d'encre colorée). Bien que similaire, les écrans affichent les images à l'aide de pixels, et en tant que tel, PPI (Pixels par pouce) est un meilleur acronyme pour notre cas d'utilisation.

Un grand avantage de ces simplifications est que cette réponse peut maintenant être utilisée en TypeScript sans // @ts-ignore (car TS n'a pas de types pour backingStorePixelRatio ).

33voto

Phil Points 11964

Résolu !

J'ai décidé de voir ce que changer le largeur y hauteur les attributs que j'ai définis dans javascript pour voir comment cela affectait la taille de la toile - et ce n'était pas le cas. Ça change la résolution.

Pour obtenir le résultat que je souhaitais, j'ai également dû définir le paramètre canvas.style.width qui modifie la taille physique de l'image. 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

16voto

1valdis Points 300

Essayez cette ligne de CSS sur votre toile : image-rendering: pixelated

Conformément à MDN :

Lors de la mise à l'échelle de l'image, l'algorithme du plus proche voisin doit être utilisé, de sorte que l'image semble être composée de grands pixels.

Il empêche donc entièrement l'anticrénelage.

8voto

Adam Mańkowski Points 191

Je redimensionne l'élément de la toile par le biais de css afin de prendre toute la largeur de l'élément parent. J'ai remarqué que la largeur et la hauteur de mon élément ne sont pas mises à l'échelle. Je cherche la meilleure façon de définir la taille qui devrait être

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

De cette manière simple, votre toile sera parfaitement réglée, quel que soit l'écran que vous utiliserez.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X