Как использовать на холсте текстовый элемент со шрифтом, описанным в CSS - PullRequest
8 голосов
/ 09 января 2020

Это в рамках проекта Bismon (программное обеспечение GPLv3 +, финансируемое европейскими проектами H2020), git commit 0e9a8eccc2976f. Этот черновой отчет описывает программное обеспечение. Этот вопрос дает больше контекста и мотивации. Речь идет о (рукописном) файле webroot / jscript / bismon-hw root. js, используемом на некоторой странице HTML, код которой генерируется Bismon (специализированный веб-сервер выше libonion ).

Я добавил класс CSS для диапазона, например span.bmcl_evalprompt (например, в моем файле first-theme. css).

Как мне кодировать JavaScript, чтобы добавить текстовую часть на холсте (предпочтительно с использованием jcanvas с jquery), имеющую тот же стиль (тот же шрифт, цвет и т. Д. c) ...) как это span.bmcl_evalprompt? Нужно ли создавать такой элемент span в моем DOM? Это даже просто возможно?

Я забочусь только о недавнем Firefox (как минимум 68) на Linux. JQuery составляет 3,4. Я также использую Jquery UI 1.12.1

У меня была идея создать один элемент <span class='bmcl_evalprompt'> с координатами, удаленными от области просмотра браузера (или X11 окно), например, в x= -10000 и y= -10000 (в пикселях), затем добавьте этот единственный плохо расположенный элемент в DOM документа, затем используйте традиционные методы Jquery, чтобы получить семейство шрифтов, размер шрифта и размер элемента. Но есть ли лучший способ? Или какая-нибудь Jquery совместимая библиотека делает это?

Ответы [ 2 ]

5 голосов
/ 16 января 2020

Соответствует шрифт DOM на холсте?

Простой ответ: "Путь к труду !!" и "Это никогда не будет идеально."

Лучшее, что вы можете сделать, - это аппроксимация, приведенная в примере внизу ответа, который также покажет, что соответствие видимого стиля не связано с видимым качеством.

Расширение от просто CSS правил.

Если вы хотите, чтобы шрифт максимально приближался к элементу, есть некоторые дополнительные проблемы, помимо простого получения CSS, как указано в Ответе Spark Fountain .

Размер шрифта & CSS Размер пикселя

  • Размер шрифта связан с CSS размером пикселя. Размер HTMLCanvasElement
  • CSS не всегда соответствует пикселям дисплея устройства. Например, дисплеи HiDPI / Retina. Вы можете получить доступ к устройству CSS в пикселях через devicePixelRatio
  • CSS Размер пикселя не является постоянным и может изменяться по многим причинам. Изменения можно отслеживать с помощью MediaQueryListEvent и прослушивания события change
  • Элементы могут быть преобразованы. CanvasRenderingContext2D не может выполнять 3D-преобразования, поэтому элемент или холст имеют 3D-преобразование. Вы не сможете сопоставить визуализированный шрифт холста с элементами визуализированного шрифта.

  • Разрешение холста и размер дисплея не зависят.

    • Вы можете получить разрешение холста через свойства HTMLCanvasElement.width и HTMLCanvasElement.height
    • Вы можете получить размер отображения холста через ширину и высоту свойств стиля или через множество других методов см. в примере.
    • Пиксельный аспект может не соответствовать пиксельному аспекту CSS и должен вычисляться при визуализации шрифта на холсте.
    • Рендеринг шрифта Canvas мелким шрифтом размеры ужасны. Например, шрифт размером 4 пикселя размером 16 пикселей не читается. ctx.font = "4px arial"; ctx.scale(4,4); ctx.fillText("Hello pixels"); Вы должны использовать фиксированный размер шрифта, который обеспечивает хорошие результаты рендеринга холста, и уменьшать размер рендеринга при использовании мелких шрифтов.

Цвет шрифта

Стиль цвета элементов представляет только визуализированный цвет. Он не представляет фактический цвет, видимый пользователем.

Поскольку это относится как к холсту, так и к элементу, из которого вы получаете цвет, и к любым элементам, лежащим над или под слоем, объем работы, необходимый для визуального соответствия цвета, огромен и выходит за рамки стека. ответ переполнения (ответы имеют максимальную длину 30 КБ)

Рендеринг шрифтов

Механизм рендеринга шрифтов на холсте отличается от движка DOM. DOM может использовать различные методы рендеринга, чтобы улучшить видимое качество шрифтов, используя преимущества расположения физических подпикселей RGB устройств. Например, TrueType шрифты и связанные с ними хинты, используемые рендерером, и производные субпиксели ClearType с рендерингом хинтовки.

Эти методы рендеринга шрифтов МОГУТ совпадать на холсте , хотя для сопоставления в реальном времени вам придется использовать WebGL.

Проблема в том, что рендеринг шрифта DOMs определяется многими факторами, включая настройки браузеров. JavaScript не может получить доступ к любой информации, необходимой для определения способа визуализации шрифта. В лучшем случае вы можете сделать обоснованное предположение.

Дополнительные сложности

Существуют также другие факторы, влияющие на шрифт, и то, как правила стиля шрифта CSS связаны с визуальным результатом отображаемого шрифта. Например CSS единицы, анимация, выравнивание, направление, преобразования шрифтов и режим причуд.

Лично для рендеринга и цвета я не беспокоюсь. Событие, если я написал полный движок шрифтов, использующий WebGL для соответствия каждому варианту шрифта, фильтрации, компоновки и рендеринга, они не являются частью стандарта и поэтому могут быть изменены без предварительного уведомления. Таким образом, проект всегда будет открытым и может в любой момент потерпеть неудачу до уровня нечитаемых результатов. Просто не стоит усилий.


Пример

Пример имеет холст рендеринга слева. Текст и верх шрифта по центру. Увеличенный вид справа, который показывает увеличенный вид левого холста

Первый используемый стиль - это страницы по умолчанию. Разрешение холста составляет 300 × 150, но масштабируется до 500 на 500 CSS пикселей. Это приводит к ОЧЕНЬ низкому качеству текста холста. Циклическое разрешение холста покажет, как холст влияет на качество.

Функции

  • drawText(text, x, y, fontCSS, sizeCSSpx, colorStyleCSS) dr aws текста с использованием CSS значений свойств. Масштабирование шрифта для соответствия визуальному размеру DOM и соотношению сторон как можно ближе.

  • getFontStyle(element) возвращает необходимые стили шрифта как объект из element

Использование пользовательского интерфейса

  • ЩЕЛКНИТЕ центральный шрифт для циклического изменения стилей шрифта.

  • ЩЕЛКНИТЕ левое полотно для циклического разрешения холста.

  • Внизу находятся настройки, используемые для визуализации текста на холсте.

Вы увидите, что качество текста зависит от разрешение холста.

Чтобы увидеть, как DOM-масштабирование влияет на рендеринг, вы должны увеличивать или уменьшать масштаб страницы. Дисплеи HiDPI и Retina будут иметь более низкое качество текста холста из-за того, что холст составляет половину разрешения 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>
3 голосов
/ 14 января 2020

Если вы просто хотите визуализировать текст из вашего диапазона на холсте, вы можете получить доступ к атрибутам стиля с помощью функции window.getComputedStyle . Чтобы сделать оригинальный диапазон невидимым, установите его стиль на 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...