Skip to content

Commit

Permalink
Fix QR Code Resizing Visually (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
lyqht authored Aug 5, 2023
1 parent 0a51ba8 commit 8066534
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 53 deletions.
82 changes: 42 additions & 40 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -127,34 +127,32 @@ function randomizeStyleSettings() {
}
/* export image utils */
const options = computed(() => ({
width: width.value,
height: height.value
}))
async function copyQRToClipboard() {
console.debug('Copying image to clipboard')
const qrCode = document.querySelector('#qr-code-container')
if (qrCode) {
await copyImageToClipboard(qrCode as HTMLElement)
await copyImageToClipboard(qrCode as HTMLElement, options.value)
}
}
function downloadQRImageAsPng() {
console.debug('Copying image to clipboard')
const qrCode = document.querySelector('#qr-code-container')
if (qrCode) {
downloadPngElement(qrCode as HTMLElement, 'qr-code.png', {
width: width.value,
height: height.value
})
downloadPngElement(qrCode as HTMLElement, 'qr-code.png', options.value)
}
}
function downloadQRImageAsSvg() {
console.debug('Copying image to clipboard')
const qrCode = document.querySelector('#qr-code-container')
if (qrCode) {
downloadSvgElement(qrCode as HTMLElement, 'qr-code.svg', {
width: width.value,
height: height.value
})
downloadSvgElement(qrCode as HTMLElement, 'qr-code.svg', options.value)
}
}
Expand Down Expand Up @@ -282,10 +280,7 @@ function uploadImage() {
<div class="w-full md:w-5/6">
<div class="w-full mb-8 flex flex-col items-center justify-center">
<h1 class="text-4xl">{{ $t('styled_qr_gen') }}</h1>
<button
class="p-2 mt-2 m-0 rounded-lg secondary-button"
@click="randomizeStyleSettings"
>
<button class="p-2 mt-2 m-0 rounded-lg secondary-button" @click="randomizeStyleSettings">
{{ $t('random_style') }}
</button>
</div>
Expand All @@ -294,21 +289,27 @@ function uploadImage() {
id="main-content"
class="flex flex-col items-center justify-center flex-shrink-0 w-full md:w-fit"
>
<div
id="qr-code-container"
class="grid place-items-center overflow-hidden mb-4"
:style="[
style,
{
width: width + 'px',
height: height + 'px'
}
]"
>
<StyledQRCode v-if="data" v-bind="qrCodeProps" role="img" aria-label="QR code" />
<p v-else>{{ $t('no_data') }}</p>
<div id="qr-code-container">
<div
class="grid place-items-center overflow-hidden"
:style="[
style,
{
width: '200px',
height: '200px'
}
]"
>
<StyledQRCode
v-if="data"
v-bind="{ ...qrCodeProps, width: 200, height: 200 }"
role="img"
aria-label="QR code"
/>
<p v-else>{{ $t('no_data') }}</p>
</div>
</div>
<div class="flex flex-col gap-2 items-center">
<div class="flex flex-col gap-2 items-center mt-4">
<div class="flex flex-col gap-3 items-center justify-center">
<button
id="copy-qr-image-button"
Expand Down Expand Up @@ -454,10 +455,7 @@ function uploadImage() {
</div>
<div class="w-full">
<div class="flex flex-row gap-2 items-center mb-2">
<label
class="block text-gray-700 dark:text-white text-sm font-bold"
for="image-url"
>
<label class="block text-gray-700 dark:text-white text-sm font-bold" for="image-url">
{{ $t('image_label') }}
</label>
<button class="secondary-button" @click="uploadImage">
Expand Down Expand Up @@ -507,13 +505,7 @@ function uploadImage() {
<label class="block text-gray-700 dark:text-white text-sm font-bold mb-2" for="margin">
{{ $t('margin_label') }}
</label>
<input
class="text-input"
id="margin"
type="number"
placeholder="0"
v-model="margin"
/>
<input class="text-input" id="margin" type="number" placeholder="0" v-model="margin" />
</div>
<div class="w-full">
<label class="block text-gray-700 dark:text-white text-sm font-bold mb-2" for="margin">
Expand Down Expand Up @@ -563,7 +555,12 @@ function uploadImage() {
<label class="block text-gray-700 dark:text-white text-sm font-bold">{{
$t('corners_square_color_label')
}}</label>
<input id="cornersSquareColor" type="color" class="color-input" v-model="cornersSquareOptionsColor" />
<input
id="cornersSquareColor"
type="color"
class="color-input"
v-model="cornersSquareOptionsColor"
/>
</div>
<div class="w-full">
<label class="block text-gray-700 dark:text-white text-sm font-bold">{{
Expand All @@ -588,7 +585,12 @@ function uploadImage() {
<label class="block text-gray-700 dark:text-white text-sm font-bold">{{
$t('corners_dot_color_label')
}}</label>
<input id="cornersDotColor" type="color" class="color-input" v-model="cornersDotOptionsColor" />
<input
id="cornersDotColor"
type="color"
class="color-input"
v-model="cornersDotOptionsColor"
/>
</div>
<div class="w-full">
<label class="block text-gray-700 dark:text-white text-sm font-bold">{{
Expand Down
56 changes: 43 additions & 13 deletions src/utils/convertToImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,59 @@ const defaultOptions: Options = {
height: 400
}

export async function copyImageToClipboard(element: HTMLElement) {
console.debug("Converting to blob")
const blob = await domtoimage.toBlob(element, defaultOptions)
const item = new ClipboardItem({ [blob.type]: blob });
navigator.clipboard.write([item]).then(() => {
console.log('Blob copied to clipboard');
}, (error) => {
console.error('Error copying blob to clipboard:', error);
});
const getFormattedOptions = (element: HTMLElement, options: Options): Options => {
if (options.width && options.height) {
const scale = getResizeScaleToFit(element, options.width, options.height)
return {
style: { scale, transformOrigin: 'left top', borderRadius: '48px' },
quality: 100,
...options
}
}

return defaultOptions
}

const getResizeScaleToFit = (child: HTMLElement, width: number, height: number): number => {
child.style.transformOrigin = 'center'

const scaleX = width / child.offsetWidth
const scaleY = height / child.offsetHeight

const maxScale = Math.min(scaleX, scaleY)
return maxScale
}

export async function copyImageToClipboard(element: HTMLElement, options: Options) {
console.debug('Converting to blob')
const formattedOptions = getFormattedOptions(element, options)
domtoimage.toBlob(element, formattedOptions).then((blob: Blob) => {
const item = new ClipboardItem({ [blob.type]: blob })
navigator.clipboard.write([item]).then(
() => {
console.log('Blob copied to clipboard')
},
(error) => {
console.error('Error copying blob to clipboard:', error)
}
)
})
}

export function downloadPngElement(element: HTMLElement, filename: string, options?: Options) {
domtoimage.toPng(element, options ?? defaultOptions).then((dataUrl: string) => {
export function downloadPngElement(element: HTMLElement, filename: string, options: Options) {
const formattedOptions = getFormattedOptions(element, options)
domtoimage.toPng(element, formattedOptions).then((dataUrl: string) => {
const link = document.createElement('a')
link.href = dataUrl
link.download = filename
link.click()
})
}

export function downloadSvgElement(element: HTMLElement, filename: string, options?: Options) {
export function downloadSvgElement(element: HTMLElement, filename: string, options: Options) {
const formattedOptions = getFormattedOptions(element, options)
domtoimage
.toSvg(element, options ?? defaultOptions)
.toSvg(element, formattedOptions)
.then((dataUrl: string) => {
const link = document.createElement('a')
link.href = dataUrl
Expand Down

1 comment on commit 8066534

@vercel
Copy link

@vercel vercel bot commented on 8066534 Aug 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.