qrcode.vue 3.9.1 → 3.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  /*!
2
- * qrcode.vue v3.9.1
2
+ * qrcode.vue v3.11.0
3
3
  * A Vue.js component to generate QRCode. Both support Vue 2 and Vue 3
4
4
  * © 2017-PRESENT @scopewu(https://github.com/scopewu)
5
- * MIT License.
5
+ * @license MIT.
6
6
  */
7
7
  'use strict';
8
8
 
@@ -1073,6 +1073,14 @@ function useQRCode(props) {
1073
1073
  });
1074
1074
  return { margin: margin, numCells: numCells, cells: cells, fgPath: fgPath, imageProps: imageProps, imageBorderProps: imageBorderProps };
1075
1075
  }
1076
+ function downloadDataURLAsFile(data, filename) {
1077
+ var link = document.createElement('a');
1078
+ link.download = filename;
1079
+ link.href = data;
1080
+ document.body.appendChild(link);
1081
+ link.click();
1082
+ document.body.removeChild(link);
1083
+ }
1076
1084
  var QRCodeProps = {
1077
1085
  value: {
1078
1086
  type: String,
@@ -1098,38 +1106,32 @@ var QRCodeProps = {
1098
1106
  },
1099
1107
  margin: {
1100
1108
  type: Number,
1101
- required: false,
1102
1109
  default: DEFAULT_MARGIN,
1110
+ validator: function (m) { return m >= 0; },
1103
1111
  },
1104
1112
  imageSettings: {
1105
1113
  type: Object,
1106
- required: false,
1107
1114
  default: function () { return ({}); },
1108
1115
  },
1109
1116
  gradient: {
1110
1117
  type: Boolean,
1111
- required: false,
1112
1118
  default: false,
1113
1119
  },
1114
1120
  gradientType: {
1115
1121
  type: String,
1116
- required: false,
1117
1122
  default: 'linear',
1118
1123
  validator: function (t) { return ['linear', 'radial'].indexOf(t) > -1; },
1119
1124
  },
1120
1125
  gradientStartColor: {
1121
1126
  type: String,
1122
- required: false,
1123
1127
  default: '#000',
1124
1128
  },
1125
1129
  gradientEndColor: {
1126
1130
  type: String,
1127
- required: false,
1128
1131
  default: '#fff',
1129
1132
  },
1130
1133
  radius: {
1131
1134
  type: Number,
1132
- required: false,
1133
1135
  default: 0,
1134
1136
  validator: function (r) { return !isNaN(r) && r >= 0 && r <= 0.5; },
1135
1137
  },
@@ -1147,8 +1149,9 @@ var QRCodeVueProps = __assign(__assign({}, QRCodeProps), { renderAs: {
1147
1149
  var QrcodeSvg = vue.defineComponent({
1148
1150
  name: 'QRCodeSvg',
1149
1151
  props: QRCodeProps,
1150
- setup: function (props) {
1152
+ setup: function (props, ctx) {
1151
1153
  var _a = useQRCode(props), numCells = _a.numCells, fgPath = _a.fgPath, imageProps = _a.imageProps, imageBorderProps = _a.imageBorderProps;
1154
+ var svgEl = vue.ref();
1152
1155
  var uid = getUid(props.id);
1153
1156
  var qrGradientId = "qrcode.vue-gradient-".concat(uid);
1154
1157
  var qrLogoClipPathId = "qrcode.vue-logo-clip-path-".concat(uid);
@@ -1197,15 +1200,32 @@ var QrcodeSvg = vue.defineComponent({
1197
1200
  }),
1198
1201
  ]);
1199
1202
  });
1203
+ var getSvgDataURL = function (svg) { return 'data:image/svg+xml;charset=utf-8,' +
1204
+ encodeURIComponent('<?xml version="1.0" standalone="no"?>' + new XMLSerializer().serializeToString(svg)); };
1205
+ ctx.expose({
1206
+ toDataURL: function () {
1207
+ var svg = svgEl.value;
1208
+ if (!svg)
1209
+ return;
1210
+ return getSvgDataURL(svg);
1211
+ },
1212
+ download: function (filename) {
1213
+ if (filename === void 0) { filename = 'qrcode.svg'; }
1214
+ var svg = svgEl.value;
1215
+ if (!svg)
1216
+ return;
1217
+ downloadDataURLAsFile(getSvgDataURL(svg), filename);
1218
+ },
1219
+ });
1200
1220
  return function () { return vue.h('svg', {
1221
+ ref: svgEl,
1201
1222
  width: props.size,
1202
1223
  height: props.size,
1203
1224
  xmlns: 'http://www.w3.org/2000/svg',
1204
1225
  viewBox: "0 0 ".concat(numCells.value, " ").concat(numCells.value),
1205
1226
  role: 'img',
1206
- 'aria-label': props.value,
1207
1227
  }, [
1208
- vue.h('defs', {}, [gradientVNode.value, clipPathVNode.value]),
1228
+ vue.h('defs', {}, [gradientVNode.value, clipPathVNode.value].filter(Boolean)),
1209
1229
  vue.h('rect', {
1210
1230
  width: '100%',
1211
1231
  height: '100%',
@@ -1224,7 +1244,7 @@ var QrcodeSvg = vue.defineComponent({
1224
1244
  rx: imageBorderProps.value.borderRadius,
1225
1245
  ry: imageBorderProps.value.borderRadius,
1226
1246
  }),
1227
- props.imageSettings.src && imageProps.value && vue.h('image', __assign(__assign({ href: props.imageSettings.src }, imageProps.value), (imageProps.value.borderRadius > 0 ? { 'clip-path': "url(#".concat(qrLogoClipPathId, ")") } : {}))),
1247
+ props.imageSettings.src && imageProps.value && vue.h('image', __assign({ href: props.imageSettings.src, crossorigin: props.imageSettings.crossOrigin, 'clip-path': imageProps.value.borderRadius > 0 ? "url(#".concat(qrLogoClipPathId, ")") : void 0 }, imageProps.value)),
1228
1248
  ]); };
1229
1249
  },
1230
1250
  });
@@ -1310,12 +1330,23 @@ var QrcodeCanvas = vue.defineComponent({
1310
1330
  }
1311
1331
  };
1312
1332
  vue.onMounted(generate);
1313
- vue.watchEffect(generate);
1333
+ vue.watchEffect(generate, { flush: 'post' });
1334
+ ctx.expose({
1335
+ toDataURL: function (type, quality) { var _a; return (_a = canvasEl.value) === null || _a === void 0 ? void 0 : _a.toDataURL(type, quality); },
1336
+ download: function (filename) {
1337
+ if (filename === void 0) { filename = 'qrcode.png'; }
1338
+ var canvas = canvasEl.value;
1339
+ if (!canvas)
1340
+ return;
1341
+ downloadDataURLAsFile(canvas.toDataURL('image/png'), filename);
1342
+ },
1343
+ });
1314
1344
  return function () { return vue.h(vue.Fragment, [
1315
- vue.h('canvas', __assign(__assign({}, ctx.attrs), { ref: canvasEl, role: 'img', 'aria-label': props.value, style: __assign(__assign({}, ctx.attrs.style), { width: "".concat(props.size, "px"), height: "".concat(props.size, "px") }) })),
1345
+ vue.h('canvas', __assign(__assign({}, ctx.attrs), { ref: canvasEl, role: 'img', style: __assign(__assign({}, ctx.attrs.style), { width: "".concat(props.size, "px"), height: "".concat(props.size, "px") }) })),
1316
1346
  props.imageSettings.src && vue.h('img', {
1317
1347
  ref: imageEl,
1318
1348
  src: props.imageSettings.src,
1349
+ crossorigin: props.imageSettings.crossOrigin,
1319
1350
  style: { display: 'none' },
1320
1351
  onLoad: generate,
1321
1352
  })
@@ -1325,8 +1356,14 @@ var QrcodeCanvas = vue.defineComponent({
1325
1356
  var QrcodeVue = vue.defineComponent({
1326
1357
  name: 'Qrcode',
1327
1358
  props: QRCodeVueProps,
1328
- setup: function (props) {
1359
+ setup: function (props, ctx) {
1360
+ var childRef = vue.ref();
1361
+ ctx.expose({
1362
+ toDataURL: function (type, quality) { var _a, _b; return (_b = (_a = childRef.value) === null || _a === void 0 ? void 0 : _a.toDataURL) === null || _b === void 0 ? void 0 : _b.call(_a, type, quality); },
1363
+ download: function (filename) { var _a, _b; return (_b = (_a = childRef.value) === null || _a === void 0 ? void 0 : _a.download) === null || _b === void 0 ? void 0 : _b.call(_a, filename); },
1364
+ });
1329
1365
  return function () { return vue.h(props.renderAs === 'svg' ? QrcodeSvg : QrcodeCanvas, {
1366
+ ref: childRef,
1330
1367
  value: props.value,
1331
1368
  size: props.size,
1332
1369
  margin: props.margin,
@@ -1,10 +1,10 @@
1
1
  /*!
2
- * qrcode.vue v3.9.1
2
+ * qrcode.vue v3.11.0
3
3
  * A Vue.js component to generate QRCode. Both support Vue 2 and Vue 3
4
4
  * © 2017-PRESENT @scopewu(https://github.com/scopewu)
5
- * MIT License.
5
+ * @license MIT.
6
6
  */
7
- import { defineComponent, computed, h, ref, onMounted, watchEffect, Fragment } from 'vue';
7
+ import { defineComponent, ref, computed, h, onMounted, watchEffect, Fragment } from 'vue';
8
8
 
9
9
  /******************************************************************************
10
10
  Copyright (c) Microsoft Corporation.
@@ -1069,6 +1069,14 @@ function useQRCode(props) {
1069
1069
  });
1070
1070
  return { margin: margin, numCells: numCells, cells: cells, fgPath: fgPath, imageProps: imageProps, imageBorderProps: imageBorderProps };
1071
1071
  }
1072
+ function downloadDataURLAsFile(data, filename) {
1073
+ var link = document.createElement('a');
1074
+ link.download = filename;
1075
+ link.href = data;
1076
+ document.body.appendChild(link);
1077
+ link.click();
1078
+ document.body.removeChild(link);
1079
+ }
1072
1080
  var QRCodeProps = {
1073
1081
  value: {
1074
1082
  type: String,
@@ -1094,38 +1102,32 @@ var QRCodeProps = {
1094
1102
  },
1095
1103
  margin: {
1096
1104
  type: Number,
1097
- required: false,
1098
1105
  default: DEFAULT_MARGIN,
1106
+ validator: function (m) { return m >= 0; },
1099
1107
  },
1100
1108
  imageSettings: {
1101
1109
  type: Object,
1102
- required: false,
1103
1110
  default: function () { return ({}); },
1104
1111
  },
1105
1112
  gradient: {
1106
1113
  type: Boolean,
1107
- required: false,
1108
1114
  default: false,
1109
1115
  },
1110
1116
  gradientType: {
1111
1117
  type: String,
1112
- required: false,
1113
1118
  default: 'linear',
1114
1119
  validator: function (t) { return ['linear', 'radial'].indexOf(t) > -1; },
1115
1120
  },
1116
1121
  gradientStartColor: {
1117
1122
  type: String,
1118
- required: false,
1119
1123
  default: '#000',
1120
1124
  },
1121
1125
  gradientEndColor: {
1122
1126
  type: String,
1123
- required: false,
1124
1127
  default: '#fff',
1125
1128
  },
1126
1129
  radius: {
1127
1130
  type: Number,
1128
- required: false,
1129
1131
  default: 0,
1130
1132
  validator: function (r) { return !isNaN(r) && r >= 0 && r <= 0.5; },
1131
1133
  },
@@ -1143,8 +1145,9 @@ var QRCodeVueProps = __assign(__assign({}, QRCodeProps), { renderAs: {
1143
1145
  var QrcodeSvg = defineComponent({
1144
1146
  name: 'QRCodeSvg',
1145
1147
  props: QRCodeProps,
1146
- setup: function (props) {
1148
+ setup: function (props, ctx) {
1147
1149
  var _a = useQRCode(props), numCells = _a.numCells, fgPath = _a.fgPath, imageProps = _a.imageProps, imageBorderProps = _a.imageBorderProps;
1150
+ var svgEl = ref();
1148
1151
  var uid = getUid(props.id);
1149
1152
  var qrGradientId = "qrcode.vue-gradient-".concat(uid);
1150
1153
  var qrLogoClipPathId = "qrcode.vue-logo-clip-path-".concat(uid);
@@ -1193,15 +1196,32 @@ var QrcodeSvg = defineComponent({
1193
1196
  }),
1194
1197
  ]);
1195
1198
  });
1199
+ var getSvgDataURL = function (svg) { return 'data:image/svg+xml;charset=utf-8,' +
1200
+ encodeURIComponent('<?xml version="1.0" standalone="no"?>' + new XMLSerializer().serializeToString(svg)); };
1201
+ ctx.expose({
1202
+ toDataURL: function () {
1203
+ var svg = svgEl.value;
1204
+ if (!svg)
1205
+ return;
1206
+ return getSvgDataURL(svg);
1207
+ },
1208
+ download: function (filename) {
1209
+ if (filename === void 0) { filename = 'qrcode.svg'; }
1210
+ var svg = svgEl.value;
1211
+ if (!svg)
1212
+ return;
1213
+ downloadDataURLAsFile(getSvgDataURL(svg), filename);
1214
+ },
1215
+ });
1196
1216
  return function () { return h('svg', {
1217
+ ref: svgEl,
1197
1218
  width: props.size,
1198
1219
  height: props.size,
1199
1220
  xmlns: 'http://www.w3.org/2000/svg',
1200
1221
  viewBox: "0 0 ".concat(numCells.value, " ").concat(numCells.value),
1201
1222
  role: 'img',
1202
- 'aria-label': props.value,
1203
1223
  }, [
1204
- h('defs', {}, [gradientVNode.value, clipPathVNode.value]),
1224
+ h('defs', {}, [gradientVNode.value, clipPathVNode.value].filter(Boolean)),
1205
1225
  h('rect', {
1206
1226
  width: '100%',
1207
1227
  height: '100%',
@@ -1220,7 +1240,7 @@ var QrcodeSvg = defineComponent({
1220
1240
  rx: imageBorderProps.value.borderRadius,
1221
1241
  ry: imageBorderProps.value.borderRadius,
1222
1242
  }),
1223
- props.imageSettings.src && imageProps.value && h('image', __assign(__assign({ href: props.imageSettings.src }, imageProps.value), (imageProps.value.borderRadius > 0 ? { 'clip-path': "url(#".concat(qrLogoClipPathId, ")") } : {}))),
1243
+ props.imageSettings.src && imageProps.value && h('image', __assign({ href: props.imageSettings.src, crossorigin: props.imageSettings.crossOrigin, 'clip-path': imageProps.value.borderRadius > 0 ? "url(#".concat(qrLogoClipPathId, ")") : void 0 }, imageProps.value)),
1224
1244
  ]); };
1225
1245
  },
1226
1246
  });
@@ -1306,12 +1326,23 @@ var QrcodeCanvas = defineComponent({
1306
1326
  }
1307
1327
  };
1308
1328
  onMounted(generate);
1309
- watchEffect(generate);
1329
+ watchEffect(generate, { flush: 'post' });
1330
+ ctx.expose({
1331
+ toDataURL: function (type, quality) { var _a; return (_a = canvasEl.value) === null || _a === void 0 ? void 0 : _a.toDataURL(type, quality); },
1332
+ download: function (filename) {
1333
+ if (filename === void 0) { filename = 'qrcode.png'; }
1334
+ var canvas = canvasEl.value;
1335
+ if (!canvas)
1336
+ return;
1337
+ downloadDataURLAsFile(canvas.toDataURL('image/png'), filename);
1338
+ },
1339
+ });
1310
1340
  return function () { return h(Fragment, [
1311
- h('canvas', __assign(__assign({}, ctx.attrs), { ref: canvasEl, role: 'img', 'aria-label': props.value, style: __assign(__assign({}, ctx.attrs.style), { width: "".concat(props.size, "px"), height: "".concat(props.size, "px") }) })),
1341
+ h('canvas', __assign(__assign({}, ctx.attrs), { ref: canvasEl, role: 'img', style: __assign(__assign({}, ctx.attrs.style), { width: "".concat(props.size, "px"), height: "".concat(props.size, "px") }) })),
1312
1342
  props.imageSettings.src && h('img', {
1313
1343
  ref: imageEl,
1314
1344
  src: props.imageSettings.src,
1345
+ crossorigin: props.imageSettings.crossOrigin,
1315
1346
  style: { display: 'none' },
1316
1347
  onLoad: generate,
1317
1348
  })
@@ -1321,8 +1352,14 @@ var QrcodeCanvas = defineComponent({
1321
1352
  var QrcodeVue = defineComponent({
1322
1353
  name: 'Qrcode',
1323
1354
  props: QRCodeVueProps,
1324
- setup: function (props) {
1355
+ setup: function (props, ctx) {
1356
+ var childRef = ref();
1357
+ ctx.expose({
1358
+ toDataURL: function (type, quality) { var _a, _b; return (_b = (_a = childRef.value) === null || _a === void 0 ? void 0 : _a.toDataURL) === null || _b === void 0 ? void 0 : _b.call(_a, type, quality); },
1359
+ download: function (filename) { var _a, _b; return (_b = (_a = childRef.value) === null || _a === void 0 ? void 0 : _a.download) === null || _b === void 0 ? void 0 : _b.call(_a, filename); },
1360
+ });
1325
1361
  return function () { return h(props.renderAs === 'svg' ? QrcodeSvg : QrcodeCanvas, {
1362
+ ref: childRef,
1326
1363
  value: props.value,
1327
1364
  size: props.size,
1328
1365
  margin: props.margin,
package/llms.txt ADDED
@@ -0,0 +1,30 @@
1
+ # qrcode.vue
2
+
3
+ > A Vue.js component to generate QR codes. Version 3.x supports Vue 3 (peer dependency `vue@^3.0.0`, no runtime dependencies); Vue 2 apps use qrcode.vue 1.x. Renders to `<canvas>` or `<svg>`, with gradients, rounded modules, and embedded logo images. MIT licensed.
4
+
5
+ Install: `npm install --save qrcode.vue`
6
+
7
+ ```ts
8
+ import QrcodeVue, { QrcodeCanvas, QrcodeSvg } from 'qrcode.vue'
9
+ ```
10
+
11
+ Key props: `value` (required), `size` (default 100), `render-as` (`canvas`|`svg`, svg works on SSR), `level` (L/M/Q/H, default L), `margin`, `background`/`foreground`, `gradient` + `gradient-type` + `gradient-start-color`/`gradient-end-color`, `radius` (0–0.5 rounded modules), `image-settings` (embedded logo: `src`, `x`/`y`, `width`/`height`, `excavate`, `borderRadius`, `crossOrigin`), `id` (SSR-safe internal SVG ids, pass `useId()`). Template-ref methods: `toDataURL()`, `download(filename)`.
12
+
13
+ ## Docs
14
+
15
+ - [README (English)](https://github.com/scopewu/qrcode.vue/blob/main/README.md): install, usage, full props reference, template ref methods
16
+ - [README (简体中文)](https://github.com/scopewu/qrcode.vue/blob/main/README-zh_cn.md)
17
+ - [README (繁體中文)](https://github.com/scopewu/qrcode.vue/blob/main/README.zh-hant.md)
18
+ - [README (日本語)](https://github.com/scopewu/qrcode.vue/blob/main/README-ja.md)
19
+ - [CHANGELOG](https://github.com/scopewu/qrcode.vue/blob/main/CHANGELOG.md): version history and breaking changes
20
+
21
+ ## AI assistant resources
22
+
23
+ - [SKILL.md](https://github.com/scopewu/qrcode.vue/blob/main/skills/qrcode-vue/SKILL.md): Agent Skills–format skill with props table, recipes, and pitfalls. Also shipped in the npm package at `node_modules/qrcode.vue/skills/qrcode-vue/SKILL.md`.
24
+ - [Live demo](https://qr-vue.tie.pub): interactive playground for all props
25
+
26
+ ## Source
27
+
28
+ - [src/index.ts](https://github.com/scopewu/qrcode.vue/blob/main/src/index.ts): the whole library — components, props, exported types (`Level`, `RenderAs`, `GradientType`, `ImageSettings`)
29
+ - [npm package](https://www.npmjs.com/package/qrcode.vue)
30
+ - [Repository](https://github.com/scopewu/qrcode.vue)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qrcode.vue",
3
- "version": "3.9.1",
3
+ "version": "3.11.0",
4
4
  "description": "A Vue.js component to generate QRCode. Both support Vue 2 and Vue 3",
5
5
  "type": "module",
6
6
  "main": "./dist/qrcode.vue.cjs.js",
@@ -18,8 +18,10 @@
18
18
  "./package": "./package.json"
19
19
  },
20
20
  "scripts": {
21
- "dev": "rsbuild dev",
21
+ "dev": "node example/scripts/generate-i18n.js && rsbuild dev",
22
22
  "build": "rollup -c",
23
+ "build:example": "node example/scripts/generate-i18n.js && rsbuild build",
24
+ "generate:i18n": "node example/scripts/generate-i18n.js",
23
25
  "test": "rstest"
24
26
  },
25
27
  "repository": "https://github.com/scopewu/qrcode.vue.git",
@@ -34,9 +36,10 @@
34
36
  ],
35
37
  "files": [
36
38
  "/dist",
39
+ "/skills",
37
40
  "CHANGELOG.md",
38
- "README-zh_cn.md",
39
- "README.md"
41
+ "README.md",
42
+ "llms.txt"
40
43
  ],
41
44
  "author": "scope.wu <scope.wu@gmail.com>",
42
45
  "license": "MIT",
@@ -50,13 +53,13 @@
50
53
  "dependencies": {},
51
54
  "devDependencies": {
52
55
  "@rollup/plugin-terser": "^1.0.0",
53
- "@rsbuild/core": "^2.0.5",
54
- "@rstest/core": "^0.9.10",
55
- "@vue/test-utils": "^2.4.10",
56
- "happy-dom": "^20.9.0",
57
- "rollup": "^4.60.3",
56
+ "@rsbuild/core": "^2.2.1",
57
+ "@rstest/core": "^0.11.10",
58
+ "@vue/test-utils": "^2.5.0",
59
+ "happy-dom": "^20.11.12",
60
+ "rollup": "^4.63.1",
58
61
  "rollup-plugin-typescript2": "^0.37.0",
59
62
  "typescript": "^5.9.3",
60
- "vue": "^3.5.34"
63
+ "vue": "^3.5.42"
61
64
  }
62
65
  }
@@ -0,0 +1,129 @@
1
+ ---
2
+ name: qrcode-vue
3
+ description: Generate QR codes in Vue 3 apps with the qrcode.vue component library. Use this skill whenever the user wants to render, style, or download a QR code in Vue — including QR with a logo/image, gradient or rounded modules, SVG vs canvas output, SSR/Nuxt usage, or TypeScript typing — even if they don't name the library explicitly.
4
+ ---
5
+
6
+ # qrcode.vue
7
+
8
+ A Vue.js component to generate QR codes. Version 3.x targets **Vue 3 only** (Vue 2 apps must use qrcode.vue 1.x). Peer dependency: `vue@^3.0.0`. No other runtime dependencies.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install --save qrcode.vue # or: yarn add qrcode.vue
14
+ ```
15
+
16
+ ## Exports
17
+
18
+ ```ts
19
+ import QrcodeVue, { QrcodeCanvas, QrcodeSvg } from 'qrcode.vue'
20
+ import type { Level, RenderAs, GradientType, ImageSettings } from 'qrcode.vue'
21
+ ```
22
+
23
+ - `QrcodeVue` (default) — switches between canvas and SVG via the `render-as` prop.
24
+ - `QrcodeCanvas` / `QrcodeSvg` (named) — single-renderer components. Prefer these when the renderer is fixed.
25
+
26
+ CommonJS / CDN note: exports are named since 3.5 — use `require('qrcode.vue').default` or `QrcodeVue.default` when loading the UMD bundle.
27
+
28
+ ## Props
29
+
30
+ All three components share these props (`render-as` exists only on `QrcodeVue`):
31
+
32
+ | Prop | Type | Default | Notes |
33
+ |------|------|---------|-------|
34
+ | `value` | `string` | `''` | **Required.** QR content. |
35
+ | `size` | `number` | `100` | Element size in px. |
36
+ | `render-as` | `'canvas' \| 'svg'` | `'canvas'` | `QrcodeVue` only. Use `'svg'` for SSR. |
37
+ | `level` | `'L' \| 'M' \| 'Q' \| 'H'` | `'L'` | Error correction. Use `'H'` when embedding a logo. |
38
+ | `background` | `string` | `'#fff'` | Background color. |
39
+ | `foreground` | `string` | `'#000'` | Module color (ignored when `gradient` is on). |
40
+ | `margin` | `number` | `0` | Quiet-zone width in modules, `>= 0`. |
41
+ | `radius` | `number` | `0` | Module corner radius, `0`–`0.5`. `0.5` = circular modules. Inner corners between adjacent modules stay sharp. |
42
+ | `gradient` | `boolean` | `false` | Enable gradient fill. |
43
+ | `gradient-type` | `'linear' \| 'radial'` | `'linear'` | Gradient direction type. |
44
+ | `gradient-start-color` | `string` | `'#000'` | Gradient start. |
45
+ | `gradient-end-color` | `string` | `'#fff'` | Gradient end. |
46
+ | `image-settings` | `ImageSettings` | `{}` | Embedded logo, see below. |
47
+ | `id` | `string` | `undefined` | ID base for internal SVG defs. Pass `useId()` (Vue 3.5+) for SSR hydration safety. |
48
+
49
+ ```ts
50
+ type ImageSettings = {
51
+ src: string // image URL
52
+ x?: number; y?: number // offset; centered when omitted
53
+ height?: number; width?: number // default: 10% of `size`
54
+ excavate?: boolean // clear modules behind the image
55
+ borderRadius?: number
56
+ crossOrigin?: 'anonymous' | 'use-credentials' | '' // required to export canvas with a cross-origin logo
57
+ }
58
+ ```
59
+
60
+ ## Template ref methods
61
+
62
+ Expose `toDataURL` and `download` on all components (`QrcodeVue` forwards to the active renderer):
63
+
64
+ ```html
65
+ <script setup>
66
+ import { ref } from 'vue'
67
+ import { QrcodeCanvas } from 'qrcode.vue'
68
+
69
+ const qrRef = ref()
70
+ const save = () => qrRef.value?.download('qrcode.png')
71
+ </script>
72
+
73
+ <template>
74
+ <qrcode-canvas ref="qrRef" value="https://example.com" />
75
+ <button @click="save">Download</button>
76
+ </template>
77
+ ```
78
+
79
+ - Canvas: `toDataURL(type?, quality?)`, `download(filename?)` → PNG.
80
+ - SVG: `toDataURL()`, `download(filename?)` → SVG.
81
+
82
+ ## Recipes
83
+
84
+ QR with a centered logo — always raise `level` so the code stays scannable:
85
+
86
+ ```html
87
+ <qrcode-svg
88
+ value="https://example.com"
89
+ :size="300"
90
+ level="H"
91
+ :image-settings="{ src: logoUrl, width: 60, height: 60, excavate: true, borderRadius: 8 }"
92
+ />
93
+ ```
94
+
95
+ Gradient + rounded modules:
96
+
97
+ ```html
98
+ <qrcode-vue
99
+ value="https://example.com"
100
+ render-as="svg"
101
+ gradient
102
+ gradient-type="radial"
103
+ gradient-start-color="#000000"
104
+ gradient-end-color="#38bdf8"
105
+ :radius="0.35"
106
+ />
107
+ ```
108
+
109
+ SSR / Nuxt — render as SVG and pass a stable `id`:
110
+
111
+ ```html
112
+ <script setup>
113
+ import { useId } from 'vue'
114
+ import { QrcodeSvg } from 'qrcode.vue'
115
+ const uid = useId() // Vue 3.5+; on older versions pass any unique string
116
+ </script>
117
+
118
+ <template>
119
+ <qrcode-svg value="https://example.com" :id="uid" />
120
+ </template>
121
+ ```
122
+
123
+ ## Pitfalls
124
+
125
+ - **Vue 2**: do not use 3.x — install `qrcode.vue@1.x`.
126
+ - **Tainted canvas**: with a cross-origin logo, set `imageSettings.crossOrigin: 'anonymous'` and ensure the image server sends `Access-Control-Allow-Origin`, or `toDataURL`/`download` throws `SecurityError`.
127
+ - **SSR hydration**: without an explicit `id`, internal SVG IDs come from a module-level counter and can mismatch between server and client. Always pass `useId()` under SSR.
128
+ - **Scannability**: keep contrast high between `foreground`/`gradient` and `background`; a logo covering too much area breaks scanning even at level `H`.
129
+ - **Multiple instances with gradients**: `id` keeps each SVG's `<defs>` unique; collisions cause one QR's gradient to leak into another.