Je suis un total n00b avec HTML5
et travaille avec le canvas
pour rendre les formes, les couleurs et le texte. Dans mon application, j'ai un adaptateur de vue qui crée un canevas dynamiquement et le remplit de contenu. Cela fonctionne vraiment bien, sauf que mon texte est rendu très flou / flou / étiré. J'ai vu beaucoup d'autres messages sur pourquoi la définition de la largeur et la hauteur en CSS
causera cette question, mais je définir tout en javascript
.
Le code correspondant (voir Fiddle ):
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 (dans Safari ) sont beaucoup plus biaisés que ceux indiqués dans le violon:
Mien
Violon
Qu'est-ce que je fais mal? Ai-je besoin d'un canevas distinct pour chaque élément de texte? Est-ce 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.
clearRect
.Réponses:
L'élément canvas s'exécute indépendamment du ratio de pixels du périphérique ou du moniteur.
Sur l'iPad 3+, ce rapport est de 2. Cela signifie essentiellement que votre toile de 1000px de largeur devrait maintenant remplir 2000px pour correspondre à sa largeur indiquée sur l'écran de l'iPad. Heureusement pour nous, cela se fait automatiquement par le navigateur. D'un autre côté, c'est aussi la raison pour laquelle vous voyez moins de définition sur les images et les éléments de canevas conçus pour s'adapter directement à leur zone visible. Étant donné que votre canevas ne sait remplir que 1000 pixels mais qu'il est demandé de dessiner à 2000 pixels, le navigateur doit maintenant remplir intelligemment les espaces entre les pixels pour afficher l'élément à sa taille appropriée.
Je vous recommande vivement de lire cet article de HTML5Rocks qui explique plus en détail comment créer des éléments haute définition.
tl; dr? Voici un exemple (basé sur le tut ci-dessus) que j'utilise dans mes propres projets pour cracher une toile avec la résolution appropriée:
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 t'aides!
la source
Résolu!
J'ai décidé de voir ce que je définissais en modifiant les attributs de largeur et de hauteur
javascript
pour voir comment cela affectait la taille du canevas - et ce n'est pas le cas. Cela change la résolution.Pour obtenir le résultat que je souhaitais, j'ai également dû définir l'
canvas.style.width
attribut, qui modifie la taille physique ducanvas
: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
la source
window.devicePixelRatio
, et il est bien implémenté dans la plupart des navigateurs modernes.Je redimensionne l'élément canvas via css pour 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 cherchais la meilleure façon de définir la taille qui devrait être.
De cette manière simple, votre toile sera parfaitement définie, quel que soit l'écran que vous utiliserez.
la source
Cela a résolu le problème à 100% pour moi:
var canvas = document.getElementById('canvas'); canvas.width = canvas.getBoundingClientRect().width; canvas.height = canvas.getBoundingClientRect().height;
(c'est proche de la solution d'Adam Mańkowski).
la source
J'ai légèrement adapté le code MyNameIsKo sous canvg (SVG to Canvas js library). J'étais confus pendant un moment et j'y ai passé du temps. J'espère que cela aidera quelqu'un.
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); }
la source
J'ai remarqué un détail non mentionné dans les autres réponses. La résolution du canevas se tronque en valeurs entières.
Les dimensions de résolution par défaut du canevas sont
canvas.width: 300
etcanvas.height: 150
.Sur mon écran,
window.devicePixelRatio: 1.75
.Ainsi, lorsque je définis,
canvas.height = 1.75 * 150
la valeur est tronquée de la valeur souhaitée262.5
à262
.Une solution consiste à choisir les dimensions de mise en page CSS pour un
window.devicePixelRatio
élément donné de telle sorte que la troncature ne se produise pas lors de la mise à l'échelle de la résolution.Par exemple, je pourrais utiliser
width: 300px
etheight: 152px
qui donnerait des nombres entiers multipliés par1.75
.Edit : Une autre solution consiste à tirer parti du fait que les pixels CSS peuvent être fractionnaires pour contrer la troncature des pixels du canevas.
Vous trouverez ci-dessous une démonstration utilisant cette stratégie.
Edit : Voici le violon de l'OP mis à jour pour utiliser cette stratégie: http://jsfiddle.net/65maD/83/ .
main(); // Rerun on window resize. window.addEventListener('resize', main); function main() { // Prepare canvas with properly scaled dimensions. scaleCanvas(); // Test scaling calculations by rendering some text. testRender(); } function scaleCanvas() { const container = document.querySelector('#container'); const canvas = document.querySelector('#canvas'); // Get desired dimensions for canvas from container. let {width, height} = container.getBoundingClientRect(); // Get pixel ratio. const dpr = window.devicePixelRatio; // (Optional) Report the dpr. document.querySelector('#dpr').innerHTML = dpr.toFixed(4); // Size the canvas a bit bigger than desired. // Use exaggeration = 0 in real code. const exaggeration = 20; width = Math.ceil (width * dpr + exaggeration); height = Math.ceil (height * dpr + exaggeration); // Set the canvas resolution dimensions (integer values). canvas.width = width; canvas.height = height; /*----------------------------------------------------------- - KEY STEP - Set the canvas layout dimensions with respect to the canvas resolution dimensions. (Not necessarily integer values!) -----------------------------------------------------------*/ canvas.style.width = `${width / dpr}px`; canvas.style.height = `${height / dpr}px`; // Adjust canvas coordinates to use CSS pixel coordinates. const ctx = canvas.getContext('2d'); ctx.scale(dpr, dpr); } function testRender() { const canvas = document.querySelector('#canvas'); const ctx = canvas.getContext('2d'); // fontBaseline is the location of the baseline of the serif font // written as a fraction of line-height and calculated from the top // of the line downwards. (Measured by trial and error.) const fontBaseline = 0.83; // Start at the top of the box. let baseline = 0; // 50px font text ctx.font = `50px serif`; ctx.fillText("Hello World", 0, baseline + fontBaseline * 50); baseline += 50; // 25px font text ctx.font = `25px serif`; ctx.fillText("Hello World", 0, baseline + fontBaseline * 25); baseline += 25; // 12.5px font text ctx.font = `12.5px serif`; ctx.fillText("Hello World", 0, baseline + fontBaseline * 12.5); }
/* HTML is red */ #container { background-color: red; position: relative; /* Setting a border will mess up scaling calculations. */ /* Hide canvas overflow (if any) in real code. */ /* overflow: hidden; */ } /* Canvas is green */ #canvas { background-color: rgba(0,255,0,.8); animation: 2s ease-in-out infinite alternate both comparison; } /* animate to compare HTML and Canvas renderings */ @keyframes comparison { 33% {opacity:1; transform: translate(0,0);} 100% {opacity:.7; transform: translate(7.5%,15%);} } /* hover to pause */ #canvas:hover, #container:hover > #canvas { animation-play-state: paused; } /* click to translate Canvas by (1px, 1px) */ #canvas:active { transform: translate(1px,1px) !important; animation: none; } /* HTML text */ .text { position: absolute; color: white; } .text:nth-child(1) { top: 0px; font-size: 50px; line-height: 50px; } .text:nth-child(2) { top: 50px; font-size: 25px; line-height: 25px; } .text:nth-child(3) { top: 75px; font-size: 12.5px; line-height: 12.5px; }
<!-- Make the desired dimensions strange to guarantee truncation. --> <div id="container" style="width: 313.235px; height: 157.122px"> <!-- Render text in HTML. --> <div class="text">Hello World</div> <div class="text">Hello World</div> <div class="text">Hello World</div> <!-- Render text in Canvas. --> <canvas id="canvas"></canvas> </div> <!-- Interaction instructions. --> <p>Hover to pause the animation.<br> Click to translate the green box by (1px, 1px).</p> <!-- Color key. --> <p><em style="color:red">red</em> = HTML rendered<br> <em style="color:green">green</em> = Canvas rendered</p> <!-- Report pixel ratio. --> <p>Device pixel ratio: <code id="dpr"></code> <em>(physical pixels per CSS pixel)</em></p> <!-- Info. --> <p>Zoom your browser to re-run the scaling calculations. (<code>Ctrl+</code> or <code>Ctrl-</code>)</p>
la source
Pour ceux d'entre vous qui travaillent dans reactjs, j'ai adapté la réponse de MyNameIsKo et cela fonctionne très bien. Voici le code.
import React from 'react' export default class CanvasComponent extends React.Component { constructor(props) { this.calcRatio = this.calcRatio.bind(this); } // Use componentDidMount to draw on the canvas componentDidMount() { this.updateChart(); } calcRatio() { let 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; } // Draw on the canvas updateChart() { // Adjust resolution const ratio = this.calcRatio(); this.canvas.width = this.props.width * ratio; this.canvas.height = this.props.height * ratio; this.canvas.style.width = this.props.width + "px"; this.canvas.style.height = this.props.height + "px"; this.canvas.getContext("2d").setTransform(ratio, 0, 0, ratio, 0, 0); const ctx = this.canvas.getContext('2d'); // now use ctx to draw on the canvas } render() { return ( <canvas ref={el=>this.canvas=el} width={this.props.width} height {this.props.height}/> ) } }
Dans cet exemple, je passe la largeur et la hauteur du canevas comme accessoires.
la source
Essayez cette ligne de CSS sur votre canevas:
image-rendering: pixelated
Selon MDN :
Ainsi, il empêche complètement l'anti-crénelage.
la source
Pour moi, seule une combinaison de différentes techniques de `` pixel parfait '' a permis d'archiver les résultats:
Obtenez et mettez à l'échelle avec un rapport de pixels comme @MyNameIsKo suggéré.
pixelRatio = window.devicePixelRatio / ctx.backingStorePixelRatio
Mettez le canevas à l'échelle lors du redimensionnement (évitez la mise à l'échelle par défaut du canevas).
multiple de lineWidth avec pixelRatio pour trouver l'épaisseur de ligne de pixel `` réelle '' appropriée:
context.lineWidth = épaisseur * pixelRatio;
Vérifiez si l'épaisseur de la ligne est impaire ou paire. ajoutez la moitié de pixelRatio à la position de la ligne pour les valeurs d'épaisseur impaires.
x = x + pixelRatio / 2;
La ligne impaire sera placée au milieu du pixel. La ligne ci-dessus sert à le déplacer un peu.
function getPixelRatio(context) { dpr = window.devicePixelRatio || 1, bsr = context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1; return dpr / bsr; } var canvas = document.getElementById('canvas'); var context = canvas.getContext("2d"); var pixelRatio = getPixelRatio(context); var initialWidth = canvas.clientWidth * pixelRatio; var initialHeight = canvas.clientHeight * pixelRatio; window.addEventListener('resize', function(args) { rescale(); redraw(); }, false); function rescale() { var width = initialWidth * pixelRatio; var height = initialHeight * pixelRatio; if (width != context.canvas.width) context.canvas.width = width; if (height != context.canvas.height) context.canvas.height = height; context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0); } function pixelPerfectLine(x) { context.save(); context.beginPath(); thickness = 1; // Multiple your stroke thickness by a pixel ratio! context.lineWidth = thickness * pixelRatio; context.strokeStyle = "Black"; context.moveTo(getSharpPixel(thickness, x), getSharpPixel(thickness, 0)); context.lineTo(getSharpPixel(thickness, x), getSharpPixel(thickness, 200)); context.stroke(); context.restore(); } function pixelPerfectRectangle(x, y, w, h, thickness, useDash) { context.save(); // Pixel perfect rectange: context.beginPath(); // Multiple your stroke thickness by a pixel ratio! context.lineWidth = thickness * pixelRatio; context.strokeStyle = "Red"; if (useDash) { context.setLineDash([4]); } // use sharp x,y and integer w,h! context.strokeRect( getSharpPixel(thickness, x), getSharpPixel(thickness, y), Math.floor(w), Math.floor(h)); context.restore(); } function redraw() { context.clearRect(0, 0, canvas.width, canvas.height); pixelPerfectLine(50); pixelPerfectLine(120); pixelPerfectLine(122); pixelPerfectLine(130); pixelPerfectLine(132); pixelPerfectRectangle(); pixelPerfectRectangle(10, 11, 200.3, 443.2, 1, false); pixelPerfectRectangle(41, 42, 150.3, 443.2, 1, true); pixelPerfectRectangle(102, 100, 150.3, 243.2, 2, true); } function getSharpPixel(thickness, pos) { if (thickness % 2 == 0) { return pos; } return pos + pixelRatio / 2; } rescale(); redraw();
canvas { image-rendering: -moz-crisp-edges; image-rendering: -webkit-crisp-edges; image-rendering: pixelated; image-rendering: crisp-edges; width: 100vh; height: 100vh; }
<canvas id="canvas"></canvas>
L'événement de redimensionnement n'est pas déclenché dans le snipped afin que vous puissiez essayer le fichier sur le github
la source
Pour moi, ce n'était pas seulement l'image, mais le texte était de mauvaise qualité. La solution de travail multi-navigateurs la plus simple pour les écrans Retina / non-Retina consistait à rendre l'image deux fois plus grande que prévu et à redimensionner le contexte du canevas comme ce gars l'a suggéré: https://stackoverflow.com/a/53921030/4837965
la source