create-puzzle
Version:
在浏览器端生成滑块验证码的拼图和背景图。
347 lines (341 loc) • 15.2 kB
JavaScript
(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