(function (window) { let l = 50, // 滑块边长 r = 0, // 滑块半径 w = 300, // canvas宽度 h = 150, // canvas高度 PI = Math.PI; let L = l + r * 2; // 滑块实际边长 function createCanvas(width, height) { const canvas = createElement("canvas"); canvas.width = width; canvas.height = height; return canvas; } function createImg(onload, info) { const img = createElement("img"); img.crossOrigin = "Anonymous"; img.onload = onload; img.onerror = () => { img.src = getRandomImg(info.backgroundImage); }; img.src = getRandomImg(info.backgroundImage); return img; } function createImg2(onload, info) { const block = createElement("img"); block.crossOrigin = "Anonymous"; block.onload = onload; block.onerror = () => { block.src = getRandomImg(info.frontImage); }; block.src = getRandomImg(info.frontImage); return block; } function createElement(tagName) { return document.createElement(tagName); } function addClass(tag, className) { tag.classList.add(className); } function removeClass(tag, className) { tag.classList.remove(className); } function getRandomImg(val) { return val; } function sum(x, y) { return x + y; } function square(x) { return x * x; } function clear(obj) { obj.canvasCtx.clearRect(0, 0, w, h); obj.blockCtx.clearRect(0, 0, w, h); obj.block.width = w; document.getElementById("msg").innerHTML = null; document.getElementById("captcha").innerHTML = null; } class jigsaw { constructor(el, success, fail, info, parent) { this.el = el; this.success = success; this.fail = fail; this.info = info; this.parent = parent; } init() { if (this.info) { const { backgroundHeight, backgroundWidth, frontHeight, frontWidth } = this.info; l = frontHeight; r = 0; w = backgroundWidth; h = backgroundHeight; L = frontWidth; } this.initDOM(); this.initImg(); this.bindEvents(); } initDOM() { const canvas = createCanvas(w, h); // 画布 const block = canvas.cloneNode(true); // 滑块 const msg = createElement("div"); const sliderContainer = createElement("div"); const refreshIcon = createElement("div"); const sliderMask = createElement("div"); const slider = createElement("div"); const sliderIcon = createElement("span"); const text = createElement("span"); block.className = "block"; msg.id = "msg"; sliderContainer.className = "sliderContainer"; refreshIcon.className = "refreshIcon"; sliderMask.className = "sliderMask"; slider.className = "slider"; sliderIcon.className = "sliderIcon"; text.innerHTML = "向右滑动滑块填充拼图"; text.className = "sliderText"; const el = this.el; el.appendChild(canvas); el.appendChild(refreshIcon); el.appendChild(block); el.appendChild(msg); slider.appendChild(sliderIcon); sliderMask.appendChild(slider); sliderContainer.appendChild(sliderMask); sliderContainer.appendChild(text); el.appendChild(sliderContainer); Object.assign(this, { canvas, block, sliderContainer, refreshIcon, slider, sliderMask, sliderIcon, text, msg, canvasCtx: canvas.getContext("2d"), blockCtx: block.getContext("2d"), addClass, clear, }); } initImg() { const block = createImg2(() => { this.blockCtx.drawImage(block, 0, 0, w, h); }, this.info); const img = createImg(() => { this.canvasCtx.drawImage(img, 0, 0, w, h); }, this.info); this.img = img; } clean() { this.canvasCtx.clearRect(0, 0, w, h); this.blockCtx.clearRect(0, 0, w, h); this.block.width = w; } bindEvents() { this.el.onselectstart = () => false; this.refreshIcon.onclick = () => { this.reset(); }; let originX, originY, trail = [], isMouseDown = false; var that = this; var handleDragStart = function (e) { originX = e.clientX || e.touches[0].clientX; originY = e.clientY || e.touches[0].clientY; // (originX = e.x), (originY = e.y); isMouseDown = true; }; var handleDragMove = function (e) { if (!isMouseDown) return false; var eventX = e.clientX || e.touches[0].clientX; var eventY = e.clientY || e.touches[0].clientY; const moveX = eventX - originX; const moveY = eventY - originY; if (moveX < 0 || moveX + 38 >= w) return false; that.slider.style.left = moveX + "px"; var blockLeft = ((w - 40 - 20) / (w - 40)) * moveX; that.block.style.left = blockLeft + "px"; addClass(that.sliderContainer, "sliderContainer_active"); that.sliderMask.style.width = moveX + "px"; console.log(that.sliderMask.offsetLeft, 4545); trail.push(moveY); }; var handleDragEnd = function (e) { if (!isMouseDown) return false; isMouseDown = false; var eventX = e.clientX || e.changedTouches[0].clientX; if (eventX == originX) return false; removeClass(that.sliderContainer, "sliderContainer_active"); that.trail = trail; const { left } = that.verify(); const { prefix } = that.info; if (left && prefix) { that.success(left, prefix, that); } else { addClass(that.sliderContainer, "sliderContainer_fail"); that.fail && that.fail(left, prefix); setTimeout(() => { that.reset(); }, 1000); } }; this.slider.addEventListener("mousedown", handleDragStart); this.slider.addEventListener("touchstart", handleDragStart); document.addEventListener("mousemove", handleDragMove); document.addEventListener("touchmove", handleDragMove); document.addEventListener("mouseup", handleDragEnd); document.addEventListener("touchend", handleDragEnd); document.addEventListener("mousedown", function () { return false; }); document.addEventListener("touchstart", function () { return false; }); // this.slider.addEventListener("mousedown"); // document.addEventListener("mousemove"); // document.addEventListener("mouseup"); } verify() { const arr = this.trail; // 拖动时y轴的移动距离 const average = arr.reduce(sum) / arr.length; // 平均值 const deviations = arr.map((x) => x - average); // 偏差数组 const stddev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length); // 标准差 const left = parseInt(this.block.style.left); return { // spliced: Math.abs(left - this.x) < 10, // TuringTest: average !== stddev, // 只是简单的验证拖动轨迹,相等时一般为0,表示可能非人为操作 left, // x: this.x, }; } reset() { this.sliderContainer.className = "sliderContainer"; this.slider.style.left = 0; this.block.style.left = 0; this.sliderMask.style.width = 0; clear(this); // this.initImg(); this.parent.getImg(); console.log(this.parent); } } window.jigsaw = { init: function (element, success, fail, info, callback) { new jigsaw(element, success, fail, info, callback).init(); }, }; })(window);