fix
This commit is contained in:
parent
a751c9d705
commit
8b30ac4c9e
@ -171,8 +171,146 @@ function showToast(e={}){let{message:t="",status:a="info",duration:l=4e3}=e,n=do
|
||||
style="margin-left:8px;color:green;display:none;">Скопировано!</span></button></div>
|
||||
</div>
|
||||
</div>
|
||||
<script defer src="/static/script/share.js"></script>
|
||||
<script>document.addEventListener('DOMContentLoaded', () => {
|
||||
<script>
|
||||
async function imageWithTextBlob(imageUrl, text, options = {}) {
|
||||
const response = await fetch(imageUrl, {
|
||||
mode: 'cors',
|
||||
credentials: 'same-origin',
|
||||
});
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
const imageBlob = await response.blob();
|
||||
const imageBitmap = await createImageBitmap(imageBlob);
|
||||
|
||||
// OffscreenCanvas (works in modern browsers; fallback to normal canvas if needed)
|
||||
const canvas = typeof OffscreenCanvas !== 'undefined'
|
||||
? new OffscreenCanvas(
|
||||
options.width ?? imageBitmap.width,
|
||||
options.height ?? imageBitmap.height
|
||||
)
|
||||
: document.createElement('canvas');
|
||||
|
||||
const width = canvas.width ?? (options.width ?? imageBitmap.width);
|
||||
const height = canvas.height ?? (options.height ?? imageBitmap.height);
|
||||
|
||||
if (!(canvas instanceof OffscreenCanvas)) {
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
}
|
||||
|
||||
const ctx = canvas.getContext('2d', { alpha: true });
|
||||
|
||||
// Draw image
|
||||
ctx.drawImage(imageBitmap, 0, 0, width, height);
|
||||
|
||||
// No text: return original bitmap as blob
|
||||
if (!text?.trim()) {
|
||||
if (canvas.convertToBlob) {
|
||||
return canvas.convertToBlob({
|
||||
type: options.type ?? 'image/png',
|
||||
quality: options.quality ?? 0.92,
|
||||
});
|
||||
}
|
||||
return await new Promise((resolve) =>
|
||||
canvas.toBlob((b) => resolve(b), options.type ?? 'image/png', options.quality ?? 0.92)
|
||||
);
|
||||
}
|
||||
|
||||
// === Text rendering ===
|
||||
const basePadding = options.basePadding ?? 8; // internal left/right/top padding
|
||||
const fontSize = options.fontSize ?? Math.round(width * 0.05);
|
||||
ctx.font = `${fontSize}px ${options.fontFamily ?? 'sans-serif'}`;
|
||||
ctx.textBaseline = 'bottom';
|
||||
ctx.textAlign = 'center';
|
||||
|
||||
const textColor = options.textColor ?? '#5F1798';
|
||||
const bgColor = options.bgColor ?? '#fff';
|
||||
const strokeColor = options.strokeColor ?? 'rgba(0,0,0,0.35)';
|
||||
const shadowBlur = options.shadowBlur ?? 6;
|
||||
const maxWidth = width - basePadding * 2 - (options.extraPadding ?? 0);
|
||||
|
||||
// Wrap text into lines
|
||||
const words = text.split(' ');
|
||||
let line = '';
|
||||
const lines = [];
|
||||
for (const word of words) {
|
||||
const testLine = line ? line + ' ' + word : word;
|
||||
if (ctx.measureText(testLine).width > maxWidth && line) {
|
||||
lines.push(line);
|
||||
line = word;
|
||||
} else {
|
||||
line = testLine;
|
||||
}
|
||||
}
|
||||
if (line) lines.push(line);
|
||||
|
||||
const lineHeight = fontSize + (options.lineGap ?? 6);
|
||||
|
||||
// Extra internal bottom padding inside the rounded box
|
||||
const internalBottomPadding = options.internalBottomPadding ?? Math.round(fontSize * 0.35);
|
||||
const boxHeight = lines.length * lineHeight + basePadding + internalBottomPadding;
|
||||
|
||||
const maxLineWidth = Math.max(...lines.map((l) => ctx.measureText(l).width));
|
||||
const boxWidth = maxLineWidth + basePadding * 2;
|
||||
|
||||
// Distance from the very bottom of the image to the bottom of the text box
|
||||
const bottomPadding = options.bottomPadding ?? Math.round(fontSize * 0.9);
|
||||
|
||||
const boxX = (width - boxWidth) / 2;
|
||||
const boxY = height - bottomPadding - boxHeight;
|
||||
const radius = options.radius ?? 8;
|
||||
|
||||
// Background rounded rect
|
||||
ctx.save();
|
||||
ctx.fillStyle = bgColor;
|
||||
ctx.shadowColor = strokeColor;
|
||||
ctx.shadowBlur = shadowBlur;
|
||||
roundRect(ctx, boxX, boxY, boxWidth, boxHeight, radius, true, false);
|
||||
ctx.restore();
|
||||
|
||||
// Text
|
||||
ctx.fillStyle = textColor;
|
||||
ctx.shadowColor = 'transparent';
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const lineY = boxY + basePadding + (i + 1) * lineHeight - (lineHeight - fontSize) / 2;
|
||||
const lineX = width / 2;
|
||||
|
||||
if (options.outline) {
|
||||
ctx.lineWidth = options.outlineWidth ?? 2;
|
||||
ctx.strokeStyle = options.outlineColor ?? 'rgba(255,255,255,0.8)';
|
||||
ctx.strokeText(lines[i], lineX, lineY);
|
||||
}
|
||||
ctx.fillText(lines[i], lineX, lineY);
|
||||
}
|
||||
|
||||
// Convert to blob
|
||||
if (canvas.convertToBlob) {
|
||||
return await canvas.convertToBlob({
|
||||
type: options.type ?? 'image/png',
|
||||
quality: options.quality ?? 0.92,
|
||||
});
|
||||
}
|
||||
return await new Promise((resolve) =>
|
||||
canvas.toBlob((b) => resolve(b), options.type ?? 'image/png', options.quality ?? 0.92)
|
||||
);
|
||||
}
|
||||
function roundRect(ctx, x, y, w, h, r, fill, stroke) {
|
||||
if (typeof r === 'number') r = { tl: r, tr: r, br: r, bl: r };
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + r.tl, y);
|
||||
ctx.lineTo(x + w - r.tr, y);
|
||||
ctx.quadraticCurveTo(x + w, y, x + w, y + r.tr);
|
||||
ctx.lineTo(x + w, y + h - r.br);
|
||||
ctx.quadraticCurveTo(x + w, y + h, x + w - r.br, y + h);
|
||||
ctx.lineTo(x + r.bl, y + h);
|
||||
ctx.quadraticCurveTo(x, y + h, x, y + h - r.bl);
|
||||
ctx.lineTo(x, y + r.tl);
|
||||
ctx.quadraticCurveTo(x, y, x + r.tl, y);
|
||||
ctx.closePath();
|
||||
if (fill) ctx.fill();
|
||||
if (stroke) ctx.stroke();
|
||||
}
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const shareModal = document.getElementById('shareModal');
|
||||
const closeshareModal = document.getElementById('closeModal');
|
||||
const fb = document.getElementById('fb');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user