feat: canvas scaling for "high quality" output

pull/9/head
WUGqnwvMQPzl 1 year ago
parent 08ae82cb56
commit cfda9a377a

@ -49,6 +49,11 @@
<span class="flex-1"><span class="mr-2">Y</span><input type="number" placeholder="Type here" value="0"
class="input input-bordered input-sm input-primary w-32" id="graphY" /></span>
</div>
<div class="flex items-center gap-2 my-4">
<div class="i18n" data-i18n="scale-level">Scale Level</div>
<input type="number" placeholder="1" value="1" min="1" max="4"
class="input input-bordered input-sm input-primary w-32" id="scaleLevel" />
</div>
</div>
</div>
</div>

@ -11,7 +11,7 @@ const {
paddingX,
hollowPath,
} = settings;
const font = `${fontSize}px RoGSanSrfStd-Bd, GlowSansSC-Normal-Heavy_diff, apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, PingFang SC, Hiragino Sans GB, Microsoft YaHei, sans-serif`;
const font = 'RoGSanSrfStd-Bd, GlowSansSC-Normal-Heavy_diff, apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, PingFang SC, Hiragino Sans GB, Microsoft YaHei, sans-serif';
export default class LogoCanvas {
public canvas: HTMLCanvasElement;
@ -26,11 +26,12 @@ export default class LogoCanvas {
private textWidthR = 0;
private graphOffset = graphOffset;
private transparentBg = false;
private scaleLevel = 1;
constructor() {
this.canvas = document.querySelector('#canvas')!;
this.ctx = this.canvas.getContext('2d')!;
this.canvas.height = canvasHeight;
this.canvas.width = canvasWidth;
this.canvas.height = canvasHeight * this.scaleLevel;
this.canvas.width = canvasWidth * this.scaleLevel;
this.bindEvent();
}
async draw() {
@ -38,12 +39,13 @@ export default class LogoCanvas {
loading.classList.remove('hidden');
const c = this.ctx;
//predict canvas width
await loadFont(this.textL + this.textR);
await loadFont(this.textL + this.textR, this.scaleLevel);
loading.classList.add('hidden');
c.font = font;
c.font = `${fontSize * this.scaleLevel}px ${font}`;
this.textMetricsL = c.measureText(this.textL);
this.textMetricsR = c.measureText(this.textR);
this.setWidth();
this.canvas.height = canvasHeight * this.scaleLevel;
//clear canvas
c.clearRect(0, 0, this.canvas.width, this.canvas.height);
//Background
@ -54,7 +56,7 @@ export default class LogoCanvas {
//guide line
if (import.meta.env.DEV) {
c.strokeStyle = '#00cccc';
c.lineWidth = 1;
c.lineWidth = 1 * this.scaleLevel;
c.beginPath();
c.moveTo(this.canvasWidthL, 0);
c.lineTo(this.canvasWidthL, this.canvas.height);
@ -68,7 +70,7 @@ export default class LogoCanvas {
c.stroke();
}
//blue text -> halo -> black text -> cross
c.font = font;
c.font = `${fontSize * this.scaleLevel}px ${font}`;
c.fillStyle = '#128AFA';
c.textAlign = 'end';
c.setTransform(1, 0, horizontalTilt, 1, 0, 0);
@ -76,10 +78,10 @@ export default class LogoCanvas {
c.resetTransform(); //restore don't work
c.drawImage(
window.halo,
this.canvasWidthL - this.canvas.height / 2 + this.graphOffset.X,
this.graphOffset.Y,
canvasHeight,
canvasHeight
this.canvasWidthL - this.canvas.height / 2 + (this.graphOffset.X * this.scaleLevel),
this.graphOffset.Y * this.scaleLevel,
canvasHeight * this.scaleLevel,
canvasHeight * this.scaleLevel
);
c.fillStyle = '#2B2B2B';
c.textAlign = 'start';
@ -87,25 +89,25 @@ export default class LogoCanvas {
c.globalCompositeOperation = 'destination-out';
}
c.strokeStyle = 'white';
c.lineWidth = 12;
c.lineWidth = 12 * this.scaleLevel;
c.setTransform(1, 0, horizontalTilt, 1, 0, 0);
c.strokeText(this.textR, this.canvasWidthL, this.canvas.height * textBaseLine);
c.globalCompositeOperation = 'source-over';
c.fillText(this.textR, this.canvasWidthL, this.canvas.height * textBaseLine);
c.resetTransform();
const graph = {
X: this.canvasWidthL - this.canvas.height / 2 + graphOffset.X,
Y: this.graphOffset.Y,
X: this.canvasWidthL - this.canvas.height / 2 + (graphOffset.X * this.scaleLevel),
Y: this.graphOffset.Y * this.scaleLevel,
};
c.beginPath();
c.moveTo(
graph.X + (hollowPath[0][0] / 500) * canvasHeight,
graph.Y + (hollowPath[0][1] / 500) * canvasHeight
graph.X + ((hollowPath[0][0] * this.scaleLevel) / (500 * this.scaleLevel)) * (canvasHeight * this.scaleLevel),
graph.Y + ((hollowPath[0][1] * this.scaleLevel) / (500 * this.scaleLevel)) * (canvasHeight * this.scaleLevel)
);
for (let i = 1; i < 4; i++) {
c.lineTo(
graph.X + (hollowPath[i][0] / 500) * canvasHeight,
graph.Y + (hollowPath[i][1] / 500) * canvasHeight
graph.X + ((hollowPath[i][0] * this.scaleLevel) / (500 * this.scaleLevel)) * (canvasHeight * this.scaleLevel),
graph.Y + ((hollowPath[i][1] * this.scaleLevel) / (500 * this.scaleLevel)) * (canvasHeight * this.scaleLevel)
);
}
c.closePath();
@ -117,10 +119,10 @@ export default class LogoCanvas {
c.globalCompositeOperation = 'source-over';
c.drawImage(
window.cross,
this.canvasWidthL - this.canvas.height / 2 + graphOffset.X,
this.graphOffset.Y,
canvasHeight,
canvasHeight
this.canvasWidthL - this.canvas.height / 2 + (graphOffset.X * this.scaleLevel),
this.graphOffset.Y * this.scaleLevel,
canvasHeight * this.scaleLevel,
canvasHeight * this.scaleLevel
);
}
bindEvent() {
@ -163,46 +165,51 @@ export default class LogoCanvas {
this.graphOffset.Y = parseInt(gy.value);
this.draw();
});
const scaleInput = document.querySelector('#scaleLevel')! as HTMLInputElement;
scaleInput.addEventListener('input', () => {
this.scaleLevel = parseInt(scaleInput.value);
this.draw();
});
}
setWidth() {
this.textWidthL =
this.textMetricsL!.width -
(textBaseLine * canvasHeight + this.textMetricsL!.fontBoundingBoxDescent) * horizontalTilt;
(textBaseLine * (canvasHeight * this.scaleLevel) + this.textMetricsL!.fontBoundingBoxDescent) * horizontalTilt;
this.textWidthR =
this.textMetricsR!.width +
(textBaseLine * canvasHeight - this.textMetricsR!.fontBoundingBoxAscent) * horizontalTilt;
(textBaseLine * (canvasHeight * this.scaleLevel) - this.textMetricsR!.fontBoundingBoxAscent) * horizontalTilt;
//extend canvas
if (this.textWidthL + paddingX > canvasWidth / 2) {
this.canvasWidthL = this.textWidthL + paddingX;
if (this.textWidthL + (paddingX * this.scaleLevel) > (canvasWidth * this.scaleLevel) / 2) {
this.canvasWidthL = this.textWidthL + (paddingX * this.scaleLevel);
} else {
this.canvasWidthL = canvasWidth / 2;
this.canvasWidthL = (canvasWidth * this.scaleLevel) / 2;
}
if (this.textWidthR + paddingX > canvasWidth / 2) {
this.canvasWidthR = this.textWidthR + paddingX;
if (this.textWidthR + (paddingX * this.scaleLevel) > (canvasWidth * this.scaleLevel) / 2) {
this.canvasWidthR = this.textWidthR + (paddingX * this.scaleLevel);
} else {
this.canvasWidthR = canvasWidth / 2;
this.canvasWidthR = (canvasWidth * this.scaleLevel) / 2;
}
this.canvas.width = this.canvasWidthL + this.canvasWidthR;
}
generateImg() {
let outputCanvas: HTMLCanvasElement;
if (
this.textWidthL + paddingX < canvasWidth / 2 ||
this.textWidthR + paddingX < canvasWidth / 2
this.textWidthL + (paddingX * this.scaleLevel) < (canvasWidth * this.scaleLevel) / 2 ||
this.textWidthR + (paddingX * this.scaleLevel) < (canvasWidth * this.scaleLevel) / 2
) {
outputCanvas = document.createElement('canvas');
outputCanvas.width = this.textWidthL + this.textWidthR + paddingX * 2;
outputCanvas.width = this.textWidthL + this.textWidthR + (paddingX * this.scaleLevel) * 2;
outputCanvas.height = this.canvas.height;
const ctx = outputCanvas.getContext('2d')!;
ctx.drawImage(
this.canvas,
canvasWidth / 2 - this.textWidthL - paddingX,
(canvasWidth * this.scaleLevel) / 2 - this.textWidthL - (paddingX * this.scaleLevel),
0,
this.textWidthL + this.textWidthR + paddingX * 2,
this.textWidthL + this.textWidthR + (paddingX * this.scaleLevel) * 2,
this.canvas.height,
0,
0,
this.textWidthL + this.textWidthR + paddingX * 2,
this.textWidthL + this.textWidthR + (paddingX * this.scaleLevel) * 2,
this.canvas.height
);
} else {

@ -7,6 +7,7 @@
"transparent-background": "Transparent Background",
"advance": "Advance settings",
"halo-cross": "Halo & Cross position",
"scale-level": "Scale Level",
"font-title": "Used Fonts",
"main-font": "Main font: ",
"fallback-font": "Fallback font: ",

@ -7,6 +7,7 @@
"transparent-background": "透明背景",
"advance": "高级设置",
"halo-cross": "光环位置微调",
"scale-level": "缩放等级",
"font-title": "使用的字体",
"main-font": "主要字体:",
"fallback-font": "Fallback 字体:",

@ -39,7 +39,7 @@ h1 {
#canvas {
border-radius: 8px;
border: 1px solid #bbb;
max-width: 100%;
max-width: 900px;
}
@media (max-width: 900px){

@ -1,6 +1,6 @@
import settings from '../settings';
export default async (content: string = 'A') => {
export default async (content: string = 'A', scaleLevel: number = 1) => {
// const G2B = new FontFace('G2B', 'url(../RoGSanSrfStd-Bd_other.woff2)');
// // const GSH = new FontFace('GSH', 'url(../GlowSansSC-Normal-Heavy.otf)');
// await Promise.all([G2B.load() /*, GSH.load()*/]).then((fonts) =>
@ -9,7 +9,7 @@ export default async (content: string = 'A') => {
// const loadingSwitch = document.querySelector('#loading-switch') as HTMLInputElement;
// loadingSwitch.checked = true;
await document.fonts.load(
`${settings.fontSize}px RoGSanSrfStd-Bd, GlowSansSC-Normal-Heavy_diff`,
`${settings.fontSize * scaleLevel}px RoGSanSrfStd-Bd, GlowSansSC-Normal-Heavy_diff`,
content
);
// loadingSwitch.checked = false;

Loading…
Cancel
Save