123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- (function ($) {
- 'use strict';
- var SliderCaptcha = function (element, options) {
- this.$element = $(element);
- this.options = $.extend({}, SliderCaptcha.DEFAULTS, options);
- this.$element.css({ 'position': 'relative', 'width': this.options.width + 'px', 'margin': '0 auto' });
- this.init();
- };
- SliderCaptcha.VERSION = '1.0';
- SliderCaptcha.Author = 'argo@163.com';
- SliderCaptcha.DEFAULTS = {
- width: 280, // canvas宽度
- height: 155, // canvas高度
- PI: Math.PI,
- sliderL: 42, // 滑块边长
- sliderR: 9, // 滑块半径
- offset: 5, // 容错偏差
- loadingText: '正在加载中...',
- failedText: '再试一次',
- barText: '向右滑动填充拼图',
- repeatIcon: 'fa fa-repeat',
- maxLoadCount: 3,
- localImages: function () {
- return 'images/Pic' + Math.round(Math.random() * 4) + '.jpg';
- },
- verify: function (arr, url) {
- var that = this;
- var ret = false;
- $.ajax({
- url: url,
- data: JSON.stringify(arr),
- async: false,
- cache: false,
- type: 'POST',
- contentType: 'application/json',
- dataType: 'json',
- success: function (result) {
- // console.log(result)
- ret = result;
- if (result.resultCode == "0000") {
- ret = true;
- } else {
- ret = false;
- }
- that.verifySuccess && that.verifySuccess(result);
- }
- });
- return ret;
- },
- remoteUrl: null,
- verifySuccess: null
- };
- function Plugin(option) {
- return this.each(function () {
- var $this = $(this);
- var data = $this.data('lgb.SliderCaptcha');
- var options = typeof option === 'object' && option;
- if (data && !/reset/.test(option)) return;
- if (!data) $this.data('lgb.SliderCaptcha', data = new SliderCaptcha(this, options));
- if (typeof option === 'string') data[option]();
- });
- }
- $.fn.sliderCaptcha = Plugin;
- $.fn.sliderCaptcha.Constructor = SliderCaptcha;
- var _proto = SliderCaptcha.prototype;
- _proto.init = function () {
- this.initDOM();
- this.initImg();
- this.bindEvents();
- };
- _proto.initDOM = function () {
- var createElement = function (tagName, className) {
- var elment = document.createElement(tagName);
- elment.className = className;
- return elment;
- };
- var createCanvas = function (width, height) {
- var canvas = document.createElement('canvas');
- canvas.width = width;
- canvas.height = height;
- return canvas;
- };
- var canvas = createCanvas(this.options.width - 2, this.options.height); // 画布
- var block = canvas.cloneNode(true); // 滑块
- var sliderContainer = createElement('div', 'sliderContainer');
- var refreshIcon = createElement('i', 'refreshIcon ' + this.options.repeatIcon);
- var sliderMask = createElement('div', 'sliderMask');
- var sliderbg = createElement('div', 'sliderbg');
- var slider = createElement('div', 'slider');
- var sliderIcon = createElement('i', 'fa fa-arrow-right sliderIcon');
- var text = createElement('span', 'sliderText');
- block.className = 'block';
- text.innerHTML = this.options.barText;
- var el = this.$element;
- el.append($(canvas));
- el.append($(refreshIcon));
- el.append($(block));
- slider.appendChild(sliderIcon);
- sliderMask.appendChild(slider);
- sliderContainer.appendChild(sliderbg);
- sliderContainer.appendChild(sliderMask);
- sliderContainer.appendChild(text);
- el.append($(sliderContainer));
- var _canvas = {
- canvas: canvas,
- block: block,
- sliderContainer: $(sliderContainer),
- refreshIcon: refreshIcon,
- slider: slider,
- sliderMask: sliderMask,
- sliderIcon: sliderIcon,
- text: $(text),
- canvasCtx: canvas.getContext('2d'),
- blockCtx: block.getContext('2d')
- };
- if ($.isFunction(Object.assign)) {
- Object.assign(this, _canvas);
- }
- else {
- $.extend(this, _canvas);
- }
- };
- _proto.initImg = function () {
- var that = this;
- var isIE = window.navigator.userAgent.indexOf('Trident') > -1;
- var L = this.options.sliderL + this.options.sliderR * 2 + 3; // 滑块实际边长
- var drawImg = function (ctx, operation) {
- var l = that.options.sliderL;
- var r = that.options.sliderR;
- var PI = that.options.PI;
- var x = that.x;
- var y = that.y;
- ctx.beginPath();
- ctx.moveTo(x, y);
- ctx.arc(x + l / 2, y - r + 2, r, 0.72 * PI, 2.26 * PI);
- ctx.lineTo(x + l, y);
- ctx.arc(x + l + r - 2, y + l / 2, r, 1.21 * PI, 2.78 * PI);
- ctx.lineTo(x + l, y + l);
- ctx.lineTo(x, y + l);
- ctx.arc(x + r - 2, y + l / 2, r + 0.4, 2.76 * PI, 1.24 * PI, true);
- ctx.lineTo(x, y);
- ctx.lineWidth = 2;
- ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
- ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)';
- ctx.stroke();
- ctx[operation]();
- ctx.globalCompositeOperation = isIE ? 'xor' : 'destination-over';
- };
- var getRandomNumberByRange = function (start, end) {
- return Math.round(Math.random() * (end - start) + start);
- };
- var img = new Image();
- img.crossOrigin = "Anonymous";
- var loadCount = 0;
- img.onload = function () {
- // 随机创建滑块的位置
- that.x = getRandomNumberByRange(L + 10, that.options.width - (L + 10));
- that.y = getRandomNumberByRange(10 + that.options.sliderR * 2, that.options.height - (L + 10));
- drawImg(that.canvasCtx, 'fill');
- drawImg(that.blockCtx, 'clip');
- that.canvasCtx.drawImage(img, 0, 0, that.options.width - 2, that.options.height);
- that.blockCtx.drawImage(img, 0, 0, that.options.width - 2, that.options.height);
- var y = that.y - that.options.sliderR * 2 - 1;
- var ImageData = that.blockCtx.getImageData(that.x - 3, y, L, L);
- that.block.width = L;
- that.blockCtx.putImageData(ImageData, 0, y + 1);
- that.text.text(that.text.attr('data-text'));
- };
- img.onerror = function () {
- loadCount++;
- if (window.location.protocol === 'file:') {
- loadCount = that.options.maxLoadCount;
- console.error("can't load pic resource file from File protocal. Please try http or https");
- }
- if (loadCount >= that.options.maxLoadCount) {
- that.text.text('加载失败').addClass('text-danger');
- return;
- }
- img.src = that.options.localImages();
- };
- img.setSrc = function () {
- var src = '';
- loadCount = 0;
- that.text.removeClass('text-danger');
- if ($.isFunction(that.options.setSrc)) src = that.options.setSrc();
- if (!src || src === '') src = 'https://picsum.photos/' + that.options.width + '/' + that.options.height + '/?image=' + Math.round(Math.random() * 20);
- if (isIE) { // IE浏览器无法通过img.crossOrigin跨域,使用ajax获取图片blob然后转为dataURL显示
- var xhr = new XMLHttpRequest();
- xhr.onloadend = function (e) {
- var file = new FileReader(); // FileReader仅支持IE10+
- file.readAsDataURL(e.target.response);
- file.onloadend = function (e) {
- img.src = e.target.result;
- };
- };
- xhr.open('GET', src);
- xhr.responseType = 'blob';
- xhr.send();
- } else img.src = src;
- };
- img.setSrc();
- this.text.attr('data-text', this.options.barText);
- this.text.text(this.options.loadingText);
- this.img = img;
- };
- _proto.clean = function () {
- this.canvasCtx.clearRect(0, 0, this.options.width, this.options.height);
- this.blockCtx.clearRect(0, 0, this.options.width, this.options.height);
- this.block.width = this.options.width;
- };
- _proto.bindEvents = function () {
- var that = this;
- this.$element.on('selectstart', function () {
- return false;
- });
- $(this.refreshIcon).on('click', function () {
- that.text.text(that.options.barText);
- that.reset();
- if ($.isFunction(that.options.onRefresh)) that.options.onRefresh.call(that.$element);
- });
- var originX, originY, trail = [],
- isMouseDown = false;
- var handleDragStart = function (e) {
- if (that.text.hasClass('text-danger')) return;
- originX = e.clientX || e.touches[0].clientX;
- originY = e.clientY || e.touches[0].clientY;
- 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;
- var moveX = eventX - originX;
- var moveY = eventY - originY;
- if (moveX < 0 || moveX + 40 > that.options.width) return false;
- that.slider.style.left = (moveX - 1) + 'px';
- var blockLeft = (that.options.width - 40 - 20) / (that.options.width - 40) * moveX;
- that.block.style.left = blockLeft + 'px';
- that.sliderContainer.addClass('sliderContainer_active');
- that.sliderMask.style.width = (moveX + 4) + 'px';
- trail.push(Math.round(moveY));
- };
- var handleDragEnd = function (e) {
- if (!isMouseDown) return false;
- isMouseDown = false;
- var eventX = e.clientX || e.changedTouches[0].clientX;
- if (eventX === originX) return false;
- that.sliderContainer.removeClass('sliderContainer_active');
- that.trail = trail;
- var data = that.verify();
- if (data.spliced && data.verified) {
- that.sliderContainer.addClass('sliderContainer_success');
- if ($.isFunction(that.options.onSuccess)) that.options.onSuccess.call(that.$element);
- } else {
- that.sliderContainer.addClass('sliderContainer_fail');
- if ($.isFunction(that.options.onFail)) that.options.onFail.call(that.$element);
- setTimeout(function () {
- that.text.text(that.options.failedText);
- 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; });
- document.addEventListener('swipe', function () { return false; });
- };
- _proto.verify = function () {
- var arr = this.trail; // 拖动时y轴的移动距离
- var left = parseInt(this.block.style.left);
- var verified = false;
- if (this.options.remoteUrl !== null) {
- verified = this.options.verify(arr, this.options.remoteUrl);
- var sum = function (x, y) { return x + y; };
- var square = function (x) { return x * x; };
- var average = arr.reduce(sum) / arr.length;
- var deviations = arr.map(function (x) { return x - average; });
- var stddev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length);
- // console.log(stddev);
- /*verified = stddev !== 0;*/
- }
- else {
- var sum = function (x, y) { return x + y; };
- var square = function (x) { return x * x; };
- var average = arr.reduce(sum) / arr.length;
- var deviations = arr.map(function (x) { return x - average; });
- var stddev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length);
- verified = stddev !== 0;
- }
- return {
- spliced: Math.abs(left - this.x) < this.options.offset,
- verified: verified
- };
- };
- _proto.reset = function () {
- this.sliderContainer.removeClass('sliderContainer_fail sliderContainer_success');
- this.slider.style.left = 0;
- this.block.style.left = 0;
- this.sliderMask.style.width = 0;
- this.clean();
- this.text.attr('data-text', this.text.text());
- this.text.text(this.options.loadingText);
- this.img.setSrc();
- };
- })(jQuery);
|