Соответствует шрифт 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>