vericode.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. <template>
  2. <view class="vericode">
  3. <canvas class="canvas" canvas-id="codecanvas" id="codecanvas"> </canvas>
  4. <canvas class="tcanvas" canvas-id="tcanvas" id="tcanvas"> </canvas>
  5. </view>
  6. </template>
  7. <script>
  8. export default {
  9. data() {
  10. return {
  11. inDraw: false,
  12. width: 0,
  13. height: 0,
  14. // 每次生成的验证码
  15. vericode: "",
  16. // 生成的数字个数
  17. nums: 4,
  18. // 混淆线的条数
  19. drawNums: 1,
  20. // 混淆线宽度
  21. lineWidth: 1,
  22. };
  23. },
  24. methods: {
  25. async drawCode() {
  26. const self = this;
  27. if (this.inDraw) {
  28. return;
  29. }
  30. this.inDraw = true;
  31. const w = this.width;
  32. const h = this.height;
  33. let vericode = "";
  34. const nums = this.nums;
  35. const xspan = w / (nums + 1);
  36. const ctx = uni.createCanvasContext("codecanvas", this);
  37. ctx.clearRect(0, 0, w, h);
  38. ctx.draw();
  39. function drawBg() {
  40. const imageData = Array.from({ length: w * h * 4 });
  41. for (let i = 0; i < imageData.length; i += 4) {
  42. imageData[i + 0] = Math.random() * 255;
  43. imageData[i + 1] = Math.random() * 255;
  44. imageData[i + 2] = Math.random() * 255;
  45. imageData[i + 3] = 30;
  46. }
  47. uni.canvasPutImageData(
  48. {
  49. canvasId: "codecanvas",
  50. x: 0,
  51. y: 0,
  52. width: w,
  53. data: new Uint8ClampedArray(imageData),
  54. },
  55. self
  56. );
  57. return new Promise((resolv) => {
  58. uni.canvasToTempFilePath(
  59. {
  60. x: 0,
  61. y: 0,
  62. width: w,
  63. height: h,
  64. canvasId: "codecanvas",
  65. success: function (res) {
  66. ctx.drawImage(res.tempFilePath, 0, 0);
  67. resolv();
  68. },
  69. },
  70. self
  71. );
  72. });
  73. }
  74. // 去掉大写 0和o gq和9 1和l 等abcdefhjkmnprstuvwyz
  75. const source = "23456789";
  76. const indexArr = Array.prototype.map.call(source, (e, i) => i);
  77. function getAlpha() {
  78. const len = indexArr.length - 1 < 0 ? 0 : indexArr.length - 1;
  79. let i = Math.round(Math.random() * len);
  80. const ch = source[indexArr[i]];
  81. indexArr.splice(i, 1);
  82. return ch;
  83. }
  84. const { drawText } = (() => {
  85. let i = 0;
  86. let fontbase = Math.round(h * 0.5);
  87. let fontspan = fontbase / 2;
  88. let font = Math.round(Math.random() * fontspan) + fontbase;
  89. const tctx = uni.createCanvasContext("tcanvas", self);
  90. function drawText() {
  91. const xpos = (i++ % nums) * xspan;
  92. font = fontbase + Math.round(fontspan * Math.random());
  93. const txspan = 2 * xspan;
  94. tctx.fillStyle = `rgb(${Math.random() * 255},${Math.random() * 255},${
  95. Math.random() * 255
  96. })`;
  97. tctx.setFontSize(font*1.2);
  98. const ybase = h / 2 + font / 2;
  99. const xtrans = ((Math.random() - 0.5) * 2) / 5;
  100. const ytrans = ((Math.random() - 0.5) * 2) / 5;
  101. tctx.transform(1, xtrans, ytrans, 1, 0, 0);
  102. const txt = getAlpha();
  103. tctx.fillText(txt, 0.5 * txspan, ybase);
  104. tctx.draw();
  105. vericode += txt;
  106. return new Promise((resolv) => {
  107. uni.canvasToTempFilePath(
  108. {
  109. x: 0,
  110. y: 0,
  111. width: 2 * xspan,
  112. height: h,
  113. destWidth: 2 * xspan,
  114. destHeight: h,
  115. canvasId: "tcanvas",
  116. success: function (res) {
  117. ctx.drawImage(res.tempFilePath, xpos, 0);
  118. tctx.clearRect(0, 0, txspan, h);
  119. tctx.draw();
  120. resolv();
  121. },
  122. },
  123. self
  124. );
  125. });
  126. }
  127. return { drawText };
  128. })();
  129. function drawLine() {
  130. const drawnums = self.drawNums;
  131. for (let i = 0; i < drawnums; i++) {
  132. ctx.beginPath();
  133. ctx.lineWidth = self.lineWidth;
  134. ctx.strokeStyle = `rgba(${Math.random() * 255},${
  135. Math.random() * 255
  136. },${Math.random() * 255},0.8)`;
  137. // 左右非正中区域
  138. const x1 =
  139. ((Math.random() > 0.5 ? 1 : -1) * (Math.random() + 1) * w) / 4 +
  140. w / 2;
  141. // 上下非正中区域
  142. const y1 =
  143. ((Math.random() + 1) * h) / 4 + (Math.random() > 0.5 ? h / 2 : 0);
  144. const x2 =
  145. ((Math.random() > 0.5 ? 1 : -1) * (Math.random() + 1) * w) / 4 +
  146. w / 2;
  147. const y2 =
  148. ((Math.random() + 1) * h) / 4 + (Math.random() > 0.5 ? h / 2 : 0);
  149. ctx.moveTo(x1, y1);
  150. ctx.lineTo(x2, y2);
  151. ctx.closePath();
  152. ctx.stroke();
  153. }
  154. }
  155. await drawBg();
  156. for (let i = 0; i < nums; i++) {
  157. await drawText();
  158. }
  159. drawLine();
  160. ctx.draw();
  161. this.vericode = vericode;
  162. this.inDraw = false;
  163. },
  164. },
  165. mounted() {
  166. const self = this;
  167. const query = uni.createSelectorQuery().in(this);
  168. query
  169. .select(".vericode")
  170. .boundingClientRect((data) => {
  171. self.width = Math.round(data.width);
  172. self.height = Math.round(data.height);
  173. self.drawCode();
  174. })
  175. .exec();
  176. },
  177. };
  178. </script>
  179. <style scoped>
  180. .vericode {
  181. width: 100%;
  182. height: 100%;
  183. position: relative;
  184. overflow: hidden;
  185. }
  186. .canvas {
  187. position: absolute;
  188. left: 0;
  189. top: 0;
  190. width: 1000px;
  191. height: 1000px;
  192. }
  193. .tcanvas {
  194. position: fixed;
  195. top: 9999px;
  196. width: 1000px;
  197. height: 1000px;
  198. }
  199. </style>