GNU/Linux >> Tutoriels Linux >  >> Linux

Comment utiliser dans un canevas un élément de texte avec une police décrite en CSS

Si vous souhaitez simplement restituer le texte de votre span dans un canevas, vous pouvez accéder aux attributs de style à l'aide de la fonction window.getComputedStyle. Pour rendre la plage d'origine invisible, définissez son style sur display: none .

// get the span element
const span = document.getElementsByClassName('bmcl_evalprompt')[0];

// get the relevant style properties
const font = window.getComputedStyle(span).font;
const color = window.getComputedStyle(span).color;

// get the element's text (if necessary)
const text = span.innerHTML;

// get the canvas element
const canvas = document.getElementById('canvas');

// set the canvas styling
const ctx = canvas.getContext('2d');
ctx.font = font;
ctx.fillStyle = color;

// print the span's content with correct styling
ctx.fillText(text, 35, 110);
#canvas {
  width: 300px;
  height: 200px;
  background: lightgrey;
}

span.bmcl_evalprompt {
  display: none;           // makes the span invisible
  font-family: monospace;  // change this value to see the difference
  font-size: 32px;         // change this value to see the difference
  color: rebeccapurple;    // change this value to see the difference
}
<span class="bmcl_evalprompt">Hello World!</span>
<canvas id="canvas" width="300" height="200"></canvas>

Correspond à la police DOM dans le canevas ?

La réponse simple est :"C'est trop dur !" et "Ce ne sera jamais parfait."

Le mieux que vous puissiez faire est une approximation qui se trouve dans l'exemple au bas de la réponse qui montrera également que la correspondance du style visible n'est pas liée à la qualité visible.

Étendre uniquement les règles CSS.

Si vous souhaitez que la police corresponde le plus possible à l'élément, il existe des problèmes supplémentaires au-delà de la simple obtention du CSS, comme indiqué dans la réponse de Spark Fountain.

Taille de la police et taille des pixels CSS

  • La taille de la police est liée à la taille des pixels CSS. L'élément HTMLCanvasElement

  • La taille des pixels CSS ne correspond pas toujours aux pixels d'affichage de l'appareil. Par exemple, les écrans HiDPI/Retina. Vous pouvez accéder au ratio de pixels CSS de l'appareil via devicePixelRatio

  • La taille des pixels CSS n'est pas une constante et peut changer pour de nombreuses raisons. Les modifications peuvent être surveillées via MediaQueryListEvent et écouter le change événement

  • Les éléments peuvent être transformés. Le CanvasRenderingContext2D ne peut pas faire de transformations 3D donc si l'élément ou le canevas a une transformation 3D, vous ne pourrez pas faire correspondre la police rendue du canevas avec la police rendue des éléments.

  • La résolution du canevas et la taille d'affichage sont indépendantes.

    • Vous pouvez obtenir la résolution du canevas via les propriétés HTMLCanvasElement.width , et HTMLCanvasElement.height
    • Vous pouvez obtenir la taille d'affichage du canevas via les propriétés de style largeur et hauteur, ou via une variété d'autres méthodes voir exemple.
    • L'aspect du pixel du canevas peut ne pas correspondre à l'aspect du pixel CSS et doit être calculé lors du rendu de la police sur le canevas.
    • Le rendu des polices Canvas avec de petites tailles de police est terrible. Par exemple, une police de 4px dont la taille est de 16px est illisible. ctx.font = "4px arial"; ctx.scale(4,4); ctx.fillText("Hello pixels"); Vous devez utiliser une taille de police fixe qui offre des résultats de rendu de canevas de bonne qualité et réduire le rendu lors de l'utilisation de petites polices.

Couleur de la police

Le style de couleur des éléments ne représente que la couleur rendue. Il ne représente pas la couleur réelle vue par l'utilisateur.

Comme cela s'applique à la fois au canevas et à l'élément à partir duquel vous obtenez la couleur et à tous les éléments superposés ou inférieurs, la quantité de travail nécessaire pour faire correspondre visuellement la couleur est énorme et bien au-delà de la portée d'une réponse de débordement de pile (les réponses ont un longueur maximale de 30 Ko)

Rendu des polices

Le moteur de rendu des polices du canevas est différent de celui du DOM. Le DOM peut utiliser une variété de techniques de rendu pour améliorer la qualité apparente des polices en tirant parti de la façon dont les sous-pixels RVB physiques des périphériques sont disposés. Par exemple, les polices TrueType et les conseils associés utilisés par le moteur de rendu, et le sous-pixel ClearType dérivé avec le rendu des conseils.

Ces méthodes de rendu des polices PEUVENT être mises en correspondance sur le canevas, mais pour une correspondance en temps réel, vous devrez utiliser WebGL.

Le problème est que le rendu des polices DOM est déterminé par de nombreux facteurs, y compris les paramètres des navigateurs. JavaScript ne peut accéder à aucune des informations nécessaires pour déterminer le rendu de la police. Au mieux, vous pouvez faire une supposition éclairée.

Autres complications

Il existe également d'autres facteurs qui affectent la police et la relation entre les règles de style de police CSS et le résultat visuel de la police affichée. Par exemple, les unités CSS, l'animation, l'alignement, la direction, les transformations de police et le mode Quirks.

Personnellement pour le rendu et la couleur je ne m'embête pas. Même si j'ai écrit un moteur de polices complet utilisant WebGL pour correspondre à chaque variante de police, de filtrage, de composition et de rendu, ils ne font pas partie de la norme et sont donc sujets à changement sans préavis. Le projet serait ainsi toujours ouvert et pourrait à tout moment échouer au niveau de résultats illisibles. Cela ne vaut tout simplement pas la peine.

Exemple

L'exemple a un canevas de rendu sur la gauche. Le texte et la police sont centrés en haut. Une vue agrandie à droite, qui montre une vue agrandie du canevas de gauche

Le premier style utilisé est celui des pages par défaut. La résolution du canevas est de 300 x 150, mais mise à l'échelle pour s'adapter à 500 x 500 pixels CSS. Cela se traduit par un texte de toile de très mauvaise qualité. Faire défiler la résolution du canevas montrera comment la résolution du canevas affecte la qualité.

Les fonctions

  • drawText(text, x, y, fontCSS, sizeCSSpx, colorStyleCSS) dessine le texte en utilisant les valeurs des propriétés CSS. Mise à l'échelle de la police pour qu'elle corresponde le plus possible à la taille visuelle et au rapport hauteur/largeur du DOM.

  • getFontStyle(element) renvoie les styles de police nécessaires sous forme d'objet à partir de element

Utilisation de l'interface utilisateur

  • CLIQUEZ sur la police centrale pour faire défiler les styles de police.

  • CLIQUEZ sur le canevas gauche pour faire défiler les résolutions du canevas.

  • En bas se trouvent les paramètres utilisés pour rendre le texte dans le canevas.

Vous verrez que la qualité du texte dépend de la résolution du canevas.

Pour voir comment le zoom DOM affecte le rendu, vous devez effectuer un zoom avant ou arrière sur la page. Les écrans HiDPI et Retina auront un texte de canevas de qualité bien inférieure en raison du fait que le canevas est la moitié de la résolution des pixels CSS.

const ZOOM_SIZE = 16;
canvas1.width = ZOOM_SIZE;
canvas1.height = ZOOM_SIZE;
const ctx = canvas.getContext("2d");
const ctx1 = canvas1.getContext("2d");
const mouse = {x:0, y:0};
const CANVAS_FONT_BASE_SIZE = 32; // the size used to render the canvas font.
const TEXT_ROWS = 12;
var currentFontClass = 0;
const fontClasses = "fontA,fontB,fontC,fontD".split(",");

const canvasResolutions = [[canvas.scrollWidth, canvas.scrollHeight],[300,150],[200,600],[600,600],[1200,1200],[canvas.scrollWidth * devicePixelRatio, canvas.scrollHeight * devicePixelRatio]];
var currentCanvasRes = canvasResolutions.length - 1;
var updateText = true;
var updating = false;
setTimeout(updateDisplay, 0, true);

function drawText(text, x, y, fontCSS, sizeCSSpx, colorStyleCSS) { // Using px as the CSS size unit
    ctx.save();
    
    // Set canvas state to default
    ctx.globalAlpha = 1;
    ctx.filter = "none";
    ctx.globalCompositeOperation = "source-over";
    
    const pxSize = Number(sizeCSSpx.toString().trim().replace(/[a-z]/gi,"")) * devicePixelRatio;
    const canvasDisplayWidthCSSpx = ctx.canvas.scrollWidth; // these are integers
    const canvasDisplayHeightCSSpx = ctx.canvas.scrollHeight;
    
    const canvasResWidth = ctx.canvas.width;
    const canvasResHeight = ctx.canvas.height;
    
    const scaleX = canvasResWidth / (canvasDisplayWidthCSSpx * devicePixelRatio);
    const scaleY = canvasResHeight / (canvasDisplayHeightCSSpx * devicePixelRatio);
    const fontScale = pxSize / CANVAS_FONT_BASE_SIZE
    
    ctx.setTransform(scaleX * fontScale, 0, 0, scaleY * fontScale, x, y); // scale and position rendering
    
    ctx.font = CANVAS_FONT_BASE_SIZE + "px " + fontCSS;
    ctx.textBaseline = "hanging";
    ctx.fillStyle = colorStyleCSS;
    ctx.fillText(text, 0, 0);
    
    ctx.restore();
}
    
function getFontStyle(element) {
    const style = getComputedStyle(element);    
    const color = style.color;
    const family = style.fontFamily;
    const size = style.fontSize;    
    styleView.textContent = `Family: ${family} Size: ${size} Color: ${color} Canvas Resolution: ${canvas.width}px by ${canvas.height}px Canvas CSS size 500px by 500px CSS pixel: ${devicePixelRatio} to 1 device pixels`
    
    return {color, family, size};
}

function drawZoomView(x, y) {
    ctx1.clearRect(0, 0, ctx1.canvas.width, ctx1.canvas.height);
    //x -= ZOOM_SIZE / 2;
    //y -= ZOOM_SIZE / 2;
    const canvasDisplayWidthCSSpx = ctx.canvas.scrollWidth; // these are integers
    const canvasDisplayHeightCSSpx = ctx.canvas.scrollHeight;
    
    const canvasResWidth = ctx.canvas.width;
    const canvasResHeight = ctx.canvas.height;
    
    const scaleX = canvasResWidth / (canvasDisplayWidthCSSpx * devicePixelRatio);
    const scaleY = canvasResHeight / (canvasDisplayHeightCSSpx * devicePixelRatio);
    
    x *= scaleX;
    y *= scaleY;
    x -= ZOOM_SIZE / 2;
    y -= ZOOM_SIZE / 2;
    
    ctx1.drawImage(ctx.canvas, -x, -y);
}

displayFont.addEventListener("click", changeFontClass);
function changeFontClass() {
   currentFontClass ++;
   myFontText.className = fontClasses[currentFontClass % fontClasses.length];
   updateDisplay(true);
}
canvas.addEventListener("click", changeCanvasRes);
function changeCanvasRes() {
   currentCanvasRes ++;
   if (devicePixelRatio === 1 && currentCanvasRes === canvasResolutions.length - 1) {
       currentCanvasRes ++;
   }
   updateDisplay(true);
}
   
   

addEventListener("mousemove", mouseEvent);
function mouseEvent(event) {
    const bounds = canvas.getBoundingClientRect();
    mouse.x = event.pageX - scrollX - bounds.left;
    mouse.y = event.pageY - scrollY - bounds.top;    
    updateDisplay();
}

function updateDisplay(andRender = false) {
    if(updating === false) {
        updating = true;
        requestAnimationFrame(render);
    }
    updateText = andRender;
}

function drawTextExamples(text, textStyle) {
    
    var i = TEXT_ROWS;
    const yStep = ctx.canvas.height / (i + 2);
    while (i--) {
        drawText(text, 20, 4 + i * yStep, textStyle.family, textStyle.size, textStyle.color);
    }
}



function render() {
    updating = false;

    const res = canvasResolutions[currentCanvasRes % canvasResolutions.length];
    if (res[0] !== canvas.width || res[1] !== canvas.height) {
        canvas.width = res[0];
        canvas.height = res[1];
        updateText = true;
    }
    if (updateText) {
        ctx.setTransform(1,0,0,1,0,0);
        ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
        updateText = false;
        const textStyle = getFontStyle(myFontText);
        const text = myFontText.textContent;
        drawTextExamples(text, textStyle);
        
    }
    
    
    
    drawZoomView(mouse.x, mouse.y)


}
.fontContainer {
  position: absolute;
  top: 8px;
  left: 35%;
  background: white;
  border: 1px solid black;
  width: 30%;   
  cursor: pointer;
  text-align: center;
}
#styleView {
}
  

.fontA {}
.fontB {
  font-family: arial;
  font-size: 12px;
  color: #F008;
}
.fontC {
  font-family: cursive;
  font-size: 32px;
  color: #0808;
}
.fontD {
  font-family: monospace;
  font-size: 26px;
  color: #000;
}

.layout {
   display: flex;
   width: 100%;
   height: 128px;
}
#container {
   border: 1px solid black;
   width: 49%;
   height: 100%;
   overflow-y: scroll;
}
#container canvas {
   width: 500px;
   height: 500px;
}

#magViewContainer {
   border: 1px solid black;
   display: flex;
   width: 49%;
   height: 100%; 
}

#magViewContainer canvas {
   width: 100%;
   height: 100%;
   image-rendering: pixelated;
}
<div class="fontContainer" id="displayFont"> 
   <span class="fontA" id="myFontText" title="Click to cycle font styles">Hello Pixels</span>
</div>


<div class="layout">
  <div id="container">
      <canvas id="canvas" title="Click to cycle canvas resolution"></canvas>
  </div>
  <div id="magViewContainer">
      <canvas id="canvas1"></canvas>
  </div>
</div>
<code id="styleView"></code>

Linux
  1. Comment j'utilise Vagrant avec libvirt

  2. Comment utiliser heredoc comme éditeur de texte

  3. Comment utiliser regex avec la commande find ?

  4. Comment utiliser la commande `subprocess` avec des pipes

  5. Comment utiliser les images docker locales avec Minikube ?

Commande d'alias Linux :comment l'utiliser avec des exemples

Comment utiliser la commande Linux rm avec des exemples

Comment utiliser PostgreSQL avec l'application Ruby On Rails

Comment utiliser une clé SSH avec des utilisateurs non root

Comment utiliser la commande shutdown avec des exemples

Comment utiliser Let's Encrypt avec Cloudflare