UNPKG

create-puzzle

Version:

在浏览器端生成滑块验证码的拼图和背景图。

347 lines (341 loc) 15.2 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.createPuzzle = {})); })(this, (function (exports) { 'use strict'; // 拼图点 exports.Point = void 0; (function (Point) { Point["None"] = "none"; Point["Outer"] = "outher"; Point["Inner"] = "inner"; })(exports.Point || (exports.Point = {})); var pointArray = [exports.Point.None, exports.Point.Outer, exports.Point.Inner]; // 获取随机整数,大于等于0,小于max function getRandomInt(max, min) { if (min === void 0) { min = 0; } return Math.max(Math.floor(Math.random() * max), min); } // 随机选择数组中的某一项 function pick(arr) { var len = arr.length; var randomIndex = getRandomInt(len); return arr[randomIndex]; } // 获取随机拼图点 function getRandomPoints(pointNum) { var points = { top: pick(pointArray), right: pick(pointArray), bottom: pick(pointArray), left: pick(pointArray), }; var pointsKeys = Object.keys(points); // 保证上下 和 左右 都必须有一个外部的拼图点 if (points.top === exports.Point.Outer && points.bottom === exports.Point.Outer) { points[pick(['top', 'bottom'])] = exports.Point.Inner; } else if (points.top !== exports.Point.Outer && points.bottom !== exports.Point.Outer) { points[pick(['top', 'bottom'])] = exports.Point.Outer; } if (points.left === exports.Point.Outer && points.right === exports.Point.Outer) { points[pick(['left', 'right'])] = exports.Point.Inner; } else if (points.left !== exports.Point.Outer && points.right !== exports.Point.Outer) { points[pick(['left', 'right'])] = exports.Point.Outer; } if (pointNum) { var inners_1 = []; var nones_1 = []; pointsKeys.forEach(function (item) { if (points[item] === exports.Point.Inner) { inners_1.push(item); } else if (points[item] === exports.Point.None) { nones_1.push(item); } }); if (pointNum === 2) { inners_1.forEach(function (item) { return (points[item] = exports.Point.None); }); } else if (pointNum === 3) { if (inners_1.length === 0) { points[pick(nones_1)] = exports.Point.Inner; } else if (inners_1.length === 2) { points[pick(inners_1)] = exports.Point.None; } } else if (pointNum == 4) { nones_1.forEach(function (item) { return (points[item] = exports.Point.Inner); }); } } return points; } // 画拼图 function drawPuzzle(ctx, options) { if (options === void 0) { options = {}; } var _a = options.x, x = _a === void 0 ? 0 : _a, _b = options.y, y = _b === void 0 ? 0 : _b, _c = options.w, w = _c === void 0 ? 60 : _c, _d = options.h, h = _d === void 0 ? 60 : _d; var points = options.points, _e = options.margin, margin = _e === void 0 ? 0 : _e; margin = margin <= 0 ? 0 : margin; if (typeof points === 'number' || !points) { points = getRandomPoints(points); } var r = (Math.min(w, h) - margin * 2) * 0.15; // 适合拼图点的比例 0.15 var l = Math.hypot(r, r); // 斜边长度 var l1_2 = l / 2; // 斜边长度一半,45度角直角三角形,邻边相等 var c2r = r + l1_2; // 圆直径 var rect = { x: x + margin, y: y + margin, w: w - c2r - margin * 2, h: h - c2r - margin * 2, }; var w1_2 = rect.w / 2; // 矩形一半宽度 var h1_2 = rect.h / 2; // 矩形一半高度 if (points.left === exports.Point.Outer) { rect.x += c2r; } if (points.top === exports.Point.Outer) { rect.y += c2r; } // draw start ctx.beginPath(); ctx.lineWidth = 2; // top ctx.moveTo(rect.x, rect.y); if (points.top !== exports.Point.None) { ctx.lineTo(rect.x + w1_2 - l1_2, rect.y); if (points.top === exports.Point.Inner) { ctx.arc(rect.x + w1_2, rect.y + l1_2, r, 1.25 * Math.PI, 1.75 * Math.PI, true); } else { ctx.arc(rect.x + w1_2, rect.y - l1_2, r, 0.75 * Math.PI, 0.25 * Math.PI); } } ctx.lineTo(rect.x + rect.w, rect.y); // right if (points.right !== exports.Point.None) { ctx.lineTo(rect.x + rect.w, rect.y + h1_2 - l1_2); if (points.right === exports.Point.Inner) { ctx.arc(rect.x + rect.w - l1_2, rect.y + h1_2, r, 1.75 * Math.PI, 0.25 * Math.PI, true); } else { ctx.arc(rect.x + rect.w + l1_2, rect.y + h1_2, r, 1.25 * Math.PI, 0.75 * Math.PI); } } ctx.lineTo(rect.x + rect.w, rect.y + rect.h); // bottom if (points.bottom !== exports.Point.None) { ctx.lineTo(rect.x + w1_2 + l1_2, rect.y + rect.h); if (points.bottom === exports.Point.Inner) { ctx.arc(rect.x + w1_2, rect.y + rect.h - l1_2, r, 0.25 * Math.PI, 0.75 * Math.PI, true); } else { ctx.arc(rect.x + w1_2, rect.y + rect.h + l1_2, r, 1.75 * Math.PI, 1.25 * Math.PI); } } ctx.lineTo(rect.x, rect.y + rect.h); // left if (points.left !== exports.Point.None) { ctx.lineTo(rect.x, rect.y + h1_2 + l1_2); if (points.left === exports.Point.Inner) { ctx.arc(rect.x + l1_2, rect.y + h1_2, r, 0.75 * Math.PI, 1.25 * Math.PI, true); } else { ctx.arc(rect.x - l1_2, rect.y + h1_2, r, 0.25 * Math.PI, 1.75 * Math.PI); } } ctx.lineTo(rect.x, rect.y); ctx.stroke(); ctx.closePath(); // ctx.fillStyle = "red"; // ctx.fill(); // ctx.strokeRect(x, y, w, h); } var SuccessResponseStatus = [200, 304]; function getUrlBlob(url) { return new Promise(function (resolve, reject) { var xhr = new XMLHttpRequest(); xhr.open('get', url, true); xhr.responseType = 'blob'; xhr.onload = function (e) { // @ts-ignore // 进入 onload 表示 readyStatus 为 4 ,但是 status 不一定是 200 。 var responseStatus = e.target.status; if (SuccessResponseStatus.indexOf(responseStatus) > -1) { resolve(e); } else { reject(new Error("[createPuzzle] The image does not support get requests, responseStatus ".concat(responseStatus, ", '").concat(url, "'."))); } }; xhr.onerror = function (e) { reject(e); }; xhr.send(null); }); } function isBlob(obj) { return typeof Blob !== 'undefined' && obj instanceof Blob; } function isBase64(str) { return typeof str === 'string' && str.indexOf('data:') === 0; } function isBlobUrl(str) { return typeof str === 'string' && str.indexOf('blob:') === 0; } function getImageUrl(image) { return new Promise(function (resolve, reject) { var imgIsBlob = isBlob(image); var imgIsBase64 = isBase64(image); if (imgIsBlob) { resolve(URL.createObjectURL(image)); } else if (!imgIsBase64) { // url 可能有跨域问题 getUrlBlob(image) .then(function (ev) { // @ts-ignore resolve(URL.createObjectURL(ev.target.response)); }) .catch(reject); } else { resolve(image); } }); } var cacheImage; var cacheImageElement; function internalLoadImage(image, useCache) { if (useCache === void 0) { useCache = true; } return new Promise(function (resolve, reject) { if (useCache && cacheImage === image && cacheImageElement) { resolve(cacheImageElement); } else { getImageUrl(image) .then(function (url) { function revoke() { if (isBlobUrl(url)) { URL.revokeObjectURL(url); } } var img = new Image(); img.onload = function () { revoke(); if (useCache) { cacheImage = image; cacheImageElement = img; } resolve(img); }; img.onerror = function (err) { revoke(); console.error("[createPuzzle] The image load failed, '".concat(image, "'.")); reject(err); }; img.src = url; }) .catch(reject); } }); } // 创建拼图和背景图 function createPuzzle(imgUrl, options) { if (options === void 0) { options = {}; } var _a = options.borderWidth, borderWidth = _a === void 0 ? 2 : _a, _b = options.borderColor, borderColor = _b === void 0 ? 'rgba(255,255,255,0.7)' : _b, _c = options.fillColor, fillColor = _c === void 0 ? 'rgba(255,255,255,0.7)' : _c, outPoints = options.points, _d = options.width, width = _d === void 0 ? 60 : _d, _e = options.height, height = _e === void 0 ? 60 : _e, outX = options.x, outY = options.y, _f = options.margin, margin = _f === void 0 ? 2 : _f, imageWidth = options.imageWidth, imageHeight = options.imageHeight, outBgWidth = options.bgWidth, outBgHeight = options.bgHeight, _g = options.bgOffset, outBgOffset = _g === void 0 ? [0, 0] : _g, _h = options.bgImageType, bgImageType = _h === void 0 ? 'image/jpeg' : _h, _j = options.bgImageEncoderOptions, bgImageEncoderOptions = _j === void 0 ? 0.8 : _j, cacheImage = options.cacheImage; return new Promise(function (resolve, reject) { var bgCanvas = document.createElement('canvas'); var puzzleCanvas = document.createElement('canvas'); var bgCtx = bgCanvas.getContext('2d'); var puzzleCtx = puzzleCanvas.getContext('2d'); internalLoadImage(imgUrl, cacheImage) .then(function (img) { if (imageWidth) { img.width = imageWidth; } if (imageHeight) { img.height = imageHeight; } var bgWidth = typeof outBgWidth === 'number' && outBgWidth > 0 ? outBgWidth > width ? outBgWidth : width : img.width; var bgHeight = typeof outBgHeight === 'number' && outBgHeight > 0 ? outBgHeight > height ? outBgHeight : height : img.height; bgCanvas.width = bgWidth; bgCanvas.height = bgHeight; var x = typeof outX === 'undefined' ? getRandomInt(bgWidth - width) : outX || 0; var y = typeof outY === 'undefined' ? getRandomInt(bgHeight - height) : outY || 0; if (x < 0) { x = 0; } else if (x > bgWidth - width) { x = bgWidth - width; } if (y < 0) { y = 0; } else if (y > bgHeight - height) { y = bgHeight - height; } var points = typeof outPoints === 'number' || !outPoints ? getRandomPoints(outPoints) : outPoints; var bgOffset = typeof outBgOffset === 'function' ? outBgOffset(img.width, img.height) : outBgOffset; if (bgCtx) { bgCtx.strokeStyle = borderColor; bgCtx.lineWidth = borderWidth; bgCtx.fillStyle = fillColor; drawPuzzle(bgCtx, { x: x, y: y, w: width, h: height, points: points, margin: margin }); bgCtx.fillStyle = fillColor; bgCtx.fill(); bgCtx.globalCompositeOperation = 'destination-over'; bgCtx.drawImage(img, bgOffset[0], bgOffset[1], img.width, img.height); } puzzleCanvas.width = bgWidth; puzzleCanvas.height = bgHeight; var puzzleUrl = ''; var singlePuzzleUrl = ''; if (puzzleCtx) { puzzleCtx.strokeStyle = borderColor; puzzleCtx.lineWidth = borderWidth; drawPuzzle(puzzleCtx, { x: x, y: y, w: width, h: height, points: points, margin: margin }); puzzleCtx.globalCompositeOperation = 'destination-over'; puzzleCtx.clip(); puzzleCtx.drawImage(img, bgOffset[0], bgOffset[1], img.width, img.height); // restore image var imgData = puzzleCtx.getImageData(x, y, width, height); puzzleCtx.clearRect(0, 0, bgWidth, bgHeight); puzzleCanvas.width = width; puzzleCtx.putImageData(imgData, 0, y); puzzleUrl = puzzleCanvas.toDataURL(); // single image puzzleCtx.clearRect(0, 0, width, bgHeight); puzzleCanvas.width = width; puzzleCanvas.height = height; puzzleCtx.putImageData(imgData, 0, 0); singlePuzzleUrl = puzzleCanvas.toDataURL(); } resolve({ bgUrl: bgCanvas.toDataURL(bgImageType, bgImageEncoderOptions), puzzleUrl: puzzleUrl, x: x, singlePuzzleUrl: singlePuzzleUrl, singlePuzzleY: y, }); }) .catch(reject); }); } exports.default = createPuzzle; exports.drawPuzzle = drawPuzzle; exports.getRandomInt = getRandomInt; exports.getRandomPoints = getRandomPoints; Object.defineProperty(exports, '__esModule', { value: true }); })); //# sourceMappingURL=createPuzzle.js.map