मैं कैनवास पर टेक्स्ट केंद्रित करना चाहता हूं। क्षैतिज रूप से ठीक लगता है, लेकिन लंबवत अभी भी एक समस्या है: मैं यह नहीं समझ सकता कि प्रोग्रामेटिक रूप से यह कैसे करें।
मैं पहले इसे 1 लाइन के साथ करने में सक्षम था, लेकिन अब मैं इसे टेक्स्ट की कई पंक्तियों के लिए काम करना चाहता हूं।
छवि अभी इस तरह दिखती है:
पाठ थोड़ा ऊपर रखा जाना चाहिए, या मैं गलत हूँ?
यहाँ कोड है:
const fs = require('fs')
const { createCanvas } = require('canvas')
const width = 2000;
const height = 2000;
const canvas = createCanvas(width, height)
const context = canvas.getContext('2d')
context.fillStyle = '#edf4ff'
context.fillRect(0, 0, width, height)
context.textAlign = 'center'
context.textBaseline = 'middle';
context.fillStyle = '#002763'
const fontSizeUsed = drawMultilineText(
context,
"This is yet another test",
{
rect: {
x: 1000,
y: 0,
width: 2000,
height: 2000
},
font: 'Arial',
verbose: true,
lineHeight: 1,
minFontSize: 100,
maxFontSize: 200
}
)
const buffer = canvas.toBuffer('image/png')
fs.writeFileSync('./image.png', buffer)
और महत्वपूर्ण कार्य drawMultiLineText, जो पाठ को संरेखित करने वाला है, यह है:
function drawMultilineText(ctx, text, opts) {
// Default options
if(!opts)
opts = {}
if (!opts.font)
opts.font = 'sans-serif'
if (typeof opts.stroke == 'undefined')
opts.stroke = false
if (typeof opts.verbose == 'undefined')
opts.verbose = false
if (!opts.rect)
opts.rect = {
x: 0,
y: 0,
width: ctx.canvas.width,
height: ctx.canvas.height
}
if (!opts.lineHeight)
opts.lineHeight = 1.1
if (!opts.minFontSize)
opts.minFontSize = 30
if (!opts.maxFontSize)
opts.maxFontSize = 100
// Default log function is console.log - Note: if verbose il false, nothing will be logged anyway
if (!opts.logFunction)
opts.logFunction = function(message) { console.log(message) }
const words = require('words-array')(text)
if (opts.verbose) opts.logFunction('Text contains ' + words.length + ' words')
var lines = []
let y; //New Line
// Finds max font size which can be used to print whole text in opts.rec
for (var fontSize = opts.minFontSize; fontSize <= opts.maxFontSize; fontSize++) {
// Line height
var lineHeight = fontSize * opts.lineHeight
// Set font for testing with measureText()
ctx.font = ' ' + fontSize + 'px ' + opts.font
// Start
var x = opts.rect.x;
y = fontSize; //modified line
lines = []
var line = ''
// Cycles on words
for (var word of words) {
// Add next word to line
var linePlus = line + word + ' '
// If added word exceeds rect width...
if (ctx.measureText(linePlus).width > (opts.rect.width)) {
// ..."prints" (save) the line without last word
lines.push({ text: line, x: x, y: y })
// New line with ctx last word
line = word + ' '
y += lineHeight
} else {
// ...continues appending words
line = linePlus
}
}
// "Print" (save) last line
lines.push({ text: line, x: x, y: y })
// If bottom of rect is reached then breaks "fontSize" cycle
if (y > opts.rect.height)
break
}
if (opts.verbose) opts.logFunction("Font used: " + ctx.font);
const offset = opts.rect.y + (opts.rect.height - y) / 2; //New line, calculates offset
for (var line of lines)
// Fill or stroke
if (opts.stroke)
ctx.strokeText(line.text.trim(), line.x, line.y + offset) //modified line
else
ctx.fillText(line.text.trim(), line.x, line.y + offset) //modified line
// Returns font size
return fontSize
}
मैं ब्राउज़र में नहीं हूं, मैं node.js का उपयोग कर रहा हूं।
1 उत्तर
आप सही कह रहे हैं, आपकी पहली छवि का टेक्स्ट भी उच्च स्थान पर होना चाहिए।
कोड में 3 समस्याएं हैं:
y
के प्रारंभिक मान कोfontSize
पर सेट करना- चर की गणना
offset
- पिछले
fontSize
(अर्थात अंतिम फिटिंग) पर वापस जाए बिना कैनवास की ऊंचाई पार होने पर लूप के लिए बाहर निकलना
अंक 1
y
के प्रारंभिक मान को fontSize
के विपरीत lineHeight
पर सेट किया जाना चाहिए।
अंक 2
चर offset
की गणना इस तथ्य को प्रतिबिंबित नहीं कर रही है कि a) पहली पाठ पंक्ति के लिए प्रारंभिक y
निर्देशांक 0
के बजाय lineHeight
है और b) एक है textBaseline
मध्य पर सेट है। एक अनुकूलित offset
गणना का एक उदाहरण नीचे मेरे कोड में है।
अंक 3
एक बार y
का मान कैनवास की ऊंचाई (स्थिति y > opts.rect.height
) से अधिक हो जाने पर, पिछले fontSize
पर एक कदम पीछे हटना चाहिए। इसे हल करने के लिए, पिछले (यानी अंतिम फिटिंग) पुनरावृत्ति से मूल्यों को संग्रहीत करने के लिए नए चर पेश किए जा सकते हैं और इस आवश्यक 'स्टेप बैक' के लिए उपयोग किया जा सकता है। (नीचे मेरे कोड में वे चर हैं lastFittingLines
, lastFittingFont
, lastFittingY
और lastFittingLineHeight
।)
उदाहरण छवि:
यहां संशोधित कोड है:
const fs = require('fs')
const { createCanvas } = require('canvas')
const width = 2000;
const height = 2000;
const canvas = createCanvas(width, height)
const context = canvas.getContext('2d')
context.fillStyle = '#edf4ff'
context.fillRect(0, 0, width, height)
context.textAlign = 'center'
context.textBaseline = 'middle';
context.fillStyle = '#002763'
const fontSizeUsed = drawMultilineText(
context,
"This is a text with multiple lines that is vertically centered as expected.",
{
rect: {
x: 1000,
y: 0,
width: 2000,
height: 2000
},
font: 'Arial',
verbose: true,
lineHeight: 1,
minFontSize: 100,
maxFontSize: 200
}
)
const buffer = canvas.toBuffer('image/png')
fs.writeFileSync('./image3.png', buffer)
function drawMultilineText(ctx, text, opts) {
// Default options
if (!opts)
opts = {}
if (!opts.font)
opts.font = 'sans-serif'
if (typeof opts.stroke == 'undefined')
opts.stroke = false
if (typeof opts.verbose == 'undefined')
opts.verbose = false
if (!opts.rect)
opts.rect = {
x: 0,
y: 0,
width: ctx.canvas.width,
height: ctx.canvas.height
}
if (!opts.lineHeight)
opts.lineHeight = 1.1
if (!opts.minFontSize)
opts.minFontSize = 30
if (!opts.maxFontSize)
opts.maxFontSize = 100
// Default log function is console.log - Note: if verbose il false, nothing will be logged anyway
if (!opts.logFunction)
opts.logFunction = function (message) { console.log(message) }
const words = require('words-array')(text)
if (opts.verbose) opts.logFunction('Text contains ' + words.length + ' words')
var lines = []
let y; //New Line
// Finds max font size which can be used to print whole text in opts.rec
let lastFittingLines; // declaring 4 new variables (addressing issue 3)
let lastFittingFont;
let lastFittingY;
let lastFittingLineHeight;
for (var fontSize = opts.minFontSize; fontSize <= opts.maxFontSize; fontSize++) {
// Line height
var lineHeight = fontSize * opts.lineHeight
// Set font for testing with measureText()
ctx.font = ' ' + fontSize + 'px ' + opts.font
// Start
var x = opts.rect.x;
y = lineHeight; //modified line // setting to lineHeight as opposed to fontSize (addressing issue 1)
lines = []
var line = ''
// Cycles on words
for (var word of words) {
// Add next word to line
var linePlus = line + word + ' '
// If added word exceeds rect width...
if (ctx.measureText(linePlus).width > (opts.rect.width)) {
// ..."prints" (save) the line without last word
lines.push({ text: line, x: x, y: y })
// New line with ctx last word
line = word + ' '
y += lineHeight
} else {
// ...continues appending words
line = linePlus
}
}
// "Print" (save) last line
lines.push({ text: line, x: x, y: y })
// If bottom of rect is reached then breaks "fontSize" cycle
if (y > opts.rect.height)
break;
lastFittingLines = lines; // using 4 new variables for 'step back' (issue 3)
lastFittingFont = ctx.font;
lastFittingY = y;
lastFittingLineHeight = lineHeight;
}
lines = lastFittingLines; // assigning last fitting values (issue 3)
ctx.font = lastFittingFont;
if (opts.verbose) opts.logFunction("Font used: " + ctx.font);
const offset = opts.rect.y - lastFittingLineHeight / 2 + (opts.rect.height - lastFittingY) / 2; // modifying calculation (issue 2)
for (var line of lines)
// Fill or stroke
if (opts.stroke)
ctx.strokeText(line.text.trim(), line.x, line.y + offset) //modified line
else
ctx.fillText(line.text.trim(), line.x, line.y + offset) //modified line
// Returns font size
return fontSize
}