qrcode.vue 3.10.0 → 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.
- package/CHANGELOG.md +8 -0
- package/README.md +9 -0
- package/README.zh-hant.md +9 -0
- package/dist/qrcode.vue.browser.js +2 -2
- package/dist/qrcode.vue.browser.min.js +2 -2
- package/dist/qrcode.vue.cjs.js +2 -2
- package/dist/qrcode.vue.esm.js +2 -2
- package/llms.txt +30 -0
- package/package.json +13 -12
- package/skills/qrcode-vue/SKILL.md +129 -0
- package/README-zh_cn.md +0 -360
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## [3.11.0] - 2026-09-07
|
|
2
|
+
|
|
3
|
+
### Feature
|
|
4
|
+
|
|
5
|
+
- Add AI assistant resources: Agent Skills-format skill (`skills/qrcode-vue/SKILL.md`) and `llms.txt` (llmstxt.org format), both shipped in the npm package.
|
|
6
|
+
- Serve `llms.txt` at the demo site root (`https://qr-vue.tie.pub/llms.txt`) via rsbuild `output.copy`.
|
|
7
|
+
- Document the AI / LLM resources in all four READMEs (en, zh_cn, zh-hant, ja).
|
|
8
|
+
|
|
1
9
|
## [3.10.0] - 2026-06-13
|
|
2
10
|
|
|
3
11
|
### Feature
|
package/README.md
CHANGED
|
@@ -353,6 +353,15 @@ Vue.createApp({
|
|
|
353
353
|
</script>
|
|
354
354
|
```
|
|
355
355
|
|
|
356
|
+
## AI / LLM support
|
|
357
|
+
|
|
358
|
+
qrcode.vue ships resources that help AI coding assistants use this library correctly:
|
|
359
|
+
|
|
360
|
+
- [`llms.txt`](./llms.txt) — a compact, LLM-friendly summary of the library in [llmstxt.org](https://llmstxt.org) format.
|
|
361
|
+
- [`skills/qrcode-vue/SKILL.md`](./skills/qrcode-vue/SKILL.md) — an [Agent Skills](https://agentskills.io)–format skill with the props table, recipes, and pitfalls. Copy it into your assistant's skills directory (e.g. `~/.agents/skills/` or `.claude/skills/`) to give the agent expert knowledge of qrcode.vue.
|
|
362
|
+
|
|
363
|
+
Both files are also included in the npm package.
|
|
364
|
+
|
|
356
365
|
## License
|
|
357
366
|
|
|
358
367
|
copyright © 2021 @scopewu, license by [MIT](https://github.com/scopewu/qrcode.vue/blob/main/LICENSE)
|
package/README.zh-hant.md
CHANGED
|
@@ -355,6 +355,15 @@ Vue.createApp({
|
|
|
355
355
|
</script>
|
|
356
356
|
```
|
|
357
357
|
|
|
358
|
+
## AI / 大模型支援
|
|
359
|
+
|
|
360
|
+
qrcode.vue 提供了幫助 AI 編程助手正確使用本組件庫的資源:
|
|
361
|
+
|
|
362
|
+
- [`llms.txt`](./llms.txt) — 遵循 [llmstxt.org](https://llmstxt.org) 格式的精簡版庫說明,適合大模型閱讀。
|
|
363
|
+
- [`skills/qrcode-vue/SKILL.md`](./skills/qrcode-vue/SKILL.md) — 符合 [Agent Skills](https://agentskills.io) 規範的技能文件,包含 props 速查表、常用範例和常見坑。將其複製到助手的技能目錄(如 `~/.agents/skills/` 或 `.claude/skills/`),即可讓 AI 助手掌握 qrcode.vue 的用法。
|
|
364
|
+
|
|
365
|
+
這兩個文件也隨 npm 套件一起發佈。
|
|
366
|
+
|
|
358
367
|
## 軟件許可
|
|
359
368
|
|
|
360
369
|
copyright © 2021 scopewu, license by [MIT](https://github.com/scopewu/qrcode.vue/blob/main/LICENSE)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* qrcode.vue v3.
|
|
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
|
|
5
|
+
* @license MIT.
|
|
6
6
|
*/
|
|
7
7
|
(function (global, factory) {
|
|
8
8
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('vue')) :
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* qrcode.vue v3.
|
|
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
|
|
5
|
+
* @license MIT.
|
|
6
6
|
*/
|
|
7
7
|
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("vue")):"function"==typeof define&&define.amd?define(["exports","vue"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).QrcodeVue={},e.Vue)}(this,function(e,t){"use strict";var r,n=function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;n>r;r++)for(var o in t=arguments[r])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e},n.apply(this,arguments)};"function"==typeof SuppressedError&&SuppressedError,function(e){var t=function(){function t(e,r,n,i){if(this.version=e,this.errorCorrectionLevel=r,this.modules=[],this.isFunction=[],t.MIN_VERSION>e||e>t.MAX_VERSION)throw new RangeError("Version value out of range");if(-1>i||i>7)throw new RangeError("Mask value out of range");this.size=4*e+17;for(var a=[],u=0;this.size>u;u++)a.push(!1);for(u=0;this.size>u;u++)this.modules.push(a.slice()),this.isFunction.push(a.slice());this.drawFunctionPatterns();var s=this.addEccAndInterleave(n);if(this.drawCodewords(s),-1==i){var l=1e9;for(u=0;8>u;u++){this.applyMask(u),this.drawFormatBits(u);var d=this.getPenaltyScore();l>d&&(i=u,l=d),this.applyMask(u)}}o(i>=0&&7>=i),this.mask=i,this.applyMask(i),this.drawFormatBits(i),this.isFunction=[]}return t.encodeText=function(r,n){var o=e.QrSegment.makeSegments(r);return t.encodeSegments(o,n)},t.encodeBinary=function(r,n){var o=e.QrSegment.makeBytes(r);return t.encodeSegments([o],n)},t.encodeSegments=function(e,n,a,u,s,l){if(void 0===a&&(a=1),void 0===u&&(u=40),void 0===s&&(s=-1),void 0===l&&(l=!0),t.MIN_VERSION>a||a>u||u>t.MAX_VERSION||-1>s||s>7)throw new RangeError("Invalid value");var d,c;for(d=a;;d++){var h=8*t.getNumDataCodewords(d,n),f=i.getTotalBits(e,d);if(h>=f){c=f;break}if(d>=u)throw new RangeError("Data too long")}for(var v=0,g=[t.Ecc.MEDIUM,t.Ecc.QUARTILE,t.Ecc.HIGH];g.length>v;v++){var p=g[v];l&&c<=8*t.getNumDataCodewords(d,p)&&(n=p)}for(var m=[],w=0,y=e;y.length>w;w++){var E=y[w];r(E.mode.modeBits,4,m),r(E.numChars,E.mode.numCharCountBits(d),m);for(var C=0,R=E.getData();R.length>C;C++){m.push(R[C])}}o(m.length==c);var M=8*t.getNumDataCodewords(d,n);o(M>=m.length),r(0,Math.min(4,M-m.length),m),r(0,(8-m.length%8)%8,m),o(m.length%8==0);for(var S=236;M>m.length;S^=253)r(S,8,m);for(var A=[];m.length>8*A.length;)A.push(0);return m.forEach(function(e,t){return A[t>>>3]|=e<<7-(7&t)}),new t(d,n,A,s)},t.prototype.getModule=function(e,t){return e>=0&&this.size>e&&t>=0&&this.size>t&&this.modules[t][e]},t.prototype.getModules=function(){return this.modules},t.prototype.drawFunctionPatterns=function(){for(var e=0;this.size>e;e++)this.setFunctionModule(6,e,e%2==0),this.setFunctionModule(e,6,e%2==0);this.drawFinderPattern(3,3),this.drawFinderPattern(this.size-4,3),this.drawFinderPattern(3,this.size-4);var t=this.getAlignmentPatternPositions(),r=t.length;for(e=0;r>e;e++)for(var n=0;r>n;n++)0==e&&0==n||0==e&&n==r-1||e==r-1&&0==n||this.drawAlignmentPattern(t[e],t[n]);this.drawFormatBits(0),this.drawVersion()},t.prototype.drawFormatBits=function(e){for(var t=this.errorCorrectionLevel.formatBits<<3|e,r=t,i=0;10>i;i++)r=r<<1^1335*(r>>>9);var a=21522^(t<<10|r);o(a>>>15==0);for(i=0;5>=i;i++)this.setFunctionModule(8,i,n(a,i));this.setFunctionModule(8,7,n(a,6)),this.setFunctionModule(8,8,n(a,7)),this.setFunctionModule(7,8,n(a,8));for(i=9;15>i;i++)this.setFunctionModule(14-i,8,n(a,i));for(i=0;8>i;i++)this.setFunctionModule(this.size-1-i,8,n(a,i));for(i=8;15>i;i++)this.setFunctionModule(8,this.size-15+i,n(a,i));this.setFunctionModule(8,this.size-8,!0)},t.prototype.drawVersion=function(){if(this.version>=7){for(var e=this.version,t=0;12>t;t++)e=e<<1^7973*(e>>>11);var r=this.version<<12|e;o(r>>>18==0);for(t=0;18>t;t++){var i=n(r,t),a=this.size-11+t%3,u=Math.floor(t/3);this.setFunctionModule(a,u,i),this.setFunctionModule(u,a,i)}}},t.prototype.drawFinderPattern=function(e,t){for(var r=-4;4>=r;r++)for(var n=-4;4>=n;n++){var o=Math.max(Math.abs(n),Math.abs(r)),i=e+n,a=t+r;i>=0&&this.size>i&&a>=0&&this.size>a&&this.setFunctionModule(i,a,2!=o&&4!=o)}},t.prototype.drawAlignmentPattern=function(e,t){for(var r=-2;2>=r;r++)for(var n=-2;2>=n;n++)this.setFunctionModule(e+n,t+r,1!=Math.max(Math.abs(n),Math.abs(r)))},t.prototype.setFunctionModule=function(e,t,r){this.modules[t][e]=r,this.isFunction[t][e]=!0},t.prototype.addEccAndInterleave=function(e){var r=this.version,n=this.errorCorrectionLevel;if(e.length!=t.getNumDataCodewords(r,n))throw new RangeError("Invalid argument");for(var i=t.NUM_ERROR_CORRECTION_BLOCKS[n.ordinal][r],a=t.ECC_CODEWORDS_PER_BLOCK[n.ordinal][r],u=Math.floor(t.getNumRawDataModules(r)/8),s=i-u%i,l=Math.floor(u/i),d=[],c=t.reedSolomonComputeDivisor(a),h=0,f=0;i>h;h++){var v=e.slice(f,f+l-a+(s>h?0:1));f+=v.length;var g=t.reedSolomonComputeRemainder(v,c);s>h&&v.push(0),d.push(v.concat(g))}var p=[],m=function(e){d.forEach(function(t,r){e==l-a&&s>r||p.push(t[e])})};for(h=0;d[0].length>h;h++)m(h);return o(p.length==u),p},t.prototype.drawCodewords=function(e){if(e.length!=Math.floor(t.getNumRawDataModules(this.version)/8))throw new RangeError("Invalid argument");for(var r=0,i=this.size-1;i>=1;i-=2){6==i&&(i=5);for(var a=0;this.size>a;a++)for(var u=0;2>u;u++){var s=i-u,l=!(i+1&2)?this.size-1-a:a;!this.isFunction[l][s]&&8*e.length>r&&(this.modules[l][s]=n(e[r>>>3],7-(7&r)),r++)}}o(r==8*e.length)},t.prototype.applyMask=function(e){if(0>e||e>7)throw new RangeError("Mask value out of range");for(var t=0;this.size>t;t++)for(var r=0;this.size>r;r++){var n=void 0;switch(e){case 0:n=(r+t)%2==0;break;case 1:n=t%2==0;break;case 2:n=r%3==0;break;case 3:n=(r+t)%3==0;break;case 4:n=(Math.floor(r/3)+Math.floor(t/2))%2==0;break;case 5:n=r*t%2+r*t%3==0;break;case 6:n=(r*t%2+r*t%3)%2==0;break;case 7:n=((r+t)%2+r*t%3)%2==0;break;default:throw Error("Unreachable")}!this.isFunction[t][r]&&n&&(this.modules[t][r]=!this.modules[t][r])}},t.prototype.getPenaltyScore=function(){for(var e=0,r=0;this.size>r;r++){for(var n=!1,i=0,a=[0,0,0,0,0,0,0],u=0;this.size>u;u++)this.modules[r][u]==n?5==++i?e+=t.PENALTY_N1:i>5&&e++:(this.finderPenaltyAddHistory(i,a),n||(e+=this.finderPenaltyCountPatterns(a)*t.PENALTY_N3),n=this.modules[r][u],i=1);e+=this.finderPenaltyTerminateAndCount(n,i,a)*t.PENALTY_N3}for(u=0;this.size>u;u++){n=!1;var s=0;for(a=[0,0,0,0,0,0,0],r=0;this.size>r;r++)this.modules[r][u]==n?5==++s?e+=t.PENALTY_N1:s>5&&e++:(this.finderPenaltyAddHistory(s,a),n||(e+=this.finderPenaltyCountPatterns(a)*t.PENALTY_N3),n=this.modules[r][u],s=1);e+=this.finderPenaltyTerminateAndCount(n,s,a)*t.PENALTY_N3}for(r=0;this.size-1>r;r++)for(u=0;this.size-1>u;u++){var l=this.modules[r][u];l==this.modules[r][u+1]&&l==this.modules[r+1][u]&&l==this.modules[r+1][u+1]&&(e+=t.PENALTY_N2)}for(var d=0,c=0,h=this.modules;h.length>c;c++){d=h[c].reduce(function(e,t){return e+(t?1:0)},d)}var f=this.size*this.size,v=Math.ceil(Math.abs(20*d-10*f)/f)-1;return o(v>=0&&9>=v),o((e+=v*t.PENALTY_N4)>=0&&2568888>=e),e},t.prototype.getAlignmentPatternPositions=function(){if(1==this.version)return[];for(var e=Math.floor(this.version/7)+2,t=2*Math.floor((8*this.version+3*e+5)/(4*e-4)),r=[6],n=this.size-7;e>r.length;n-=t)r.splice(1,0,n);return r},t.getNumRawDataModules=function(e){if(t.MIN_VERSION>e||e>t.MAX_VERSION)throw new RangeError("Version number out of range");var r=(16*e+128)*e+64;if(e>=2){var n=Math.floor(e/7)+2;r-=(25*n-10)*n-55,7>e||(r-=36)}return o(r>=208&&29648>=r),r},t.getNumDataCodewords=function(e,r){return Math.floor(t.getNumRawDataModules(e)/8)-t.ECC_CODEWORDS_PER_BLOCK[r.ordinal][e]*t.NUM_ERROR_CORRECTION_BLOCKS[r.ordinal][e]},t.reedSolomonComputeDivisor=function(e){if(1>e||e>255)throw new RangeError("Degree out of range");for(var r=[],n=0;e-1>n;n++)r.push(0);r.push(1);var o=1;for(n=0;e>n;n++){for(var i=0;r.length>i;i++)r[i]=t.reedSolomonMultiply(r[i],o),r.length>i+1&&(r[i]^=r[i+1]);o=t.reedSolomonMultiply(o,2)}return r},t.reedSolomonComputeRemainder=function(e,r){for(var n=r.map(function(e){return 0}),o=function(e){var o=e^n.shift();n.push(0),r.forEach(function(e,r){return n[r]^=t.reedSolomonMultiply(e,o)})},i=0,a=e;a.length>i;i++){o(a[i])}return n},t.reedSolomonMultiply=function(e,t){if(e>>>8!=0||t>>>8!=0)throw new RangeError("Byte out of range");for(var r=0,n=7;n>=0;n--)r=r<<1^285*(r>>>7),r^=(t>>>n&1)*e;return o(r>>>8==0),r},t.prototype.finderPenaltyCountPatterns=function(e){var t=e[1];o(3*this.size>=t);var r=t>0&&e[2]==t&&e[3]==3*t&&e[4]==t&&e[5]==t;return(!r||4*t>e[0]||t>e[6]?0:1)+(!r||4*t>e[6]||t>e[0]?0:1)},t.prototype.finderPenaltyTerminateAndCount=function(e,t,r){return e&&(this.finderPenaltyAddHistory(t,r),t=0),this.finderPenaltyAddHistory(t+=this.size,r),this.finderPenaltyCountPatterns(r)},t.prototype.finderPenaltyAddHistory=function(e,t){0==t[0]&&(e+=this.size),t.pop(),t.unshift(e)},t.MIN_VERSION=1,t.MAX_VERSION=40,t.PENALTY_N1=3,t.PENALTY_N2=3,t.PENALTY_N3=40,t.PENALTY_N4=10,t.ECC_CODEWORDS_PER_BLOCK=[[-1,7,10,15,20,26,18,20,24,30,18,20,24,26,30,22,24,28,30,28,28,28,28,30,30,26,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,10,16,26,18,24,16,18,22,22,26,30,22,22,24,24,28,28,26,26,26,26,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28],[-1,13,22,18,26,18,24,18,22,20,24,28,26,24,20,30,24,28,28,26,30,28,30,30,30,30,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,17,28,22,16,22,28,26,26,24,28,24,28,22,24,24,30,28,28,26,28,30,24,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30]],t.NUM_ERROR_CORRECTION_BLOCKS=[[-1,1,1,1,1,1,2,2,2,2,4,4,4,4,4,6,6,6,6,7,8,8,9,9,10,12,12,12,13,14,15,16,17,18,19,19,20,21,22,24,25],[-1,1,1,1,2,2,4,4,4,5,5,5,8,9,9,10,10,11,13,14,16,17,17,18,20,21,23,25,26,28,29,31,33,35,37,38,40,43,45,47,49],[-1,1,1,2,2,4,4,6,6,8,8,8,10,12,16,12,17,16,18,21,20,23,23,25,27,29,34,34,35,38,40,43,45,48,51,53,56,59,62,65,68],[-1,1,1,2,4,4,4,5,6,8,8,11,11,16,16,18,16,19,21,25,25,25,34,30,32,35,37,40,42,45,48,51,54,57,60,63,66,70,74,77,81]],t}();function r(e,t,r){if(0>t||t>31||e>>>t!=0)throw new RangeError("Value out of range");for(var n=t-1;n>=0;n--)r.push(e>>>n&1)}function n(e,t){return!!(e>>>t&1)}function o(e){if(!e)throw Error("Assertion error")}e.QrCode=t;var i=function(){function e(e,t,r){if(this.mode=e,this.numChars=t,this.bitData=r,0>t)throw new RangeError("Invalid argument");this.bitData=r.slice()}return e.makeBytes=function(t){for(var n=[],o=0,i=t;i.length>o;o++){r(i[o],8,n)}return new e(e.Mode.BYTE,t.length,n)},e.makeNumeric=function(t){if(!e.isNumeric(t))throw new RangeError("String contains non-numeric characters");for(var n=[],o=0;t.length>o;){var i=Math.min(t.length-o,3);r(parseInt(t.substring(o,o+i),10),3*i+1,n),o+=i}return new e(e.Mode.NUMERIC,t.length,n)},e.makeAlphanumeric=function(t){if(!e.isAlphanumeric(t))throw new RangeError("String contains unencodable characters in alphanumeric mode");var n,o=[];for(n=0;t.length>=n+2;n+=2){var i=45*e.ALPHANUMERIC_CHARSET.indexOf(t.charAt(n));r(i+=e.ALPHANUMERIC_CHARSET.indexOf(t.charAt(n+1)),11,o)}return t.length>n&&r(e.ALPHANUMERIC_CHARSET.indexOf(t.charAt(n)),6,o),new e(e.Mode.ALPHANUMERIC,t.length,o)},e.makeSegments=function(t){return""==t?[]:e.isNumeric(t)?[e.makeNumeric(t)]:e.isAlphanumeric(t)?[e.makeAlphanumeric(t)]:[e.makeBytes(e.toUtf8ByteArray(t))]},e.makeEci=function(t){var n=[];if(0>t)throw new RangeError("ECI assignment value out of range");if(128>t)r(t,8,n);else if(16384>t)r(2,2,n),r(t,14,n);else{if(t>=1e6)throw new RangeError("ECI assignment value out of range");r(6,3,n),r(t,21,n)}return new e(e.Mode.ECI,0,n)},e.isNumeric=function(t){return e.NUMERIC_REGEX.test(t)},e.isAlphanumeric=function(t){return e.ALPHANUMERIC_REGEX.test(t)},e.prototype.getData=function(){return this.bitData.slice()},e.getTotalBits=function(e,t){for(var r=0,n=0,o=e;o.length>n;n++){var i=o[n],a=i.mode.numCharCountBits(t);if(i.numChars>=1<<a)return 1/0;r+=4+a+i.bitData.length}return r},e.toUtf8ByteArray=function(e){e=encodeURI(e);for(var t=[],r=0;e.length>r;r++)"%"!=e.charAt(r)?t.push(e.charCodeAt(r)):(t.push(parseInt(e.substring(r+1,r+3),16)),r+=2);return t},e.NUMERIC_REGEX=/^[0-9]*$/,e.ALPHANUMERIC_REGEX=/^[A-Z0-9 $%*+.\/:-]*$/,e.ALPHANUMERIC_CHARSET="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:",e}();e.QrSegment=i}(r||(r={})),function(e){var t,r;t=e.QrCode||(e.QrCode={}),r=function(){function e(e,t){this.ordinal=e,this.formatBits=t}return e.LOW=new e(0,1),e.MEDIUM=new e(1,0),e.QUARTILE=new e(2,3),e.HIGH=new e(3,2),e}(),t.Ecc=r}(r||(r={})),function(e){var t,r;t=e.QrSegment||(e.QrSegment={}),r=function(){function e(e,t){this.modeBits=e,this.numBitsCharCount=t}return e.prototype.numCharCountBits=function(e){return this.numBitsCharCount[Math.floor((e+7)/17)]},e.NUMERIC=new e(1,[10,12,14]),e.ALPHANUMERIC=new e(2,[9,11,13]),e.BYTE=new e(4,[8,16,16]),e.KANJI=new e(8,[8,10,12]),e.ECI=new e(7,[0,0,0]),e}(),t.Mode=r}(r||(r={}));var o=r,i=0;var a={L:o.QrCode.Ecc.LOW,M:o.QrCode.Ecc.MEDIUM,Q:o.QrCode.Ecc.QUARTILE,H:o.QrCode.Ecc.HIGH},u=function(){try{(new Path2D).addPath(new Path2D)}catch(e){return!1}return!0}();function s(e){return e in a}function l(e,t,r){var n=t>0&&e[t-1][r],o=e.length-1>t&&e[t+1][r],i=r>0&&e[t][r-1],a=e[t].length-1>r&&e[t][r+1];return{nw:!n&&!i,ne:!n&&!a,se:!o&&!a,sw:!o&&!i}}function d(e){var r=t.computed(function(){var t;return(null!==(t=e.margin)&&void 0!==t?t:0)>>>0}),n=t.computed(function(){var t=s(e.level)?e.level:"L";return o.QrCode.encodeText(e.value,a[t]).getModules()}),i=t.computed(function(){return n.value.length+2*r.value}),u=t.computed(function(){return e.radius>0?function(e,t,r){void 0===t&&(t=0),void 0===r&&(r=0);for(var n=[],o=Math.min(r,.5),i=0;e.length>i;i++)for(var a=0;e[i].length>a;a++)if(e[i][a]){var u=l(e,i,a),s=u.nw,d=u.ne,c=u.se,h=u.sw,f=a+t,v=i+t;n.push("M".concat(f+(s?o:0)," ").concat(v),"L".concat(f+1-(d?o:0)," ").concat(v)),d&&n.push("A".concat(o," ").concat(o," 0 0 1 ").concat(f+1," ").concat(v+o)),n.push("L".concat(f+1," ").concat(v+1-(c?o:0))),c&&n.push("A".concat(o," ").concat(o," 0 0 1 ").concat(f+1-o," ").concat(v+1)),n.push("L".concat(f+(h?o:0)," ").concat(v+1)),h&&n.push("A".concat(o," ").concat(o," 0 0 1 ").concat(f," ").concat(v+1-o)),n.push("L".concat(f," ").concat(v+(s?o:0))),s&&n.push("A".concat(o," ").concat(o," 0 0 1 ").concat(f+o," ").concat(v)),n.push("z")}return n.join("")}(n.value,r.value,e.radius):function(e,t){void 0===t&&(t=0);for(var r=[],n=0;e.length>n;n++)for(var o=e[n],i=null,a=0;o.length>a;a++){var u=o[a];if(u||null===i)if(a!==o.length-1)u&&null===i&&(i=a);else{if(!u)continue;r.push(null===i?"M".concat(a+t,",").concat(n+t," h1v1H").concat(a+t,"z"):"M".concat(i+t,",").concat(n+t," h").concat(a+1-i,"v1H").concat(i+t,"z"))}else r.push("M".concat(i+t," ").concat(n+t,"h").concat(a-i,"v1H").concat(i+t,"z")),i=null}return r.join("")}(n.value,r.value)}),d=t.computed(function(){if(!e.imageSettings.src)return null;var t=function(e,t,r,n){var o=n.width,i=n.height,a=n.x,u=n.y,s=e.length+2*r,l=Math.floor(.1*t),d=s/t,c=(o||l)*d,h=(i||l)*d;return{x:null==a?e.length/2-c/2:a*d,y:null==u?e.length/2-h/2:u*d,h:h,w:c,borderRadius:(n.borderRadius||0)*d}}(n.value,e.size,r.value,e.imageSettings);return{x:t.x+r.value,y:t.y+r.value,width:t.w,height:t.h,borderRadius:t.borderRadius}}),c=t.computed(function(){if(!e.imageSettings.excavate||!d.value)return null;var t=2/(e.size/i.value);return{x:d.value.x-t,y:d.value.y-t,width:d.value.width+2*t,height:d.value.height+2*t,borderRadius:d.value.borderRadius}});return{margin:r,numCells:i,cells:n,fgPath:u,imageProps:d,imageBorderProps:c}}function c(e,t){var r=document.createElement("a");r.download=t,r.href=e,document.body.appendChild(r),r.click(),document.body.removeChild(r)}var h={value:{type:String,required:!0,default:""},size:{type:Number,default:100},level:{type:String,default:"L",validator:function(e){return s(e)}},background:{type:String,default:"#fff"},foreground:{type:String,default:"#000"},margin:{type:Number,default:0,validator:function(e){return e>=0}},imageSettings:{type:Object,default:function(){return{}}},gradient:{type:Boolean,default:!1},gradientType:{type:String,default:"linear",validator:function(e){return["linear","radial"].indexOf(e)>-1}},gradientStartColor:{type:String,default:"#000"},gradientEndColor:{type:String,default:"#fff"},radius:{type:Number,default:0,validator:function(e){return!isNaN(e)&&e>=0&&.5>=e}},id:{type:String,required:!1}},f=n(n({},h),{renderAs:{type:String,required:!1,default:"canvas",validator:function(e){return["canvas","svg"].indexOf(e)>-1}}}),v=t.defineComponent({name:"QRCodeSvg",props:h,setup:function(e,r){var o=d(e),a=o.numCells,u=o.fgPath,s=o.imageProps,l=o.imageBorderProps,h=t.ref(),f=e.id||"v-".concat(i++),v="qrcode.vue-gradient-".concat(f),g="qrcode.vue-logo-clip-path-".concat(f),p=t.computed(function(){return e.gradient?t.h("linear"===e.gradientType?"linearGradient":"radialGradient",n({id:v},"linear"===e.gradientType?{x1:"0%",y1:"0%",x2:"100%",y2:"100%"}:{cx:"50%",cy:"50%",r:"50%",fx:"50%",fy:"50%"}),[t.h("stop",{offset:"0%",style:{stopColor:e.gradientStartColor}}),t.h("stop",{offset:"100%",style:{stopColor:e.gradientEndColor}})]):null}),m=t.computed(function(){if(!s.value)return null;var e=s.value.borderRadius;return e>0?t.h("clipPath",{id:g},[t.h("rect",{x:s.value.x,y:s.value.y,width:s.value.width,height:s.value.height,rx:e,ry:e})]):null}),w=function(e){return"data:image/svg+xml;charset=utf-8,"+encodeURIComponent('<?xml version="1.0" standalone="no"?>'+(new XMLSerializer).serializeToString(e))};return r.expose({toDataURL:function(){var e=h.value;if(e)return w(e)},download:function(e){void 0===e&&(e="qrcode.svg");var t=h.value;t&&c(w(t),e)}}),function(){return t.h("svg",{ref:h,width:e.size,height:e.size,xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 ".concat(a.value," ").concat(a.value),role:"img"},[t.h("defs",{},[p.value,m.value].filter(Boolean)),t.h("rect",{width:"100%",height:"100%",fill:e.background}),t.h("path",{fill:e.gradient?"url(#".concat(v,")"):e.foreground,d:u.value}),l.value&&t.h("rect",{x:l.value.x,y:l.value.y,width:l.value.width,height:l.value.height,fill:e.background,rx:l.value.borderRadius,ry:l.value.borderRadius}),e.imageSettings.src&&s.value&&t.h("image",n({href:e.imageSettings.src,crossorigin:e.imageSettings.crossOrigin,"clip-path":s.value.borderRadius>0?"url(#".concat(g,")"):void 0},s.value))])}}}),g=t.defineComponent({name:"QRCodeCanvas",props:h,setup:function(e,r){var o=d(e),i=o.margin,a=o.cells,s=o.numCells,l=o.fgPath,h=o.imageProps,f=o.imageBorderProps,v=t.ref(null),g=t.ref(null),p=function(e,t,r,n,o,i){e.beginPath(),e.roundRect?e.roundRect(t,r,n,o,i):e.rect(t,r,n,o)},m=function(){var t=e.size,r=e.background,n=e.foreground,o=e.gradient,d=e.gradientType,c=e.gradientStartColor,m=e.gradientEndColor,w=v.value;if(w){var y=w.getContext("2d");if(y){var E=g.value,C="undefined"!=typeof window&&window.devicePixelRatio||1,R=t/s.value*C;if(w.height=w.width=t*C,y.setTransform(R,0,0,R,0,0),y.fillStyle=r,y.fillRect(0,0,s.value,s.value),o){var M=void 0;(M="linear"===d?y.createLinearGradient(0,0,s.value,s.value):y.createRadialGradient(s.value/2,s.value/2,0,s.value/2,s.value/2,s.value/2)).addColorStop(0,c),M.addColorStop(1,m),y.fillStyle=M}else y.fillStyle=n;if(u?y.fill(new Path2D(l.value)):a.value.forEach(function(e,t){e.forEach(function(e,r){e&&y.fillRect(r+i.value,t+i.value,1,1)})}),e.imageSettings.src&&E&&0!==E.naturalWidth&&0!==E.naturalHeight&&h.value){if(f.value){var S=f.value;y.fillStyle=e.background,p(y,S.x,S.y,S.width,S.height,S.borderRadius),y.fill()}var A=h.value.borderRadius;A>0?(y.save(),p(y,h.value.x,h.value.y,h.value.width,h.value.height,A),y.clip(),y.drawImage(E,h.value.x,h.value.y,h.value.width,h.value.height),y.restore()):y.drawImage(E,h.value.x,h.value.y,h.value.width,h.value.height)}}}};return t.onMounted(m),t.watchEffect(m,{flush:"post"}),r.expose({toDataURL:function(e,t){var r;return null===(r=v.value)||void 0===r?void 0:r.toDataURL(e,t)},download:function(e){void 0===e&&(e="qrcode.png");var t=v.value;t&&c(t.toDataURL("image/png"),e)}}),function(){return t.h(t.Fragment,[t.h("canvas",n(n({},r.attrs),{ref:v,role:"img",style:n(n({},r.attrs.style),{width:"".concat(e.size,"px"),height:"".concat(e.size,"px")})})),e.imageSettings.src&&t.h("img",{ref:g,src:e.imageSettings.src,crossorigin:e.imageSettings.crossOrigin,style:{display:"none"},onLoad:m})])}}}),p=t.defineComponent({name:"Qrcode",props:f,setup:function(e,r){var n=t.ref();return r.expose({toDataURL:function(e,t){var r,o;return null===(o=null===(r=n.value)||void 0===r?void 0:r.toDataURL)||void 0===o?void 0:o.call(r,e,t)},download:function(e){var t,r;return null===(r=null===(t=n.value)||void 0===t?void 0:t.download)||void 0===r?void 0:r.call(t,e)}}),function(){return t.h("svg"===e.renderAs?v:g,{ref:n,value:e.value,size:e.size,margin:e.margin,level:e.level,background:e.background,foreground:e.foreground,imageSettings:e.imageSettings,gradient:e.gradient,gradientType:e.gradientType,gradientStartColor:e.gradientStartColor,gradientEndColor:e.gradientEndColor,radius:e.radius,id:e.id})}}});e.QrcodeCanvas=g,e.QrcodeSvg=v,e.default=p,Object.defineProperty(e,"__esModule",{value:!0})});
|
package/dist/qrcode.vue.cjs.js
CHANGED
package/dist/qrcode.vue.esm.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* qrcode.vue v3.
|
|
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
|
|
5
|
+
* @license MIT.
|
|
6
6
|
*/
|
|
7
7
|
import { defineComponent, ref, computed, h, onMounted, watchEffect, Fragment } from 'vue';
|
|
8
8
|
|
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.
|
|
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,10 +18,10 @@
|
|
|
18
18
|
"./package": "./package.json"
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
|
-
"dev": "node example/scripts/generate-i18n.
|
|
21
|
+
"dev": "node example/scripts/generate-i18n.js && rsbuild dev",
|
|
22
22
|
"build": "rollup -c",
|
|
23
|
-
"build:example": "node example/scripts/generate-i18n.
|
|
24
|
-
"generate:i18n": "node example/scripts/generate-i18n.
|
|
23
|
+
"build:example": "node example/scripts/generate-i18n.js && rsbuild build",
|
|
24
|
+
"generate:i18n": "node example/scripts/generate-i18n.js",
|
|
25
25
|
"test": "rstest"
|
|
26
26
|
},
|
|
27
27
|
"repository": "https://github.com/scopewu/qrcode.vue.git",
|
|
@@ -36,9 +36,10 @@
|
|
|
36
36
|
],
|
|
37
37
|
"files": [
|
|
38
38
|
"/dist",
|
|
39
|
+
"/skills",
|
|
39
40
|
"CHANGELOG.md",
|
|
40
|
-
"README
|
|
41
|
-
"
|
|
41
|
+
"README.md",
|
|
42
|
+
"llms.txt"
|
|
42
43
|
],
|
|
43
44
|
"author": "scope.wu <scope.wu@gmail.com>",
|
|
44
45
|
"license": "MIT",
|
|
@@ -52,13 +53,13 @@
|
|
|
52
53
|
"dependencies": {},
|
|
53
54
|
"devDependencies": {
|
|
54
55
|
"@rollup/plugin-terser": "^1.0.0",
|
|
55
|
-
"@rsbuild/core": "^2.
|
|
56
|
-
"@rstest/core": "^0.10
|
|
57
|
-
"@vue/test-utils": "^2.
|
|
58
|
-
"happy-dom": "^20.
|
|
59
|
-
"rollup": "^4.
|
|
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",
|
|
60
61
|
"rollup-plugin-typescript2": "^0.37.0",
|
|
61
62
|
"typescript": "^5.9.3",
|
|
62
|
-
"vue": "^3.5.
|
|
63
|
+
"vue": "^3.5.42"
|
|
63
64
|
}
|
|
64
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.
|
package/README-zh_cn.md
DELETED
|
@@ -1,360 +0,0 @@
|
|
|
1
|
-
# qrcode.vue
|
|
2
|
-
|
|
3
|
-
⚠️ 如果你正在使用 Vue 3,请升级 `qrcode.vue` 到 `3.x`;
|
|
4
|
-
|
|
5
|
-
🔒 如果你正在使用 Vue 2,请保持 `qrcode.vue` 的版本为 `1.x`;
|
|
6
|
-
|
|
7
|
-
一款 Vue.js 二维码组件,同时支援 Vue 2 和 Vue 3.
|
|
8
|
-
|
|
9
|
-
[](https://github.com/scopewu/qrcode.vue/blob/main/LICENSE)
|
|
10
|
-
|
|
11
|
-
## 快速开始
|
|
12
|
-
|
|
13
|
-
快速添加 `qrcode.vue` 组件到项目中
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npm install --save qrcode.vue # yarn add qrcode.vue
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
dist/
|
|
21
|
-
|--- qrcode.vue.cjs.js // CommonJS
|
|
22
|
-
|--- qrcode.vue.esm.js // ES module
|
|
23
|
-
|--- qrcode.vue.browser.js // UMD for browser or require.js or CommonJS
|
|
24
|
-
|--- qrcode.vue.browser.min.js // UMD Minimum size
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## 使用
|
|
28
|
-
|
|
29
|
-
e.g.
|
|
30
|
-
|
|
31
|
-
```javascript
|
|
32
|
-
import { createApp } from 'vue'
|
|
33
|
-
import QrcodeVue from 'qrcode.vue'
|
|
34
|
-
|
|
35
|
-
createApp({
|
|
36
|
-
data: {
|
|
37
|
-
value: 'https://example.com',
|
|
38
|
-
},
|
|
39
|
-
template: '<qrcode-vue :value="value"></qrcode-vue>',
|
|
40
|
-
components: {
|
|
41
|
-
QrcodeVue,
|
|
42
|
-
},
|
|
43
|
-
}).mount('#root')
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
或者,在独有单文件扩展 `*.vue` 中使用:
|
|
47
|
-
|
|
48
|
-
```html
|
|
49
|
-
<template>
|
|
50
|
-
<qrcode-vue :value="value" :size="size" level="H" />
|
|
51
|
-
</template>
|
|
52
|
-
<script>
|
|
53
|
-
import QrcodeVue from 'qrcode.vue'
|
|
54
|
-
|
|
55
|
-
export default {
|
|
56
|
-
data() {
|
|
57
|
-
return {
|
|
58
|
-
value: 'https://example.com',
|
|
59
|
-
size: 300,
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
components: {
|
|
63
|
-
QrcodeVue,
|
|
64
|
-
},
|
|
65
|
-
}
|
|
66
|
-
</script>
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
在 Vue 3 中配合 `TypeScript` 使用:
|
|
70
|
-
|
|
71
|
-
```html
|
|
72
|
-
<template>
|
|
73
|
-
<qrcode-vue
|
|
74
|
-
:value="value"
|
|
75
|
-
:level="level"
|
|
76
|
-
:render-as="renderAs"
|
|
77
|
-
:background="background"
|
|
78
|
-
:foreground='foreground'
|
|
79
|
-
:gradient="gradient"
|
|
80
|
-
:gradient-type="gradientType"
|
|
81
|
-
:gradient-start-color="gradientStartColor"
|
|
82
|
-
:gradient-end-color="gradientEndColor"
|
|
83
|
-
:image-settings='imageSettings'
|
|
84
|
-
:radius="radius"
|
|
85
|
-
/>
|
|
86
|
-
</template>
|
|
87
|
-
<script setup lang="ts">
|
|
88
|
-
import { ref } from 'vue'
|
|
89
|
-
import QrcodeVue from 'qrcode.vue'
|
|
90
|
-
import type { Level, RenderAs, GradientType, ImageSettings } from 'qrcode.vue'
|
|
91
|
-
|
|
92
|
-
const value = ref('qrcode')
|
|
93
|
-
const level = ref<Level>('M')
|
|
94
|
-
const renderAs = ref<RenderAs>('svg')
|
|
95
|
-
const background = ref('#ffffff')
|
|
96
|
-
const foreground = ref('#000000')
|
|
97
|
-
const margin = ref(0)
|
|
98
|
-
|
|
99
|
-
// 可传入二维码图片相关的属性,支持二维码 LOGO;
|
|
100
|
-
const imageSettings = ref<ImageSettings>({
|
|
101
|
-
src: 'https://github.com/scopewu.png',
|
|
102
|
-
width: 30,
|
|
103
|
-
height: 30,
|
|
104
|
-
// x: 10,
|
|
105
|
-
// y: 10,
|
|
106
|
-
excavate: true,
|
|
107
|
-
// crossOrigin: 'anonymous', // 如需把 canvas 导出为图片,请设置此项。
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
// 可传入渐变相关的属性,支持渐变:
|
|
111
|
-
const gradient = ref(false)
|
|
112
|
-
const gradientType = ref<GradientType>('linear')
|
|
113
|
-
const gradientStartColor = ref('#000000')
|
|
114
|
-
const gradientEndColor = ref('#38bdf8')
|
|
115
|
-
// 可传入圆角半径:
|
|
116
|
-
const radius = ref(0)
|
|
117
|
-
</script>
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Component props
|
|
121
|
-
|
|
122
|
-
### `value`
|
|
123
|
-
|
|
124
|
-
- 类型:`string`
|
|
125
|
-
- 默认值:`''`
|
|
126
|
-
|
|
127
|
-
二维码的内容值。
|
|
128
|
-
|
|
129
|
-
### `size`
|
|
130
|
-
|
|
131
|
-
- 类型:`number`
|
|
132
|
-
- 默认值:`100`
|
|
133
|
-
|
|
134
|
-
二维码大小。
|
|
135
|
-
|
|
136
|
-
### `render-as`
|
|
137
|
-
|
|
138
|
-
- 类型:`RenderAs('canvas' | 'svg')`
|
|
139
|
-
- 默认值:`canvas`
|
|
140
|
-
|
|
141
|
-
生成二维码的 HTML 标签,可选 `canvas` 或 `svg`。其中 `svg` 可以用于 SSR。
|
|
142
|
-
|
|
143
|
-
### `margin`
|
|
144
|
-
|
|
145
|
-
- 类型:`number`
|
|
146
|
-
- 默认值:`0`
|
|
147
|
-
|
|
148
|
-
定义空白区的宽度应该是多少。
|
|
149
|
-
|
|
150
|
-
### `level`
|
|
151
|
-
|
|
152
|
-
- 类型:`Level('L' | 'M' | 'Q' | 'H')`
|
|
153
|
-
- 默认值:`L`
|
|
154
|
-
|
|
155
|
-
二维码的容错能力等级,取值为 'L', 'M', 'Q', 'H' 之一。了解更多,[维基百科:QR_code](https://en.wikipedia.org/wiki/QR_code#Error_correction)。
|
|
156
|
-
|
|
157
|
-
### `background`
|
|
158
|
-
|
|
159
|
-
- 类型:`string`
|
|
160
|
-
- 默认值:`#ffffff`
|
|
161
|
-
|
|
162
|
-
二维码背景颜色。
|
|
163
|
-
|
|
164
|
-
### `foreground`
|
|
165
|
-
|
|
166
|
-
- 类型:`string`
|
|
167
|
-
- 默认值:`#000000`
|
|
168
|
-
|
|
169
|
-
二维码前景颜色。
|
|
170
|
-
|
|
171
|
-
### `image-settings`
|
|
172
|
-
|
|
173
|
-
- 类型: `ImageSettings`
|
|
174
|
-
- 默认值: `{}`
|
|
175
|
-
|
|
176
|
-
```ts
|
|
177
|
-
export type ImageSettings = {
|
|
178
|
-
src: string, // 图片的地址。
|
|
179
|
-
x?: number, // 水平横向偏移。没有设定值时,图片剧中
|
|
180
|
-
y?: number, // 垂直竖向偏移。没有设定值时,图片剧中
|
|
181
|
-
height: number, // 图片的高度
|
|
182
|
-
width: number, // 图片的宽度
|
|
183
|
-
// 是否“挖掘”图像周围的模块。
|
|
184
|
-
// 这意味着嵌入图像重叠的任何模块都将使用背景颜色。
|
|
185
|
-
// 使用此选项可确保图像周围的边缘清晰。嵌入透明图像时也很有用。
|
|
186
|
-
excavate?: boolean,
|
|
187
|
-
borderRadius?: number, // 图片的边框圆角。
|
|
188
|
-
crossOrigin?: 'anonymous' | 'use-credentials' | '', // 图片的 CORS 属性。如需导出 canvas 为图片,请设置此项。
|
|
189
|
-
}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
二维码图片 logo 配置。
|
|
193
|
-
|
|
194
|
-
### `radius`
|
|
195
|
-
|
|
196
|
-
- 类型:`number`
|
|
197
|
-
- 默认值:`0`
|
|
198
|
-
|
|
199
|
-
每个二维码模块的圆角半径,相对于模块宽度的比例。接受 `0` 到 `0.5` 的值。
|
|
200
|
-
|
|
201
|
-
- `0`(默认)- 方形模块,直角
|
|
202
|
-
- `0.5` - 最大圆角,模块变为圆形
|
|
203
|
-
|
|
204
|
-
圆角是上下文感知的:相邻深色模块之间的内角保持直角,外角则圆角化。
|
|
205
|
-
|
|
206
|
-
```html
|
|
207
|
-
<qrcode-vue value="test" :radius="0.35" />
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### `id`
|
|
211
|
-
|
|
212
|
-
- 类型:`string`
|
|
213
|
-
- 默认值:`undefined`
|
|
214
|
-
|
|
215
|
-
自定义 SVG 内部元素(渐变、裁剪路径)的 ID。SSR 水合一致性需要此属性 — 传入 `useId()` 以确保服务端和客户端生成匹配的 ID。
|
|
216
|
-
|
|
217
|
-
```html
|
|
218
|
-
<script setup>
|
|
219
|
-
// 仅 vue 3.5+
|
|
220
|
-
import { useId } from 'vue'
|
|
221
|
-
const uid = useId()
|
|
222
|
-
|
|
223
|
-
// vue 3.4 及以下版本
|
|
224
|
-
// const uid = 'qrcode-1'
|
|
225
|
-
// const uid2 = 'qrcode-2'
|
|
226
|
-
</script>
|
|
227
|
-
<qrcode-svg value="test" :id="uid" render-as="svg" />
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
省略时使用模块级计数器,该计数器在多请求的 SSR 环境中不安全。
|
|
231
|
-
|
|
232
|
-
### `gradient`
|
|
233
|
-
|
|
234
|
-
- 类型: `boolean`
|
|
235
|
-
- 默认值: `false`
|
|
236
|
-
|
|
237
|
-
启用二维码的渐变填充。
|
|
238
|
-
|
|
239
|
-
### `gradient-type`
|
|
240
|
-
|
|
241
|
-
- 类型: `GradientType('linear' | 'radial')`
|
|
242
|
-
- 默认值: `linear`
|
|
243
|
-
|
|
244
|
-
指定渐变类型。
|
|
245
|
-
|
|
246
|
-
### `gradient-start-color`
|
|
247
|
-
|
|
248
|
-
- 类型: `string`
|
|
249
|
-
- 默认值: `#000000`
|
|
250
|
-
|
|
251
|
-
渐变的起始颜色。
|
|
252
|
-
|
|
253
|
-
### `gradient-end-color`
|
|
254
|
-
|
|
255
|
-
- 类型: `string`
|
|
256
|
-
- 默认值: `#ffffff`
|
|
257
|
-
|
|
258
|
-
渐变的结束颜色。
|
|
259
|
-
|
|
260
|
-
### `class`
|
|
261
|
-
|
|
262
|
-
- 类型:`string`
|
|
263
|
-
- 默认值:`''`
|
|
264
|
-
|
|
265
|
-
传递给二维码根元素的类名。
|
|
266
|
-
|
|
267
|
-
## 模板 Ref 方法(`QrcodeCanvas` / `QrcodeSvg`)
|
|
268
|
-
|
|
269
|
-
`QrcodeCanvas` 和 `QrcodeSvg` 都通过模板 ref 暴露以下方法。`QrcodeVue` 会根据当前的 `render-as` 转发这些方法。
|
|
270
|
-
|
|
271
|
-
### `QrcodeCanvas`
|
|
272
|
-
|
|
273
|
-
```html
|
|
274
|
-
<script setup>
|
|
275
|
-
import { ref } from 'vue'
|
|
276
|
-
import { QrcodeCanvas } from 'qrcode.vue'
|
|
277
|
-
|
|
278
|
-
const qrRef = ref()
|
|
279
|
-
const handleDownload = () => {
|
|
280
|
-
qrRef.value?.download('my-qrcode.png')
|
|
281
|
-
}
|
|
282
|
-
</script>
|
|
283
|
-
|
|
284
|
-
<qrcode-canvas ref="qrRef" value="https://example.com" />
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
| 方法 | 签名 | 说明 |
|
|
288
|
-
|------|------|------|
|
|
289
|
-
| `toDataURL` | `(type?: string, quality?: number) => string \| undefined` | 将 canvas 转换为 Data URL。 |
|
|
290
|
-
| `download` | `(filename?: string) => void` | 触发下载二维码 PNG 图片。 |
|
|
291
|
-
|
|
292
|
-
> **CORS 注意:** 当二维码包含跨域 Logo 图片时,请确保设置 `imageSettings.crossOrigin: 'anonymous'`,且图片服务器返回 `Access-Control-Allow-Origin` 响应头。否则 canvas 会被"污染",导致 `toDataURL` / `download` 抛出 `SecurityError`。
|
|
293
|
-
|
|
294
|
-
### `QrcodeSvg`
|
|
295
|
-
|
|
296
|
-
```html
|
|
297
|
-
<script setup>
|
|
298
|
-
import { ref } from 'vue'
|
|
299
|
-
import { QrcodeSvg } from 'qrcode.vue'
|
|
300
|
-
|
|
301
|
-
const qrRef = ref()
|
|
302
|
-
const handleDownload = () => {
|
|
303
|
-
qrRef.value?.download('my-qrcode.svg')
|
|
304
|
-
}
|
|
305
|
-
</script>
|
|
306
|
-
|
|
307
|
-
<qrcode-svg ref="qrRef" value="https://example.com" />
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
| 方法 | 签名 | 说明 |
|
|
311
|
-
|------|------|------|
|
|
312
|
-
| `toDataURL` | `() => string \| undefined` | 将 SVG 元素转换为 Data URL。 |
|
|
313
|
-
| `download` | `(filename?: string) => void` | 触发下载二维码 SVG 图片。 |
|
|
314
|
-
|
|
315
|
-
## `QrcodeVue` 3.5+
|
|
316
|
-
|
|
317
|
-
`QrcodeVue` 3.5+ 后导出独立的 `QrcodeCanvas` 和 `QrcodeSvg` 组件,为此修改了 rollup 的配置:
|
|
318
|
-
|
|
319
|
-
```
|
|
320
|
-
// rollup.config.js
|
|
321
|
-
|
|
322
|
-
- exports: 'default',
|
|
323
|
-
+ exports: 'named',
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
现在在 common.js 和 cdn 直接引用 `QrcodeVue` 需要使用 `default` 字段:
|
|
327
|
-
|
|
328
|
-
```js
|
|
329
|
-
const QrcodeVue = require('qrcode.vue').default
|
|
330
|
-
const { default: QrcodeVue, QrcodeCanvas, QrcodeSvg } = require('qrcode.vue')
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
```html
|
|
334
|
-
<!--With HTML-->
|
|
335
|
-
<div id="root">
|
|
336
|
-
<p class="flex space-x">
|
|
337
|
-
<qrcode-vue :value="test" render-as="svg"></qrcode-vue>
|
|
338
|
-
<qrcode-canvas :value="test"></qrcode-canvas>
|
|
339
|
-
</p>
|
|
340
|
-
<p><input v-model="test" /></p>
|
|
341
|
-
</div>
|
|
342
|
-
<script src="https://cdn.jsdelivr.net/npm/vue@3.5/dist/vue.global.prod.js"></script>
|
|
343
|
-
<script src="https://cdn.jsdelivr.net/npm/qrcode.vue@3.5/dist/qrcode.vue.browser.min.js"></script>
|
|
344
|
-
|
|
345
|
-
<script>
|
|
346
|
-
Vue.createApp({
|
|
347
|
-
data() { return {
|
|
348
|
-
test: 'Hello World',
|
|
349
|
-
}},
|
|
350
|
-
components: {
|
|
351
|
-
QrcodeVue: QrcodeVue.default,
|
|
352
|
-
QrcodeCanvas: QrcodeVue.QrcodeCanvas,
|
|
353
|
-
},
|
|
354
|
-
}).mount('#root')
|
|
355
|
-
</script>
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
## 软件许可
|
|
359
|
-
|
|
360
|
-
copyright © 2021 scopewu, license by [MIT](https://github.com/scopewu/qrcode.vue/blob/main/LICENSE)
|