Add QR code tool

This commit is contained in:
Illya Marchenko 2024-04-20 12:08:07 +03:00
parent 2d348d88a0
commit 339470df5f
Signed by: stuzer05
GPG Key ID: A6ABAAA9268F9F4F
3 changed files with 101 additions and 270 deletions

@ -12,12 +12,21 @@ input[type=text], input[type=number], input[type=url], textarea {
padding: 2px; padding: 2px;
} }
input[type=file] {
font-family: monospace;
}
input:disabled, textarea:disabled { input:disabled, textarea:disabled {
background-color: #ddd; background-color: #ddd;
color: linen; color: linen;
opacity: 1; opacity: 1;
} }
select {
min-width: 100px;
padding-left: 5px;
}
button { button {
display: inline-block; display: inline-block;
padding: 0 5px; padding: 0 5px;

@ -16,6 +16,7 @@
<div class="flex flex-col mb-2"> <div class="flex flex-col mb-2">
<p><i>Other</i></p> <p><i>Other</i></p>
<a href="https://gist.stuzer.link/" class="ml-4" tag="button">Gist</a> <a href="https://gist.stuzer.link/" class="ml-4" tag="button">Gist</a>
<a href="https://cyberchef.tools.stuzer.link/" class="ml-4" tag="button">Cyberchef</a>
<a href="https://pdf.tools.stuzer.link/" class="ml-4" tag="button">PDF tools</a> <a href="https://pdf.tools.stuzer.link/" class="ml-4" tag="button">PDF tools</a>
</div> </div>
</nav> </nav>
@ -40,7 +41,7 @@ export default {
'dummy_image': 'Dummy image', 'dummy_image': 'Dummy image',
'humans_txt': 'humans.txt generator', 'humans_txt': 'humans.txt generator',
'file_base64_encode_decode': 'File base64 encode/decode', 'file_base64_encode_decode': 'File base64 encode/decode',
// 'qr_code': 'QR code', 'qr_code': 'QR code',
}, },
'Strings': { 'Strings': {
'fix_ru_en_keyboard': 'Fix ru-en keyboard', 'fix_ru_en_keyboard': 'Fix ru-en keyboard',

@ -2,253 +2,87 @@
<h2 class="tool-title">QR code</h2> <h2 class="tool-title">QR code</h2>
<hr class="mt-5 mb-5"> <hr class="mt-5 mb-5">
<form @submit.prevent="generateQRCode"> <!-- Type options -->
<div class="input-group">
<label for="text">Data:</label>
<textarea id="data" v-model="toolData.options.text" v-on:keyup="result"></textarea>
</div>
<!-- Basic options --> <!-- Basic options -->
<div> <div class="input-group">
<label for="text">Text:</label>
<input type="text" id="text" v-model="toolData.options.text" />
</div>
<div>
<label for="width">Width:</label> <label for="width">Width:</label>
<input type="number" id="width" v-model.number="toolData.options.width" /> <input id="width" v-model.number="toolData.options.width" v-on:change="result" v-on:keyup="result" type="number">
</div> </div>
<div>
<div class="input-group">
<label for="height">Height:</label> <label for="height">Height:</label>
<input type="number" id="height" v-model.number="toolData.options.height" /> <input id="height" v-model.number="toolData.options.height" v-on:change="result" v-on:keyup="result" type="number">
</div> </div>
<div>
<label for="colorDark">Dark Color:</label> <div class="input-group">
<input type="color" id="colorDark" v-model="toolData.options.colorDark" /> <label for="height">White border:</label>
<input id="quietZone" v-model.number="toolData.options.quietZone" v-on:change="result" v-on:keyup="result" type="number">
</div> </div>
<div>
<label for="colorLight">Light Color:</label> <div class="input-group">
<input type="color" id="colorLight" v-model="toolData.options.colorLight" />
</div>
<div>
<label for="correctLevel">Correct Level:</label> <label for="correctLevel">Correct Level:</label>
<select id="correctLevel" v-model="toolData.options.correctLevel"> <div>
<select id="correctLevel" v-model="toolData.options.correctLevel" v-on:change="result">
<option value="L">L</option> <option value="L">L</option>
<option value="M">M</option> <option value="M">M</option>
<option value="Q">Q</option> <option value="Q">Q</option>
<option value="H">H</option> <option value="H">H</option>
</select> </select>
</div> </div>
<!-- dotScale options -->
<div>
<label for="dotScale">Dot Scale:</label>
<input type="number" id="dotScale" v-model.number="toolData.options.dotScale" step="0.1" min="0" max="1" />
</div>
<div>
<label for="dotScaleTiming">Dot Scale Timing:</label>
<input type="number" id="dotScaleTiming" v-model.number="toolData.options.dotScaleTiming" step="0.1" min="0" max="1" />
</div>
<div>
<label for="dotScaleTiming_H">Dot Scale Timing (Horizontal):</label>
<input type="number" id="dotScaleTiming_H" v-model.number="toolData.options.dotScaleTiming_H" step="0.1" min="0" max="1" />
</div>
<div>
<label for="dotScaleTiming_V">Dot Scale Timing (Vertical):</label>
<input type="number" id="dotScaleTiming_V" v-model.number="toolData.options.dotScaleTiming_V" step="0.1" min="0" max="1" />
</div>
<div>
<label for="dotScaleA">Dot Scale A:</label>
<input type="number" id="dotScaleA" v-model.number="toolData.options.dotScaleA" step="0.1" min="0" max="1" />
</div>
<div>
<label for="dotScaleAO">Dot Scale AO:</label>
<input type="number" id="dotScaleAO" v-model.number="toolData.options.dotScaleAO" step="0.1" min="0" max="1" />
</div>
<div>
<label for="dotScaleAI">Dot Scale AI:</label>
<input type="number" id="dotScaleAI" v-model.number="toolData.options.dotScaleAI" step="0.1" min="0" max="1" />
</div>
<!-- Quiet Zone options -->
<div>
<label for="quietZone">Quiet Zone:</label>
<input type="number" id="quietZone" v-model.number="toolData.options.quietZone" />
</div>
<div>
<label for="quietZoneColor">Quiet Zone Color:</label>
<input type="color" id="quietZoneColor" v-model="toolData.options.quietZoneColor" />
</div> </div>
<!-- Logo options --> <!-- Logo options -->
<!-- Logo upload --> <div class="input-group">
<div>
<label for="logoUpload">Logo Upload:</label> <label for="logoUpload">Logo Upload:</label>
<input type="file" id="logoUpload" ref="logoUpload" @change="handleLogoUpload" accept="image/*" />
</div>
<div> <div>
<label for="logoWidth">Logo Width:</label> <input type="file" id="logoUpload" ref="logoUpload" @change="handleLogoUpload" accept="image/*">
<input type="number" id="logoWidth" v-model.number="toolData.options.logoWidth" /> <button @click="removeLogo">🞩</button>
</div> </div>
<div>
<label for="logoHeight">Logo Height:</label>
<input type="number" id="logoHeight" v-model.number="toolData.options.logoHeight" />
</div>
<div>
<label for="logoMaxWidth">Logo Max Width:</label>
<input type="number" id="logoMaxWidth" v-model.number="toolData.options.logoMaxWidth" />
</div>
<div>
<label for="logoMaxHeight">Logo Max Height:</label>
<input type="number" id="logoMaxHeight" v-model.number="toolData.options.logoMaxHeight" />
</div>
<div>
<label for="logoBackgroundColor">Logo Background Color:</label>
<input type="color" id="logoBackgroundColor" v-model="toolData.options.logoBackgroundColor" />
</div>
<div>
<label for="logoBackgroundTransparent">Logo Background Transparent:</label>
<input type="checkbox" id="logoBackgroundTransparent" v-model="toolData.options.logoBackgroundTransparent" />
</div> </div>
<!-- Background Image options --> <!-- Background Image options -->
<!-- Background Image upload --> <div class="input-group">
<div>
<label for="backgroundImageUpload">Background Image Upload:</label> <label for="backgroundImageUpload">Background Image Upload:</label>
<input type="file" id="backgroundImageUpload" ref="backgroundImageUpload" @change="handleBackgroundImageUpload" accept="image/*" />
</div>
<div> <div>
<input type="file" id="backgroundImageUpload" ref="backgroundImageUpload" @change="handleBackgroundImageUpload" accept="image/*">
<button @click="removeBackgroundImage">🞩</button>
</div>
</div>
<div class="input-group">
<label for="backgroundImageAlpha">Background Image Alpha:</label> <label for="backgroundImageAlpha">Background Image Alpha:</label>
<input type="number" id="backgroundImageAlpha" v-model.number="toolData.options.backgroundImageAlpha" step="0.1" min="0" max="1" /> <input id="backgroundImageAlpha" v-model.number="toolData.options.backgroundImageAlpha" step="0.1" min="0" max="1" v-on:change="result" v-on:keyup="result" type="number">
</div> </div>
<div>
<!--
<div class="input-group">
<label for="autoColor">Auto Color:</label> <label for="autoColor">Auto Color:</label>
<input type="checkbox" id="autoColor" v-model="toolData.options.autoColor" />
</div>
<div> <div>
<label for="autoColorDark">Auto Color Dark:</label> <input id="autoColor" v-model="toolData.options.autoColor" v-on:change="result" type="checkbox">
<input type="color" id="autoColorDark" v-model="toolData.options.autoColorDark" />
</div> </div>
<div>
<label for="autoColorLight">Auto Color Light:</label>
<input type="color" id="autoColorLight" v-model="toolData.options.autoColorLight" />
</div> </div>
-->
<!-- Colorful options -->
<div>
<label for="PO">Position Outer Color:</label>
<input type="color" id="PO" v-model="toolData.options.PO" />
</div>
<div>
<label for="PI">Position Inner Color:</label>
<input type="color" id="PI" v-model="toolData.options.PI" />
</div>
<div>
<label for="PO_TL">Position Outer Color - Top Left:</label>
<input type="color" id="PO_TL" v-model="toolData.options.PO_TL" />
</div>
<div>
<label for="PI_TL">Position Inner Color - Top Left:</label>
<input type="color" id="PI_TL" v-model="toolData.options.PI_TL" />
</div>
<div>
<label for="PO_TR">Position Outer Color - Top Right:</label>
<input type="color" id="PO_TR" v-model="toolData.options.PO_TR" />
</div>
<div>
<label for="PI_TR">Position Inner Color - Top Right:</label>
<input type="color" id="PI_TR" v-model="toolData.options.PI_TR" />
</div>
<div>
<label for="PO_BL">Position Outer Color - Bottom Left:</label>
<input type="color" id="PO_BL" v-model="toolData.options.PO_BL" />
</div>
<div>
<label for="PI_BL">Position Inner Color - Bottom Left:</label>
<input type="color" id="PI_BL" v-model="toolData.options.PI_BL" />
</div>
<!-- Alignment Color options -->
<div>
<label for="AO">Alignment Outer Color:</label>
<input type="color" id="AO" v-model="toolData.options.AO" />
</div>
<div>
<label for="AI">Alignment Inner Color:</label>
<input type="color" id="AI" v-model="toolData.options.AI" />
</div>
<!-- Timing Pattern Color options -->
<div>
<label for="timing">Timing Color:</label>
<input type="color" id="timing" v-model="toolData.options.timing" />
</div>
<div>
<label for="timing_H">Timing Color (Horizontal):</label>
<input type="color" id="timing_H" v-model="toolData.options.timing_H" />
</div>
<div>
<label for="timing_V">Timing Color (Vertical):</label>
<input type="color" id="timing_V" v-model="toolData.options.timing_V" />
</div>
<!-- Title options -->
<div>
<label for="title">Title:</label>
<input type="text" id="title" v-model="toolData.options.title" />
</div>
<div>
<label for="titleFont">Title Font:</label>
<input type="text" id="titleFont" v-model="toolData.options.titleFont" />
</div>
<div>
<label for="titleColor">Title Color:</label>
<input type="color" id="titleColor" v-model="toolData.options.titleColor" />
</div>
<div>
<label for="titleBackgroundColor">Title Background Color:</label>
<input type="color" id="titleBackgroundColor" v-model="toolData.options.titleBackgroundColor" />
</div>
<div>
<label for="titleHeight">Title Height:</label>
<input type="number" id="titleHeight" v-model.number="toolData.options.titleHeight" />
</div>
<div>
<label for="titleTop">Title Top:</label>
<input type="number" id="titleTop" v-model.number="toolData.options.titleTop" />
</div>
<!-- Subtitle options -->
<div>
<label for="subTitle">Subtitle:</label>
<input type="text" id="subTitle" v-model="toolData.options.subTitle" />
</div>
<div>
<label for="subTitleFont">Subtitle Font:</label>
<input type="text" id="subTitleFont" v-model="toolData.options.subTitleFont" />
</div>
<div>
<label for="subTitleColor">Subtitle Color:</label>
<input type="color" id="subTitleColor" v-model="toolData.options.subTitleColor" />
</div>
<div>
<label for="subTitleTop">Subtitle Top:</label>
<input type="number" id="subTitleTop" v-model.number="toolData.options.subTitleTop" />
</div>
<!-- Event Handler options -->
<!-- You can add event handler options based on your specific requirements -->
<!-- Drawing method option --> <!-- Drawing method option -->
<div> <div class="input-group">
<label for="drawer">Drawer:</label> <label for="drawer">Drawer:</label>
<div>
<select id="drawer" v-model="toolData.options.drawer"> <select id="drawer" v-model="toolData.options.drawer">
<option value="canvas">Canvas</option> <option value="canvas">Canvas</option>
<option value="svg">SVG</option> <option value="svg">SVG</option>
</select> </select>
</div> </div>
</div>
<button type="submit">Generate QR Code</button>
</form>
<hr class="mt-5 mb-5"> <hr class="mt-5 mb-5">
<div class="input-group"> <div class="input-group">
<label for="result">Result</label> <label>Result</label>
<div ref="qrcodeContainer"></div> <div ref="qrcodeContainer"></div>
</div> </div>
@ -256,77 +90,61 @@
<script> <script>
import QRCode from 'easyqrcodejs'; import QRCode from 'easyqrcodejs';
import { unproxy } from "../utils/unproxy";
export default { export default {
data() { data() {
return { return {
toolData: { toolData: {
options: { options: {
text: 'https://github.com/ushelp/EasyQRCodeJS', text: '',
width: 256, width: 256,
height: 256, height: 256,
colorDark: '#000000', correctLevel: 'L',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H,
dotScale: 1,
dotScaleTiming: 1,
dotScaleTiming_H: 0,
dotScaleTiming_V: 0,
dotScaleA: 1,
dotScaleAO: 0,
dotScaleAI: 0,
quietZone: 0,
quietZoneColor: 'rgba(0,0,0,0)',
logo: '', logo: '',
logoWidth: 0, quietZone: 10,
logoHeight: 0, logoBackgroundTransparent: true,
logoMaxWidth: 0,
logoMaxHeight: 0,
logoBackgroundColor: '#ffffff',
logoBackgroundTransparent: false,
backgroundImage: '', backgroundImage: '',
backgroundImageAlpha: 1, backgroundImageAlpha: 1,
autoColor: false, autoColor: true,
autoColorDark: 'rgba(0, 0, 0, .6)',
autoColorLight: 'rgba(255, 255, 255, .7)',
PO: '',
PI: '',
PO_TL: '',
PI_TL: '',
PO_TR: '',
PI_TR: '',
PO_BL: '',
PI_BL: '',
AO: '',
AI: '',
timing: '',
timing_H: '',
timing_V: '',
title: '',
titleFont: 'bold 16px Arial',
titleColor: '#000',
titleBackgroundColor: '#fff',
titleHeight: 0,
titleTop: 30,
subTitle: '',
subTitleFont: '14px Arial',
subTitleColor: '#4F4F4F',
subTitleTop: 0,
drawer: 'canvas', drawer: 'canvas',
}, },
}, },
}; };
}, },
mounted() {
this.toolData.options.text = ' ';
this.result();
this.toolData.options.text = '';
},
methods: { methods: {
result() { result() {
// Clear previous QR code
this.$refs.qrcodeContainer.innerHTML = '';
let options = unproxy(this.toolData.options);
// convert correction levels
options.correctLevel = {
'L': QRCode.CorrectLevel.L,
'M': QRCode.CorrectLevel.M,
'Q': QRCode.CorrectLevel.Q,
'H': QRCode.CorrectLevel.H,
}[options.correctLevel];
console.log(options.correctLevel);
// Generate the QR code
new QRCode(this.$refs.qrcodeContainer, options);
}, },
handleLogoUpload(event) { handleLogoUpload(event) {
const file = event.target.files[0]; const file = event.target.files[0];
if (file) { if (file) {
const reader = new FileReader(); const reader = new FileReader();
reader.onload = (e) => { reader.onload = (e) => {
this.toolData.options.logo = e.target.result; this.toolData.options.logo = e.target.result;
this.result();
}; };
reader.readAsDataURL(file); reader.readAsDataURL(file);
} }
@ -337,16 +155,19 @@ export default {
const reader = new FileReader(); const reader = new FileReader();
reader.onload = (e) => { reader.onload = (e) => {
this.toolData.options.backgroundImage = e.target.result; this.toolData.options.backgroundImage = e.target.result;
this.result();
}; };
reader.readAsDataURL(file); reader.readAsDataURL(file);
} }
}, },
generateQRCode() {
// Clear previous QR code
this.$refs.qrcodeContainer.innerHTML = '';
// Generate the QR code removeLogo() {
const qrcode = new QRCode(this.$refs.qrcodeContainer, this.toolData.options); this.toolData.options.logo = '';
this.result();
},
removeBackgroundImage() {
this.toolData.options.backgroundImage = '';
this.result();
}, },
} }
} }