Как сделать так, чтобы размер шрифта Canvas соответствовал размеру шрифта html?

#javascript #html #canvas

Вопрос:

Я пытаюсь отобразить текст на холсте, и я пытаюсь сделать так, чтобы размер холста содержал только текст.

Вот код, который отображает строку текста на холсте заданным шрифтом:

(Код взят из большего кода):

 // Renderer base class is used to calculate width and height of the terminal
// based on char size
class Renderer {
    constructor(render, { color = '#cccccc', background = 'black', char = { width: 7, height: 14 }} = {}) {
        this._options = {
            background,
            color,
            char
        };
        this._render = render;
    }
    option(arg, value) {
        if (typeof arg === 'object') {
            Object.assign(this._options, arg);
        } else if (typeof value === 'undefined') {
            return this._options[arg];
        } else {
            this._options[arg] = value;
        }
    }
    render() {
        const char = this.option('char');
        const lines = this._render();
        const max = Math.max(...lines.map(l => l.length));
        const width = max * char.width;
        const size = char.height;
        const height = lines.length * size;
        this.clear({ width, height, size });
        for (let line = 0; line < lines.length;   line) {
            const text = lines[line];
            this.line(text, 0, size * line);
        }
    }
    line(text, x, y) {
        throw new Error('Renderer::line invalid Invocation');
    }
    clear({ width, height, size } = {}) { }
}

// child class is used to render the text calculated in base class on a Canvas
class CanvasRenderer extends Renderer {
    constructor(render, options = {}) {
        super(render, options);
        this.canvas = document.createElement('canvas');
        this.ctx = this.canvas.getContext('2d');
    }
    clear({ width, height, size }) {
        this.canvas.width = width;
        this.canvas.height = height;
        this.ctx.fillStyle = this.option('background');
        this.ctx.fillRect(0, 0, width, height);
        this.ctx.font = `${size}px monospace`;
        this.ctx.textBaseline = 'hanging';
        this.ctx.fillStyle = this.option('color');
    }
    line(text, x, y) {
        this.ctx.fillText(text, x, y);
    }
}


var span = document.getElementsByTagName('span')[0];
span.getBoundingClientRect();


var renderer = new CanvasRenderer(() => (["Hello", "World"]), {
    background: 'white',
    color: 'black',
    char: span.getBoundingClientRect()
});

document.body.appendChild(renderer.canvas);
renderer.render(); 
 html, body {
    font-family: monospace;
} 
 <p>Some Text</p>
<span>amp;nbsp;</span>
<br/> 

проблема в том, что при использовании одного и того же шрифта на холсте и HTML размер не совпадает.
И вы можете рассчитать количество символов в строке так же, как в HTML.

Мой вопрос в том, как я могу сделать размер шрифта таким же, как в HTML? Или, может быть, как я могу рассчитать количество строк при отображении текста на холсте?

Ответ №1:

Как насчет использования this.ctx.font = '1rem monospace'; ?

 // Renderer base class is used to calculate width and height of the terminal
// based on char size
class Renderer {
    constructor(render, { color = '#cccccc', background = 'black', char = { width: 7, height: 14 }} = {}) {
        this._options = {
            background,
            color,
            char
        };
        this._render = render;
    }
    option(arg, value) {
        if (typeof arg === 'object') {
            Object.assign(this._options, arg);
        } else if (typeof value === 'undefined') {
            return this._options[arg];
        } else {
            this._options[arg] = value;
        }
    }
    render() {
        const char = this.option('char');
        const lines = this._render();
        const max = Math.max(...lines.map(l => l.length));
        const width = max * char.width;
        const size = char.height;
        const height = lines.length * size;
        this.clear({ width, height, size });
        for (let line = 0; line < lines.length;   line) {
            const text = lines[line];
            this.line(text, 0, size * line);
        }
    }
    line(text, x, y) {
        throw new Error('Renderer::line invalid Invocation');
    }
    clear({ width, height, size } = {}) { }
}

// child class is used to render the text calculated in base class on a Canvas
class CanvasRenderer extends Renderer {
    constructor(render, options = {}) {
        super(render, options);
        this.canvas = document.createElement('canvas');
        this.ctx = this.canvas.getContext('2d');
    }
    clear({ width, height, size }) {
        this.canvas.width = width;
        this.canvas.height = height;
        this.ctx.fillStyle = this.option('background');
        this.ctx.fillRect(0, 0, width, height);
        this.ctx.font = `1rem monospace`;
        this.ctx.textBaseline = 'hanging';
        this.ctx.fillStyle = this.option('color');
    }
    line(text, x, y) {
        this.ctx.fillText(text, x, y);
    }
}


var span = document.getElementsByTagName('span')[0];
span.getBoundingClientRect();


var renderer = new CanvasRenderer(() => (["Hello", "World"]), {
    background: 'white',
    color: 'black',
    char: span.getBoundingClientRect()
});

document.body.appendChild(renderer.canvas);
renderer.render(); 
 html, body {
    font-family: monospace;
} 
 <p>Some Text</p>
<span>amp;nbsp;</span>
<br/> 

Комментарии:

1. Спасибо. это решило проблему, но в моем случае мне нужно было использовать 1em , чтобы я мог изменить размер холста.

Ответ №2:

Вы можете динамически получить шрифт, выбрав случайный элемент абзаца с window.getComputedStyle(document.querySelector('p')).getPropertyValue("font") :

 // Renderer base class is used to calculate width and height of the terminal
// based on char size
class Renderer {
    constructor(render, { color = '#cccccc', background = 'black', char = { width: 7, height: 14 }} = {}) {
        this._options = {
            background,
            color,
            char
        };
        this._render = render;
    }
    option(arg, value) {
        if (typeof arg === 'object') {
            Object.assign(this._options, arg);
        } else if (typeof value === 'undefined') {
            return this._options[arg];
        } else {
            this._options[arg] = value;
        }
    }
    render() {
        const char = this.option('char');
        const lines = this._render();
        const max = Math.max(...lines.map(l => l.length));
        const width = max * char.width;
        const size = char.height;
        const height = lines.length * size;
        this.clear({ width, height, size });
        for (let line = 0; line < lines.length;   line) {
            const text = lines[line];
            this.line(text, 0, size * line);
        }
    }
    line(text, x, y) {
        throw new Error('Renderer::line invalid Invocation');
    }
    clear({ width, height, size } = {}) { }
}

// child class is used to render the text calculated in base class on a Canvas
class CanvasRenderer extends Renderer {
    constructor(render, options = {}) {
        super(render, options);
        this.canvas = document.createElement('canvas');
        this.ctx = this.canvas.getContext('2d');
    }
    clear({ width, height, size }) {
        this.canvas.width = width;
        this.canvas.height = height;
        this.ctx.fillStyle = this.option('background');
        this.ctx.fillRect(0, 0, width, height);
        this.ctx.font = window.getComputedStyle(document.querySelector('p')).getPropertyValue("font");
        this.ctx.textBaseline = 'hanging';
        this.ctx.fillStyle = this.option('color');
    }
    line(text, x, y) {
        this.ctx.fillText(text, x, y);
    }
}


var span = document.getElementsByTagName('span')[0];
span.getBoundingClientRect();


var renderer = new CanvasRenderer(() => (["Hello", "World"]), {
    background: 'white',
    color: 'black',
    char: span.getBoundingClientRect()
});

document.body.appendChild(renderer.canvas);
renderer.render(); 
 html, body {
    font-family: monospace;
} 
 <p>Some Text</p>
<span>amp;nbsp;</span>
<br/> 

Комментарии:

1. Спасибо, только что понял, что высота символа не совпадает с размером шрифта.