54leibo / blog Goto Github PK
View Code? Open in Web Editor NEW记录走过的脚步,用过往步伐鼓励自己继续前行
记录走过的脚步,用过往步伐鼓励自己继续前行
.parent {
display: flex;
}
.parent {
position: relative;
}
.child {
position: absolute;
top: 0;
bottom: 0; // 或者 height: 100%;
}
{
percentage: 9.05, // 下载百分比
transferred: 949624, // 已传输数据(单位byte)
length: 10485760, // 数据总长度
remaining: 9536136, // 剩余未下载
eta: 42, // 预计剩余时间
runtime: 3, // 已经消耗时间
delta: 295396, // 未知
speed: 949624 // 处理速度
}
const BufferHelper = require('bufferhelper');
const progress = require('progress-stream');
const { Duplex } = require('stream');
function _streamToBuffer(stream) {
return new Promise((resolve, reject) => {
const bufferHelper = new BufferHelper();
stream.on('error', reject);
stream.on('data', (chunk) => bufferHelper.concat(chunk));
stream.on('end', () => resolve(bufferHelper.toBuffer()));
});
}
const bufferStream = // buffer stream
const bufferSize = // buffer total size
const processGenerator = progress({
length: buffer.byteLength,
time: 100,
}, progressInfo => {
// process progessInfo
});
_streamToBuffer(bufferStream.pipe(processGenerator))
.then(() => {
// success process
})
.catch((e) => {
// error process
});
const BufferHelper = require('bufferhelper');
const progress = require('progress-stream');
const { Duplex } = require('stream');
function _bufferToStream(buffer, chunkCount = 100) {
if (!(buffer instanceof Buffer)) {
throw new Error(`buffer must be an instance of Buffer, get ${typeof buffer}`)
}
if (typeof chunkCount !== 'number') {
throw new Error(`chunkCount must be a number, get ${typeof chunkCount}`)
}
const size = buffer.byteLength;
let index = 0;
const chunkSize = Math.ceil(size / chunkCount);
const stream = new Duplex();
stream._read = () => {
if (index < chunkCount) {
stream.push(buffer.slice(index * chunkSize, (index + 1) * chunkSize));
// eslint-disable-next-line no-plusplus
index++
} else if(index === chunkCount) {
stream.push(buffer.slice(index * chunkCount, size + 1));
} else {
stream.push(null);
}
};
return stream;
}
const buffer = // buffer
const processGenerator = progress({
length: buffer.byteLength,
time: 100, /* ms */
}, progressInfo => {
options.onProgress(progressInfo);
});
try {
const bufferStream = _bufferToStream(buffer, 100).pipe(processGenerator);
} catch (e) {
// error process
}
/**
* 深度递归搜索
* @param {Array} arr 你要搜索的数组
* @param {Function} condition 回调函数,必须返回谓词,判断是否找到了。会传入item参数
* @param {String} children 子数组的key
*/
export function deepFind(arr, condition, children) {
// 即将返回的数组
const main = []
// 开始轮询
;(function poll(arr) {
// 如果传入非数组
if (!Array.isArray(arr)) return
// 遍历数组
for (let i = 0; i < arr.length; i++) {
// 获取当前项
const item = arr[i]
// 检验是否已经找到了
const isFind = condition && condition(item) || false
// 如果已经找到了
if (isFind) {
main.push(item)
// 如果存在children,那么深入递归
} else if (children && item[children] && item[children].length) {
poll(item[children])
}
}
})(arr, 0)
// 返回最终数组
return main
}
// example
const fuck = [
{
'label': '占用道路问题',
'value': 31,
'children': [
{
'label': '经营占道',
'value': 35,
'children': [
{ 'label': '店外经营占道', 'value': 40, 'children': null },
{
'label': '流动摊贩占道',
'value': 41,
'children': null
}
]
},
{
'label': '垃圾占道',
'value': 36,
'children': [
{ 'label': '生活垃圾', 'value': 42, 'children': null },
{
'label': '建筑垃圾',
'value': 43,
'children': null
},
{ 'label': '工业垃圾', 'value': 44, 'children': null }
]
},
{
'label': '车辆占道',
'value': 37,
'children': [
{ 'label': '机动车占道', 'value': 45, 'children': null },
{
'label': '非机动车占道',
'value': 46,
'children': null
}
]
},
{ 'label': '霸占车位', 'value': 38, 'children': [] },
{ 'label': '其他占道', 'value': 39, 'children': [] }
]
},
{
'label': '“两违”问题',
'value': 32,
'children': [
{
'label': '违法建筑',
'value': 58,
'children': [
{ 'label': '房屋违建', 'value': 61, 'children': null },
{
'label': '小区违建',
'value': 62,
'children': null
},
{ 'label': '违建棚架', 'value': 63, 'children': null }
]
},
{ 'label': '违法用地', 'value': 59, 'children': [] },
{ 'label': '其他违建', 'value': 60, 'children': [] }
]
},
{
'label': '市容设施管理问题',
'value': 33,
'children': [
{ 'label': '道路损坏', 'value': 47, 'children': [] },
{
'label': '垃圾桶损坏',
'value': 48,
'children': []
},
{ 'label': '下水道堵塞', 'value': 49, 'children': [] },
{
'label': '井盖损坏',
'value': 50,
'children': []
},
{ 'label': '路灯损坏', 'value': 51, 'children': [] },
{
'label': '树木修剪',
'value': 52,
'children': []
},
{ 'label': '水电气', 'value': 63, 'children': [] },
{
'label': '户外广告牌',
'value': 54,
'children': []
},
{ 'label': '隔音屏损坏', 'value': 55, 'children': [] },
{
'label': '洒水车问题',
'value': 56,
'children': []
},
{ 'label': '其他', 'value': 57, 'children': [] }
]
},
{ 'label': '其他问题', 'value': 34, 'children': [] }
]
const arr = deepFind(fuck, item => item.value === 63, 'children')
console.log(20181115092957, arr) // [{"label":"违建棚架","value":63,"children":null},{"label":"水电气","value":63,"children":[]}]
const isFunction = val => typeof val === 'function'
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
constructor(handle) {
// 保存Promise的状态和值
this._status = PENDING
this._value = undefined
// 保存单个Promise多次调用then的回调
this._fulfilledQueues = []
this._rejectedQueues = []
try {
handle(this._resolve.bind(this), this._reject.bind(this))
} catch (e) {
this._reject(e)
}
}
_resolve(val) {
if(this._status !== PENDING) return
const run = () => {
const runFulfilled = (value) => {
let cb
this._status = FULFILLED
this._value = val
// 依次执行成功队列回调
while (cb = this._fulfilledQueues.shift()) {
cb(value)
}
}
const runRejected = (value) => {
let cb
this._status = REJECTED
this._value = val
// 依次执行失败队列回调
while (cb = this._rejectedQueues.shift()) {
cb(value)
}
}
// 传入Promise则由传入Promise的结果决定_status和_value
if(val instanceof MyPromise) {
val.then(runFulfilled, runRejected)
return
}
runFulfilled(val)
}
// 模拟Promise在本次事件循环末执行
setTimeout(run, 0)
}
_reject(val) {
if(this._status !== PENDING) return
// 不同于_resolve,即时是传入Promise也直接作为值传递给回调
const run = () => {
this._status = REJECTED
this._value = val
let cb
// 依次执行失败队列回调
while (cb = this._rejectedQueues.shift()) {
cb()
}
}
// 模拟Promise在本次事件循环末执行
setTimeout(run, 0)
}
then(onFulFilled, onRejected) {
const { _status, _value } = this
return new MyPromise((onFulFilledNext, onRejectedNext) => {
const fulFilled = (val) => {
try {
// onFulFilled 不是函数
if(!isFunction(onFulFilled)) {
onFulFilledNext(val)
return
}
// onFulFilled 返回了Promise,则该Promise决定了then返回Promise的状态
const res = onFulFilled(val)
if(res instanceof MyPromise) {
res.then(onFulFilledNext, onRejectedNext)
return
}
// onFulFilled 返回普通值
onFulFilledNext(res)
} catch (err) {
// onFulFilled 内执行报错
onRejectedNext(err)
}
}
const rejected = (val) => {
try {
// onRejected 不是函数
if(!isFunction(onFulFilled)) {
onRejectedNext(val)
return
}
// onRejected 返回了Promise,则该Promise决定了then返回Promise的状态
const res = onRejected(val)
if(res instanceof MyPromise) {
res.then(onFulFilledNext, onRejectedNext)
return
}
// onRejected 返回普通值
onRejectedNext(res)
} catch (err) {
// onRejected 内执行报错
onRejectedNext(err)
}
}
switch (_status) {
// 当状态为pending时,将then方法回调函数加入执行队列等待执行
case PENDING:
this._fulfilledQueues.push(fulFilled)
this._rejectedQueues.push(rejected)
break
// 当前状态已改变是,立即执行回调函数
case FULFILLED:
fulFilled(_value)
break
case REJECTED:
rejected(_value)
break
}
})
}
catch(onRejected) {
return this.then(undefined, onRejected)
}
finally(onFinally) {
// onFinally 不是函数
if(!isFunction(onFinally)) {
onFinally = () => {}
}
// 返回Promise且保持原Promise结果,onFinally返回结果并不重要
// 暂时不对onFinally中报错进行处理,根据浏览器原生Promise处理,
// 如果finally回调函数中产生报错,则该错误会被返回
return this.then(
value => MyPromise.resolve(onFinally()).then(() => value),
error => MyPromise.resolve(onFinally()).then(() => {throw error})
)
}
static resolve(val) {
// 如果是Promise则直接返回
if(val instanceof MyPromise) return val
// 其它值则返回一个返回该值的已完成Promise
return new MyPromise(resolve => resolve(val))
}
static reject(val) {
// 不同于resolve,reject返回一个rejected的Promise,并把值直接返回
return new MyPromise((undefined, rejected) => rejected(val))
}
static all(list) {
if((list instanceof Array)) return
// 当全部为fulfilled或有一个为rejected时结束,返回一个Promise
// 若全部为fulfilled,则值为promiseArr中promise返回结果的按次序排列的数组
// 若有一个为rejected,则值为发生rejected的参数
const results = []
return MyPromise((resolve, reject) => {
for(let [i, v] of list.entries()) {
// 如果值不是Promise则构建一个
this.resolve(v).then((val) => {
results[i] = val
if(results.length === list.length) {
resolve(results)
}
}, err => reject(err))
}
})
}
static race(list) {
// 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
return new MyPromise((resolve, reject) => {
for (let p of list) {
this.resolve(p).then(res => {
resolve(res)
}, err => {
reject(err)
})
}
})
}
}
语法:const req= require.context(directory, useSubdirectories = false, regExp = /^.//);
例子1:多文件便捷载入(例子2有展示)
例子2:多文件便捷载入之SVG图标解决方案(elementUI使用的该方法,除此之外还可以使用Unicode、font-class详见参考资料)
目录说明:
svg文件夹存放svg图标并与index.js同目录存放
index.js:
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'// svg component
// register globally
Vue.component('svg-icon', SvgIcon)
const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)
SvgIcon组件:
<template>
<svg :class="svgClass" aria-hidden="true">
<use :href="iconName" />
</svg>
</template>
<script>
export default {
name: 'SvgIcon',
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String,
default: ''
}
},
computed: {
iconName() {
return `#icon-${this.iconClass}`
},
svgClass() {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
}
}
}
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>
/**
* Get chinese first spell letter
*
* @params {String} c: chinese
* @return {String} letter
*
function getChineseFirstSpellLetter(c) {
const classify = 'ABCDEFGHJKLMNOPQRSTWXYZ';
const boundaryChar = '驁簿錯鵽樲鰒餜靃攟鬠纙鞪黁漚曝裠鶸蜶籜鶩鑂韻糳';
let idx = -1;
if(!String.prototype.localeCompare) {
throw Error('String.prototype.localeCompare not supported.');
}
if(/[^\u4e00-\u9fa5]/.test(c)) {
return c;
}
for (let i = 0; i < boundaryChar.length; i++) {
if(boundaryChar[i].localeCompare(c, 'zh-CN-u-co-pinyin') >= 0) {
idx = i;
break;
}
}
return classify[idx];
}
/**
* @method processStr
* @params {String} val
* @return {String} val
*/
const processStr = val => {
val = val.replace(/[^a-z0-9\u4e00-\u9fa5]/ig, '')
const getLen = () => val.length + val.replace(/[^\u4e00-\u9fa5]/ig, '').length
let mixLen = getLen()
while (mixLen > 16) {
val = val.slice(0, val.length - 1)
mixLen = getLen()
}
return val
}
/**
* @arr: {Array} 操作数组
* @startPos: {Number} 需要移动数据开始项
* @length: {Number} 需要移动数据项数
* @moveSteps: {Number} 需要移动位数, positive number move right, minus number move left
* @return {Array}
* */
const moveArrayItem = (arr, startPos, length, moveSteps) => {
if (!(arr instanceof Array)) {
throw new Error(`arr can only be: Array, got:${Object.prototype.toString.call(arr)}`)
}
if (typeof startPos !== 'number') {
if (startPos < 0) {
throw new Error(`startPos can only be: positive integer, got:${startPos}`)
}
if (startPos.toString().indexOf('.') !== -1) {
throw new Error(`startPos can only be: positive integer, got:${startPos}`)
}
throw new Error(`startPos can only be: Number, got:${startPos}`)
}
if (typeof length !== 'number') {
if (length < 0) {
throw new Error(`length can only be: positive integer, got:${length}`)
}
if (length.toString().indexOf('.') !== -1) {
throw new Error(`startPos can only be: positive integer, got:${length}`)
}
throw new Error(`length can only be: Number, got:${length}`)
}
if (typeof moveSteps !== 'number') {
throw new Error(`moveSteps can only be: Number, got:${length}`)
}
const originArrLen = arr.length
const moveItems = arr.splice(startPos, length) || []
if (moveSteps < 0) {
moveSteps = Math.abs(moveSteps)
arr.splice(startPos - moveSteps >= 0 ? startPos - moveSteps : 0, 0, ...moveItems)
} else {
arr.splice(startPos + moveSteps <= originArrLen - 1 ? startPos + moveSteps : originArrLen - 1, 0, ...moveItems)
}
return arr
}
// example
const arr = [1, 2, 3, 4, 5, 6, 7]
console.log(`move: ${arr}`, moveArrayItem(arr, 1, 2, 1)) // [1, 4, 2, 3, 5, 6, 7]
class QueueJob {
queue(callback) {
const previous = this.pending || Promise.resolve()
this.pending = previous.then(callback).catch(callback)
// 利用闭包保存当前pending
const current = this.pending
current.then(() => {
// this.pending 完成时,this.pending没有被覆盖
if(this.pending === current) {
delete this.pending
}
})
}
}
var queueJob = new QueueJob()
queueJob.queue(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('first')
resolve()
}, 2000)
})
})
queueJob.queue(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('second')
resolve()
}, 1000)
})
})
queueJob.queue(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('three')
resolve()
}, 500)
})
})
// first
// second
// three
<form id="upload-form" action="upload.php" method="post" enctype="multipart/form-data" >
<input type="file" id="upload" name="upload" /> <br />
<input type="submit" value="Upload" />
</form>
html:
<form id="upload-form" action="upload.php" method="post" enctype="multipart/form-data" >
<input type="file" id="upload" name="upload" /> <br />
<input type="submit" value="Upload" />
</form>
js:
var form = $("#upload-form");
form.on('submit',function() {
var seed = Math.floor(Math.random() * 1000);
var id = "uploader-frame-" + seed;
var callback = "uploader-cb-" + seed;
var iframe = $('<iframe id="'+id+'" name="'+id+'" style="display:none;">');
var url = form.attr('action');
/*
首先,它为表单添加target属性,指向动态插入的iframe窗口,这使得上传结束后,服务器将结果返回iframe窗口,所以当前页面就不会跳转了。其次,它在action属性指定的上传网址的后面,添加了一个参数,使得服务器知道回调函数的名称。这样就能将服务器返回的信息,从iframe窗口传到上层页面。
*/
form.attr('target', id).append(iframe).attr('action', url + '?iframe=' + callback);
});
window[callback] = function(data){
console.log('received callback:', data);
iframe.remove(); //removing iframe
form.removeAttr('target');
form.attr('action', url);
window[callback] = undefined; //removing callback
};
php(upload.php):
<script type="text/javascript">
window.top.window['callback'](data);
</script>
if (window.FormData) {
var formData = new FormData()
// 建立一个upload表单项,值为上传的文件
formData.append('upload', document.getElementById('upload').files[0])
var xhr = new XMLHttpRequest()
xhr.open('POST', $(this).attr('action'))
// 定义上传完成后的回调函数
xhr.onload = function () {
if (xhr.status === 200) {
console.log('上传成功')
} else {
console.log('出错了')
}
}
xhr.send(formData)
}
css:
#holder {
border: 10px dashed #ccc;
width: 300px;
min-height: 300px;
margin: 20px auto;
}
#holder.hover {
border: 10px dashed #0c0;
}
html:
<div id="holder"></div>
js:
// 检查浏览器是否支持拖放上传。
if ('draggable' in document.createElement('span')) {
var holder = document.getElementById('holder')
holder.ondragover = function () {
this.className = 'hover'
return false
}
holder.ondragend = function () {
this.className = ''
return false
}
holder.ondrop = function (event) {
event.preventDefault()
this.className = ''
var files = event.dataTransfer.files
// do something with files
}
}
function downloadFile(actoinURL,filePath,fileName){
var form = $("<form>");
$('body').append(form);
form.attr('style','display:none');
form.attr('target','');
form.attr('method','post');
form.attr('action',actoinURL);//下载文件的请求路径
var input1 = $('<input>');
input1.attr('type','hidden');
input1.attr('name','filePath');
input1.attr('value',filePath);
form.append(input1);
var input2 = $('<input>');
input2.attr('type','hidden');
input2.attr('name','fileName');
input2.attr('value',fileName);
form.append(input2);
form.submit();
html:
<a id="download" href="">download</a>
js:
function downloadFile(fileName, content){
var aLink = document.createElement('a');
var blob = new Blob([content]);
aLink.download = fileName;
aLink.href = URL.createObjectURL(blob);
aLink.click();
}
downloadFile('aa.txt', '123')
function _ajax() {
return new Promise((resolve, reject) => {
...
})
}
/**
* HTTP Error retry
*
* @params {Object} options: ajax params
* @params {Number | undefined} limitCount: retry max count
* @params {Number | undefined} providedCount: current retry count
*/
function _retry(options, limitCount, providedCount) {
// set default value
if(!limitCount) {
limitCount = 3
}
if(!providedCount) {
providedCount = 0
}
return _ajax(options)
.catch(err => {
// retry condition
if(err.name === 'HTTPError' && providedCount < limitCount) {
return new Promise(resolve => {
// retry after 1 second
setTimeout(() => {
resolve(_retry(options, limitCount, providedCount++))
}, 1000)
})
}
throw err
})
}
document.body.onscroll = () => console.log('body onscroll') // 正常, 和window.onscroll相互覆盖
document.body.addEventListener('scroll', () => console.log('body addEventListener')) // 无效
document.onscroll = () => console.log('document onscroll') // 正常
document.addEventListener('scroll', () => console.log('document addEventListener')) // 正常
document.documentElement.onscroll = () => console.log('documentElement onscroll') // 无效
document.documentElement.addEventListener('scroll', () => console.log('documentElement addEventListener')) // 无效
window.onscroll = () => console.log('window onscroll') // 正常,和document.body.onscroll 相互覆盖
window.addEventListener('scroll', () => console.log('window addEventListener')) // 正常
说明:
- 在滚动body时,document对scroll的监听也会被触发
- 这些感觉有点凌乱的API应该是浏览器历史遗留问题,平常的话监听document即可
- 测试环境(userAgenet):Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
// 宽度和高度设置为0,再把另外三个border背景设置为transparent
#box{
position: relative;
width:0;
height:0;
border: 20px solid transparent;
border-top-color:red;
border-bottom-width:0;
}
// 如果需要空心三角形(箭头),利用before再叠加一个,正确设置其背景色,border宽度小一个像素,调整left和top值即可
#box: before {
content: ' ';
position: absolute;
left: -19px;
top: -21px;
width:0;
height:0;
border: 19px solid transparent;
border-top-color:#fff;
border-bottom-width:0;
}
const _arrayBufferToString = arrayBuffer =>
String.fromCharCode(...new Uint16Array(arrayBuffer))
const _stringToArrayBuffer = str =>
Uint16Array.from(str, c => c.charCodeAt(0)).buffer
// example
const str = '123'
const buf = _stringToArrayBuffer(str)
console.log(str, _arrayBufferToString(buf))
const _arrayBufferToBase64 = arrayBuffer =>
btoa(String.fromCharCode(...new Uint16Array(arrayBuffer)))
const _base64ToArrayBuffer = base64 =>
Uint16Array.from(atob(base64), c => c.charCodeAt(0)).buffer
// example
const base64 = window.btoa('123')
const buf = _base64ToArrayBuffer(base64)
console.log(base64, _arrayBufferToBase64(buf))
const _arrayBufferToHex = (arrayBuffer) =>
Array.prototype.map.call(new Uint16Array(arrayBuffer), x => ('00' + x.toString(16)).slice(-2)).join('')
const _hexToArrayBuffer = (str) => {
const len = str.length
const arrayBuffer = new ArrayBuffer(len)
const uint16 = new Uint16Array(arrayBuffer)
let b;
for (let i=0, j=0; i<len; i+=2) {
b = parseInt(str.substring(i, i+2), 16)
uint16[j++] = b
}
return arrayBuffer;
}
// example
const hex = "39b2abc192db9bc9175de7e5f9e7ef08"
const buffer = _hexToArrayBuffer(hex);
console.log(hex, _arrayBufferToHex(buffer), hex === _arrayBufferToHex(buffer))
一句话概括:用于在模块解析之前完成相应模块路径的替换,常用于满足不同环境下需要不同的资源。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.