add label to qr-code
Main daploy / deploy (push) Successful in 44s

This commit is contained in:
2024-11-19 20:30:57 +02:00
parent 5d66a3c79a
commit 66093e9c80
5 changed files with 187 additions and 8 deletions
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 103 KiB

@@ -0,0 +1,24 @@
export function calculateFontSizeForText(text: string, maxWidth: number): number {
// Создаем временный элемент <span> для измерения текста
const span = document.createElement('span')
span.style.position = 'absolute'
span.style.whiteSpace = 'nowrap'
span.style.fontFamily = 'Arial, sans-serif' // Задаем нужный шрифт
span.style.visibility = 'hidden'
span.textContent = text
document.body.appendChild(span)
// Начальный размер шрифта
let fontSize = maxWidth
span.style.fontSize = `${fontSize}px`
// Уменьшаем размер шрифта, пока ширина текста не впишется в maxWidth
while (span.offsetWidth > maxWidth) {
fontSize -= 1
span.style.fontSize = `${fontSize}px`
}
// Удаляем временный элемент
document.body.removeChild(span)
return fontSize
}
+58
View File
@@ -0,0 +1,58 @@
export async function convertImageToBlob({
url,
width,
height,
type,
}: {
url: string
width?: number // Ширина необязательна
height?: number // Высота необязательна
type: string
}): Promise<Blob> {
if (!width && !height) {
throw new Error('Необходимо указать хотя бы одно значение: width или height.')
}
return new Promise((resolve, reject) => {
const img = new Image()
img.onload = () => {
// Вычисляем размеры, сохраняя пропорции
const aspectRatio = img.naturalWidth / img.naturalHeight
const targetWidth = width || height! * aspectRatio
const targetHeight = height || width! / aspectRatio
// Создаем canvas и рисуем изображение
const canvas = document.createElement('canvas')
canvas.width = Math.round(targetWidth)
canvas.height = Math.round(targetHeight)
const context = canvas.getContext('2d')
if (!context) {
return reject(new Error('Ошибка создания контекста'))
}
context.drawImage(img, 0, 0, canvas.width, canvas.height)
// Конвертируем canvas в Blob
canvas.toBlob(
(blob) => {
if (blob) {
resolve(blob)
} else {
reject(new Error('Ошибка создания изображения'))
}
},
type,
1.0,
)
URL.revokeObjectURL(url)
}
img.onerror = () => {
reject(new Error('Ошибка загрузки изображения'))
}
img.src = url
})
}
+28
View File
@@ -0,0 +1,28 @@
import { convertImageToBlob } from './convertImageToBlob'
export async function downloadImage({
url,
width,
height,
filename,
type,
}: {
url: string
width?: number
height?: number
filename: string
type: string
}) {
try {
const blob = await convertImageToBlob({ url, width, height, type })
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = filename
link.click()
URL.revokeObjectURL(link.href) // Очищаем URL после загрузки
} catch (err) {
console.error('Ошибка преобразования SVG в PNG:', err)
}
}
+75 -7
View File
@@ -60,14 +60,20 @@
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { computed, ref } from 'vue'
import { useQuery } from '@tanstack/vue-query'
import QRCodeStyling from 'qr-code-styling'
import { optimize } from 'svgo'
import { getQrCodeDocument } from '@/api/qrCode'
import { type QRCodeDocument } from '@/types/DBDocumentTypes'
import { calculateFontSizeForText } from '@/utils/images/calculateFontSizeForText'
import { downloadImage } from '@/utils/images/downloadImage'
const { qr_code_uri } = defineProps<{ qr_code_uri: QRCodeDocument['uri'] }>()
const color = ref('#f43f5e')
const label = ref('Сообщите о проблеме или задайте вопрос')
const width = ref(256 * 4)
const height = ref(width.value)
// Хук для загрузки данных QR‑кода
const {
@@ -81,12 +87,28 @@ const {
// Инициализация QRCodeStyling
const qrCode = new QRCodeStyling({
width: 256 * 4,
height: 256 * 4,
width: width.value,
height: height.value,
data: '',
image: '/images/logo.svg',
imageOptions: {
margin: 20,
// hideBackgroundDots: false,
imageSize: 0.45,
},
type: 'svg',
backgroundOptions: {
color: '#ffffff',
color: 'transparent',
},
cornersSquareOptions: {
type: 'extra-rounded',
},
cornersDotOptions: {
type: 'dot',
},
dotsOptions: {
color: color.value,
type: 'classy-rounded',
},
})
@@ -126,7 +148,48 @@ const {
})
if (optimizedSvg.data) {
if (!label.value) {
// Если label пуст, возвращаем SVG без изменений
return URL.createObjectURL(new Blob([optimizedSvg.data], { type: 'image/svg+xml' }))
}
// Добавляем надпись и увеличиваем viewBox
const parser = new DOMParser()
const svgDoc = parser.parseFromString(optimizedSvg.data, 'image/svg+xml')
const svgElement = svgDoc.querySelector('svg')
if (svgElement) {
// Увеличиваем viewBox
const viewBox = svgElement.getAttribute('viewBox')?.split(' ').map(Number)
if (viewBox) {
// Расчет реального размера шрифта
const fontSize = calculateFontSizeForText(label.value, viewBox[2])
// Рассчитываем вертикальное расстояние между QR-кодом и текстом
const textMargin = Math.min(fontSize, viewBox[3] * 0.1) // Максимум 10% от высоты QR-кода
viewBox[3] += fontSize + textMargin // Увеличиваем высоту для текста
svgElement.setAttribute('viewBox', viewBox.join(' '))
// Добавляем текст
const textElement = svgDoc.createElementNS('http://www.w3.org/2000/svg', 'text')
textElement.setAttribute('x', '50%')
textElement.setAttribute('y', viewBox[3] - fontSize / 2) // Уменьшаем нижний отступ
textElement.setAttribute('text-anchor', 'middle')
textElement.setAttribute('fill', color.value)
textElement.setAttribute('font-size', fontSize.toString())
textElement.setAttribute('font-family', 'Arial, sans-serif')
textElement.textContent = label.value
svgElement.appendChild(textElement)
}
}
// Сериализуем обратно в строку
const serializer = new XMLSerializer()
const updatedSvg = serializer.serializeToString(svgDoc)
return URL.createObjectURL(new Blob([updatedSvg], { type: 'image/svg+xml' }))
} else {
throw new Error('Ошибка оптимизации SVG')
}
@@ -144,9 +207,14 @@ const {
// Скачивание PNG
async function downloadPng() {
try {
await qrCode.download({
name: sanitizedFileName.value,
extension: 'png',
if (!svgQueryData.value) {
throw new Error('SVG не загружен')
}
await downloadImage({
type: 'image/png',
filename: `${sanitizedFileName.value}.png`,
width: width.value,
url: svgQueryData.value,
})
} catch (err) {
console.error('Ошибка скачивания PNG:', err)