A simple canvas captcha
// 随机数
const randomNum = (min, max) => {
return parseInt(Math.random() * (max - min) + min)
}
// 随机颜色
const randomColor = (min, max) => {
const r = randomNum(min, max)
const g = randomNum(min, max)
const b = randomNum(min, max)
return `rgb(${r},${g},${b})`
}
export default (el, state = {
pool: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890', // 字符串
width: 120,
height: 40,
}) => {
// 3.填充背景颜色,背景颜色要浅一点
const ctx = el.getContext('2d')
// 填充颜色
ctx.fillStyle = randomColor(180, 230)
// 填充的位置
ctx.fillRect(0, 0, state.width, state.height)
// 定义paramText
let imgCode = ''
// 4.随机产生字符串,并且随机旋转
for (let i = 0; i < 4; i++) {
// 随机的四个字
const text = state.pool[randomNum(0, state.pool.length)]
imgCode += text
// 随机的字体大小
const fontSize = randomNum(18, 40)
// 字体随机的旋转角度
const deg = randomNum(-30, 30)
/*
* 绘制文字并让四个文字在不同的位置显示的思路 :
* 1、定义字体
* 2、定义对齐方式
* 3、填充不同的颜色
* 4、保存当前的状态(以防止以上的状态受影响)
* 5、平移translate()
* 6、旋转 rotate()
* 7、填充文字
* 8、restore出栈
* */
ctx.font = fontSize + 'px Simhei'
ctx.textBaseline = 'top'
ctx.fillStyle = randomColor(80, 150)
/*
* save() 方法把当前状态的一份拷贝压入到一个保存图像状态的栈中。
* 这就允许您临时地改变图像状态,
* 然后,通过调用 restore() 来恢复以前的值。
* save是入栈,restore是出栈。
* 用来保存Canvas的状态。save之后,可以调用Canvas的平移、放缩、旋转、错切、裁剪等操作。 restore:用来恢复Canvas之前保存的状态。防止save后对Canvas执行的操作对后续的绘制有影响。
*
* */
ctx.save()
ctx.translate(30 * i + 15, 15)
ctx.rotate((deg * Math.PI) / 180)
// fillText() 方法在画布上绘制填色的文本。文本的默认颜色是黑色。
// 请使用 font 属性来定义字体和字号,并使用 fillStyle 属性以另一种颜色/渐变来渲染文本。
// context.fillText(text,x,y,maxWidth);
ctx.fillText(text, -15 + 5, -15)
ctx.restore()
}
// 5.随机产生5条干扰线,干扰线的颜色要浅一点
for (let i = 0; i < 5; i++) {
ctx.beginPath()
ctx.moveTo(randomNum(0, state.width), randomNum(0, state.height))
ctx.lineTo(randomNum(0, state.width), randomNum(0, state.height))
ctx.strokeStyle = randomColor(180, 230)
ctx.closePath()
ctx.stroke()
}
// 6.随机产生40个干扰的小点
for (let i = 0; i < 40; i++) {
ctx.beginPath()
ctx.arc(randomNum(0, state.width), randomNum(0, state.height), 1, 0, 2 * Math.PI)
ctx.closePath()
ctx.fillStyle = randomColor(150, 200)
ctx.fill()
}
return imgCode
}
A simple event emitter
export default {
events: {},
on(event, callback) {
if (!this.events[event]) {
this.events[event] = []
}
if (typeof callback === 'function') {
this.events[event].push(callback)
}
},
once(event, callback) {
if (typeof callback === 'function') {
const once = (...args) => {
callback(...args)
this.off(event, once)
}
this.on(event, once)
}
},
emit(event, ...args) {
if (this.events[event]) {
this.events[event].forEach(callback => {
callback(...args)
})
}
},
off(event, callback) {
const callbacks = this.events[event]
if (callbacks) {
if (typeof callback === 'function') {
this.events[event] = callbacks.filter(cb => cb.toString() !== callback.toString())
} else {
this.events[event] = null
}
}
},
}
Use UMD to create a jQuery plugin
(function(factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else if (typeof module === 'object' && module.exports) {
// Node/CommonJS
module.exports = function(root, jQuery) {
if (jQuery === undefined) {
// require('jQuery') returns a factory that requires window to
// build a jQuery instance, we normalize how we use modules
// that require this pattern but the window provided is a noop
// if it's defined (how jquery works)
if (typeof window !== 'undefined') {
jQuery = require('jquery');
} else {
jQuery = require('jquery')(root);
}
}
factory(jQuery);
return jQuery;
};
} else {
// Browser globals
factory(jQuery);
}
}(function($) {
$.fn.jqueryPlugin = function() {
return true;
};
}));
Lazy load images
function lazyLoad(offset = 0, selector = 'img[data-src]') {
const load = () => {
const imgs = [...document.querySelectorAll(selector + ':not(.loaded)')]
imgs.forEach(img => {
if (window.innerHeight - img.getBoundingClientRect().top + offset > 0) {
img.src = img.dataset.src
img.onload = () => img.classList.add('loaded')
}
})
}
load()
window.addEventListener('scroll', load, false)
window.addEventListener('resize', load, false)
}
Lazy load images with IntersectionObserver
function lazyLoad(offset = 0, selector = 'img[data-src]', root = null) {
const interactSettings = {
root: document.querySelector(root),
rootMargin: `0px 0px ${offset}px 0px`
}
const onIntersection = (entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target
observer.unobserve(img)
img.src = img.dataset.src
img.onload = () => img.classList.add('loaded')
}
})
}
const observer = new IntersectionObserver(onIntersection, interactSettings)
const imgs = [...document.querySelectorAll(selector + ':not(.loaded)')]
imgs.forEach(img => observer.observe(img))
}
Throttle
const throttle = (fn, wait = 17) => {
let lastTime = 0
return function(...args) {
let now = Date.now()
if (now - lastTime >= wait) {
lastTime = now
fn.apply(this, args)
}
}
}
Debounce
const debounce = (fn, wait = 17) => {
let timer = null
return function(...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, wait)
}
}
Throttle and Debounce
const throttleDebounce = (fn, wait = 17) => {
let lastTime = 0
let timer = null
return function(...args) {
let now = Date.now()
if (now - lastTime < wait) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
lastTime = now
fn.apply(this, args)
}, wait)
} else {
lastTime = now
fn.apply(this, args)
}
}
}
Lodash cloneDeep
function cloneDeep(obj) {
function isObject(o) {
return (typeof o === 'object' || typeof o === 'function') && o !== null
}
if (!isObject(obj)) {
return obj
}
let isArray = Array.isArray(obj)
let newObj = isArray ? [...obj] : { ...obj }
Reflect.ownKeys(newObj).forEach(key => {
newObj[key] = isObject(obj[key]) ? cloneDeep(obj[key]) : obj[key]
})
return newObj
}
Format a Date
Date.prototype.format = function(format) {
var o = {
'M+': this.getMonth() + 1, // month
'd+': this.getDate(), // day
'h+': this.getHours(), // hour
'm+': this.getMinutes(), // minute
's+': this.getSeconds(), // second
'q+': Math.floor((this.getMonth() + 3) / 3), // quarter
'S': this.getMilliseconds() // millisecond
}
if (/(y+)/.test(format)) {
format = format.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length));
}
for (var k in o) {
if (new RegExp('(' + k + ')').test(format)) {
format = format.replace(RegExp.$1, RegExp.$1.length == 1 ?
o[k] :
('00' + o[k]).substr(('' + o[k]).length));
}
}
return format;
};
new Date().format('yyyy-MM-dd hh:mm:ss'); // 2014-04-02 08:00:00
Set fetch body from an object
function setFetchBody(obj) {
let str = ''
if (!obj) return str
let isFirstKey = true
for (const key of Object.keys(obj)) {
if (isFirstKey) {
isFirstKey = false
str += `${key}=${obj[key]}`
} else {
str += `&${key}=${obj[key]}`
}
}
return str
}
Get items filter by including any one of other items
function getIncludedItems(arr1, arr2, prop) {
return arr1.filter(function(el) {
var included = false;
for (var i = 0; i < arr2.length; i++) {
var element = prop ? el[prop] : el;
if (element.includes(arr2[i])) {
included = true;
break;
}
}
return included;
});
}
getIncludedItems(['as', 'bs', 'cs'], ['a', 'b']);
// ['as', 'bs']
getIncludedItems([{ domain: 'as.com' }, { domain: 'bs.com' }, { domain: 'cs.com' }], ['as', 'bs'], 'domain');
// [{ domain: 'as.com' }, { domain: 'bs.com' }]
Remove a certain item and return the rest
function getRestItems(arr, value, prop) {
var rest = arr.slice();
for (var i = 0; i < rest.length; i++) {
if (prop && rest[i][prop] === value || !prop && rest[i] === value) {
rest.splice(i, 1);
break;
}
}
return rest;
}
getRestItems([1, 2, { x: 3 }], 2); // [1, { x: 3}]
getRestItems([1, 2, { x: 3 }], 3, 'x'); // [1, 2]
Stop double-scrolling propagation
$('.scrollable').on('DOMMouseScroll mousewheel', function(ev) {
var $this = $(this),
scrollTop = this.scrollTop,
scrollHeight = this.scrollHeight,
height = $this.height(),
delta = (ev.type == 'DOMMouseScroll' ?
ev.originalEvent.detail * -40 :
ev.originalEvent.wheelDelta),
up = delta > 0;
var prevent = function() {
ev.stopPropagation();
ev.preventDefault();
ev.returnValue = false;
return false;
};
if (!up && -delta > scrollHeight - height - scrollTop) {
$this.scrollTop(scrollHeight);
return prevent();
} else if (up && delta > scrollTop) {
$this.scrollTop(0);
return prevent();
}
});
Update URL query string
function updateQueryString(name, value) {
var href = location.href;
var search = location.search;
var hash = location.hash;
var query = getQueryString(name);
var hasQuery = typeof query !== 'object';
if (hasQuery) {
return href.replace(name + '=' + query, name + '=' + value);
} else {
if (search) {
return href.replace(search, search + '&' + name + '=' + value);
} else {
if (hash) {
return href.replace(hash, '?' + name + '=' + value + hash);
} else {
return href + '?' + name + '=' + value;
}
}
}
}
Get URL query string
function getQueryString(name, url) {
var href = url ? url : window.location.href;
var reg = new RegExp('[?&]' + name + '=([^&#]*)', 'i');
var string = reg.exec(href);
return string ? string[1] : null;
}
or:
function getQueryString(name, url) {
var search = url ? url : window.location.search;
return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(search)||[,""])[1].replace(/\+/g, '%20'))||null;
}
get the anchor:
function getAnchor(url) {
return url ? url.split('#')[1] : window.location.hash.substring(1);
}
Remove HTML tags
function strip(html) {
var tmp = document.createElement('DIV');
tmp.innerHTML = html;
return tmp.textContent || tmp.innerText || '';
}
with RegEx:
function strip(html) {
return html.replace(/(<([^>]+)>)/ig, '');
}
with jQuery:
function strip(html) {
return $(html).text();
}
Check for animation support
function browserSupportsCSSProperty(propertyName) {
var elm = document.createElement('div');
propertyName = propertyName.toLowerCase();
if (elm.style[propertyName] != undefined)
return true;
var propertyNameCapital = propertyName.charAt(0).toUpperCase() + propertyName.substr(1),
domPrefixes = 'Webkit Moz ms O'.split(' ');
for (var i = 0; i < domPrefixes.length; i++) {
if (elm.style[domPrefixes[i] + propertyNameCapital] != undefined)
return true;
}
return false;
}
if (!browserSupportsCSSProperty('animation')) {
// fallback…
}
Get the HTTP headers
var req = new XMLHttpRequest();
var headers = req.getAllResponseHeaders().toLowerCase();
req.open('GET', document.location, false);
req.send(null);
alert(headers);
with jQuery:
$.ajax({
url: location.href
})
.done(function(xhr) {
console.log(xhr.getAllResponseHeaders(), xhr.getResponseHeader('Server'));
});
Preload sound files
function preloadFiles(files, onComplete, onProgress) {
var filesLoaded = 0,
arr = [];
for (var i = 0, len = files.length; i < len; i++) {
arr[i] = new Audio();
arr[i].addEventListener('canplaythrough', function() {
filesLoaded++;
if (filesLoaded === len && typeof onComplete === 'function') onComplete();
if (typeof onProgress === 'function') onProgress(filesLoaded, len);
}, false);
arr[i].src = files[i];
arr[i].load();
};
}
A imitation of the localStorage object
if (!window.localStorage) {
window.localStorage = {
getItem: function(sKey) {
if (!sKey || !this.hasOwnProperty(sKey)) {
return null;
}
return unescape(document.cookie.replace(new RegExp("(?:^|.*;\\s*)" + escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*"), "$1"));
},
key: function(nKeyId) {
return unescape(document.cookie.replace(/\s*\=(?:.(?!;))*$/, "").split(/\s*\=(?:[^;](?!;))*[^;]?;\s*/)[nKeyId]);
},
setItem: function(sKey, sValue) {
if (!sKey) {
return;
}
document.cookie = escape(sKey) + "=" + escape(sValue) + "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/";
this.length = document.cookie.match(/\=/g).length;
},
length: 0,
removeItem: function(sKey) {
if (!sKey || !this.hasOwnProperty(sKey)) {
return;
}
document.cookie = escape(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
this.length--;
},
hasOwnProperty: function(sKey) {
return (new RegExp("(?:^|;\\s*)" + escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie);
}
};
window.localStorage.length = (document.cookie.match(/\=/g) || window.localStorage).length;
}
Set and get cookie
set cookie:
function setCookie(name, value, days, path, domain) {
var newDate = new Date();
var days = isNaN(days) ? 365 : days;
var expires;
var cookie;
newDate.setTime(newDate.getTime() + (days * 24 * 60 * 60 * 1000));
expires = 'expires=' + newDate.toUTCString();
cookie = name + '=' + value + ';' + expires + ';';
if (path) cookie += 'path=' + path + ';';
if (domain) cookie += 'domain=' + domain + ';';
document.cookie = cookie;
};
get cookie:
function getCookie(name) {
var name = name + '=';
var cookies = document.cookie.split(';');
var cookie;
for (var i = 0; i < cookies.length; i++) {
cookie = cookies[i];
while (cookie.charAt(0) == ' ') cookie = cookie.substring(1);
if (cookie.indexOf(name) == 0) return cookie.substring(name.length, cookie.length);
}
return '';
}
Get a shuffled copy of an array
var shuffle = function(arr) {
var len = arr.length,
shuffled = Array(len);
for (var index = 0, rand; index < len; index++) {
rand = Math.floor(Math.random() * (index + 1));
if (rand !== index) shuffled[index] = shuffled[rand];
shuffled[rand] = arr[index];
}
return shuffled;
};
Create dynamic variables
with eval():
eval('var variable' + i + '= arr[i]');
with window[]:
window['variable' + i] = arr[i];
Trigger click on an anchor
document.getElementsByTagName('a')[0].click();
with jQuery, you can use:
$('a')[0].click();