описывать
Все функции родныеjavascript
Реализация без каких-либо зависимостей, мой последовательный подход заключается в использовании наименьшего количества кода для создания наиболее эффективных вещей.
я делаю кое-чтоH5
Когда дело доходит до одной страницы (активной страницы), такие люди, как я, хотят получить максимальную скорость загрузки и не любят использовать сторонние библиотеки, поэтому я обычно делаю некоторые из них сам.无依赖
,精简高效
а затем применить его к реальному проекту по мере необходимости и поделиться им со старым нуждающимся железом, чтобы найти код лучше, чем Baidu.
Здесь рекомендуется использовать интерфейсvs code
Это редактор кода, причина в том, чтобы написать стандарт во время объявленияJSDoc
Комментарии, при звонке будут очень исчерпывающие подсказки по коду, так что слабо типизированныйjavascript
Также есть подсказки по типам
1. HTTP-запрос
Основные навыки работы с интерфейсом также являются наиболее часто используемыми функциями. я лично не люблю использоватьaxios
Эта штука (лень читать документацию, и чувствую себя очень безвкусно), почти всеweb
Все проекты используют это колесо.
Первый:fetch
/**
* 基于`fetch`请求 [MDN文档](https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API)
* @param {"GET"|"POST"|"PUT"|"DELETE"} method 请求方法
* @param {string} url 请求路径
* @param {object} data 请求参数对象
* @param {number} timeout 超时毫秒
*/
function fetchRequest(method, url, data = {}, timeout = 5000) {
let body = null;
let query = "";
if (method === "GET") {
// 解析对象传参
for (const key in data) {
query += `&${key}=${data[key]}`;
}
if (query) {
query = "?" + query.slice(1);
}
} else {
// 若后台没设置接收 JSON 则不行 需要跟 GET 一样的解析对象传参
body = JSON.stringify(data);
}
return new Promise((resolve, reject) => {
fetch(url + query, {
// credentials: "include", // 携带cookie配合后台用
// mode: "cors", // 貌似也是配合后台设置用的跨域模式
method: method,
headers: {
"Content-Type": "application/json"
// "Content-Type": "application/x-www-form-urlencoded"
},
body: body
}).then(response => {
// 把响应的信息转为`json`
return response.json();
}).then(res => {
resolve(res);
}).catch(error => {
reject(error);
});
setTimeout(reject.bind(this, "fetch is timeout"), timeout);
});
}
Специальное примечание:Я здесьH5
Некоторая простота одной страницыGET
Обычно он чаще всего используется при запросе, потому что кода очень мало, как показано ниже.
fetch("http://xxx.com/api/get").then(response => response.text()).then(res => {
console.log("请求成功", res);
})
Второй (и самый распространенный):XMLHttpRequest
,нужноPromise
Используйте дополнительный слой хлебаfunction
Делать вторичную упаковку
/**
* `XMLHttpRequest`请求 [MDN文档](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
* @param {object} params 传参对象
* @param {string} params.url 请求路径
* @param {"GET"|"POST"|"PUT"|"DELETE"} params.method 请求方法
* @param {object|FormData|string} params.data 传参对象,json、formdata、普通表单字符串
* @param {{ [key: string]: string }} params.headers `XMLHttpRequest.header`设置对象
* @param {number?} params.overtime 超时检测毫秒数
* @param {(result?: any, response: XMLHttpRequest) => void} params.success 成功回调
* @param {(error?: XMLHttpRequest) => void} params.fail 失败回调
* @param {(info?: XMLHttpRequest) => void} params.timeout 超时回调
* @param {(res?: ProgressEvent<XMLHttpRequestEventTarget>) => void} params.progress 进度回调(暂时没用到)
*/
function ajax(params) {
if (checkType(params) !== "object") return console.error("ajax 请求参数类型有误");
if (!params.method) return console.error("ajax 缺少请求方法");
if (!params.url) return console.error("ajax 缺少请求 url");
const XHR = new XMLHttpRequest();
/** 请求方法 */
const method = params.method;
/** 超时检测 */
const overtime = checkType(params.overtime) === "number" ? params.overtime : 0;
/** 请求链接 */
let url = params.url;
/** 非`GET`请求传参 */
let body = null;
/** `GET`请求传参 */
let query = "";
/** 传参数据类型 */
const dataType = checkType(params.data);
// 传参处理
if (method === "GET") {
// 解析对象传参
if (dataType === "object") {
for (const key in params.data) {
query += "&" + key + "=" + params.data[key];
}
} else {
console.warn("ajax 传参处理 GET 传参有误,需要的请求参数应为 object 类型");
}
if (query) {
query = "?" + query.slice(1);
url += query;
}
} else {
body = dataType === "object" ? JSON.stringify(params.data) : params.data;
}
// 监听请求变化;XHR.status learn: http://tool.oschina.net/commons?type=5
XHR.onreadystatechange = function () {
if (XHR.readyState !== 4) return;
if (XHR.status === 200 || XHR.status === 304) {
typeof params.success === "function" && params.success(JSON.parse(XHR.response), XHR);
} else {
typeof params.fail === "function" && params.fail(XHR);
}
}
// 判断请求进度
if (params.progress) {
XHR.addEventListener("progress", params.progress);
}
// XHR.responseType = "json"; // 设置响应结果为`json`这个一般由后台返回指定格式,前端无配置
// XHR.withCredentials = true; // 是否Access-Control应使用cookie或授权标头等凭据进行跨站点请求。
XHR.open(method, url, true);
// 设置对应的传参请求头,GET 方法不需要
if (params.method !== "GET") {
switch (dataType) {
case "object":
XHR.setRequestHeader("Content-Type", "application/json"); // `json`请求
break;
case "string":
XHR.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); // 表单请求,非`new FormData`
break;
default:
break;
}
}
// 判断设置配置头信息
if (params.headers) {
for (const key in params.headers) {
const value = params.headers[key];
XHR.setRequestHeader(key, value);
}
}
// 在IE中,超时属性只能在调用 open() 方法之后且在调用 send() 方法之前设置。
if (overtime > 0) {
XHR.timeout = overtime;
XHR.ontimeout = function () {
console.warn("XMLHttpRequest 请求超时 !!!");
XHR.abort();
typeof params.timeout === "function" && params.timeout(XHR);
}
}
XHR.send(body);
}
Демонстрация фактического использования проекта
2. компонент карусели swiper
Это первый, который я написал.web
функциональные компоненты,拖拽
,回弹
Физические эффекты относятся к проектам с открытым исходным кодом.Swiper.js
Сделайте это, функция эффекта непротиворечива, а реализация кода выполняется самостоятельно.
/**
* 轮播组件
* @param {object} params 配置传参
* @param {string} params.el 组件节点 class|id|<label>
* @param {number} params.moveTime 过渡时间(毫秒)默认 300
* @param {number} params.interval 自动播放间隔(毫秒)默认 3000
* @param {boolean} params.loop 是否需要回路
* @param {boolean} params.vertical 是否垂直滚动
* @param {boolean} params.autoPaly 是否需要自动播放
* @param {boolean} params.pagination 是否需要底部圆点
* @param {(index: number) => void} params.slideCallback 滑动/切换结束回调
* @author https://github.com/Hansen-hjs
* @description
* 移动端`swiper`组件,如果需要兼容`pc`自行修改对应的`touch`到`mouse`事件即可。现成效果预览:https://huangjingsheng.gitee.io/hjs/cv/demo/face/
*/
function swiper(params) {
/**
* css class 命名列表
* @dec ["滑动列表","滑动item","圆点容器","底部圆点","圆点高亮"]
*/
const classNames = [".swiper_list", ".swiper_item", ".swiper_pagination", ".swiper_dot", ".swiper_dot_active"];
/** 滑动结束函数 */
const slideEnd = params.slideCallback || function() {};
/**
* 组件节点
* @type {HTMLElement}
*/
let node = null;
/**
* item列表容器
* @type {HTMLElement}
*/
let nodeItem = null;
/**
* item节点列表
* @type {Array<HTMLElement>}
*/
let nodeItems = [];
/**
* 圆点容器
* @type {HTMLElement}
*/
let nodePagination = null;
/**
* 圆点节点列表
* @type {Array<HTMLElement>}
*/
let nodePaginationItems = [];
/** 是否需要底部圆点 */
let pagination = false;
/** 是否需要回路 */
let isLoop = false;
/** 方向 `X => true` | `Y => false` */
let direction = false;
/** 是否需要自动播放 */
let autoPaly = false;
/** 自动播放间隔(毫秒)默认 3000 */
let interval = 3000;
/** 过渡时间(毫秒)默认 300 */
let moveTime = 300;
/** 设置动画 */
function startAnimation() {
nodeItem.style.transition = `${moveTime / 1000}s all`;
}
/** 关闭动画 */
function stopAnimation() {
nodeItem.style.transition = "0s all";
}
/**
* 属性样式滑动
* @param {number} n 移动的距离
*/
function slideStyle(n) {
let x = 0, y = 0;
if (direction) {
y = n;
} else {
x = n;
}
nodeItem.style.transform = `translate3d(${x}px, ${y}px, 0px)`;
}
/**
* 事件开始
* @param {number} width 滚动容器的宽度
* @param {number} height 滚动容器的高度
*/
function main(width, height) {
/**
* 动画帧
* @type {requestAnimationFrame}
*/
const animation = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
/** 触摸开始时间 */
let startTime = 0;
/** 触摸结束时间 */
let endTime = 0;
/** 开始的距离 */
let startDistance = 0;
/** 结束的距离 */
let endDistance = 0;
/** 结束距离状态 */
let endState = 0;
/** 移动的距离 */
let moveDistance = 0;
/** 圆点位置 && 当前 item 索引 */
let index = 0;
/** 动画帧计数 */
let count = 0;
/** loop 帧计数 */
let loopCount = 0;
/** 移动范围 */
let range = direction ? height : width;
/** 获取拖动距离 */
function getDragDistance() {
/** 拖动距离 */
let dragDistance = 0;
// 默认这个公式
dragDistance = moveDistance + (endDistance - startDistance);
// 判断最大正负值
if ((endDistance - startDistance) >= range) {
dragDistance = moveDistance + range;
} else if ((endDistance - startDistance) <= -range) {
dragDistance = moveDistance - range;
}
// 没有 loop 的时候惯性拖拽
if (!isLoop) {
if ((endDistance - startDistance) > 0 && index === 0) {
// console.log("到达最初");
dragDistance = moveDistance + ((endDistance - startDistance) - ((endDistance - startDistance) * 0.6));
} else if ((endDistance - startDistance) < 0 && index === nodeItems.length - 1) {
// console.log("到达最后");
dragDistance = moveDistance + ((endDistance - startDistance) - ((endDistance - startDistance) * 0.6));
}
}
return dragDistance;
}
/**
* 判断触摸处理函数
* @param {number} slideDistance 滑动的距离
*/
function judgeTouch(slideDistance) {
// 这里我设置了200毫秒的有效拖拽间隔
if ((endTime - startTime) < 200) return true;
// 这里判断方向(正值和负值)
if (slideDistance < 0) {
if ((endDistance - startDistance) < (slideDistance / 2)) return true;
return false;
} else {
if ((endDistance - startDistance) > (slideDistance / 2)) return true;
return false;
}
}
/** 返回原来位置 */
function backLocation() {
startAnimation();
slideStyle(moveDistance);
}
/**
* 滑动
* @param {number} slideDistance 滑动的距离
*/
function slideMove(slideDistance) {
startAnimation();
slideStyle(slideDistance);
loopCount = 0;
// 判断 loop 时回到第一张或最后一张
if (isLoop && index < 0) {
// 我这里是想让滑块过渡完之后再重置位置所以加的延迟 (之前用setTimeout,快速滑动有问题,然后换成 requestAnimationFrame解决了这类问题)
function loopMoveMin() {
loopCount += 1;
if (loopCount < moveTime / 1000 * 60) return animation(loopMoveMin);
stopAnimation();
slideStyle(range * -(nodeItems.length - 3));
// 重置一下位置
moveDistance = range * -(nodeItems.length - 3);
}
loopMoveMin();
index = nodeItems.length - 3;
} else if (isLoop && index > nodeItems.length - 3) {
function loopMoveMax() {
loopCount += 1;
if (loopCount < moveTime / 1000 * 60) return animation(loopMoveMax);
stopAnimation();
slideStyle(0);
moveDistance = 0;
}
loopMoveMax();
index = 0;
}
// console.log(`第${ index+1 }张`); // 这里可以做滑动结束回调
if (pagination) {
nodePagination.querySelector(classNames[4]).className = classNames[3].slice(1);
nodePaginationItems[index].classList.add(classNames[4].slice(1));
}
}
/** 判断移动 */
function judgeMove() {
// 判断是否需要执行过渡
if (endDistance < startDistance) {
// 往上滑动 or 向左滑动
if (judgeTouch(-range)) {
// 判断有loop的时候不需要执行下面的事件
if (!isLoop && moveDistance === (-(nodeItems.length - 1) * range)) return backLocation();
index += 1;
slideMove(moveDistance - range);
moveDistance -= range;
slideEnd(index);
} else {
backLocation();
}
} else {
// 往下滑动 or 向右滑动
if (judgeTouch(range)) {
if (!isLoop && moveDistance === 0) return backLocation();
index -= 1;
slideMove(moveDistance + range);
moveDistance += range;
slideEnd(index)
} else {
backLocation();
}
}
}
/** 自动播放移动 */
function autoMove() {
// 这里判断 loop 的自动播放
if (isLoop) {
index += 1;
slideMove(moveDistance - range);
moveDistance -= range;
} else {
if (index >= nodeItems.length - 1) {
index = 0;
slideMove(0);
moveDistance = 0;
} else {
index += 1;
slideMove(moveDistance - range);
moveDistance -= range;
}
}
slideEnd(index);
}
/** 开始自动播放 */
function startAuto() {
count += 1;
if (count < interval / 1000 * 60) return animation(startAuto);
count = 0;
autoMove();
startAuto();
}
// 判断是否需要开启自动播放
if (autoPaly && nodeItems.length > 1) startAuto();
// 开始触摸
nodeItem.addEventListener("touchstart", ev => {
startTime = Date.now();
count = 0;
loopCount = moveTime / 1000 * 60;
stopAnimation();
startDistance = direction ? ev.touches[0].clientY : ev.touches[0].clientX;
});
// 触摸移动
nodeItem.addEventListener("touchmove", ev => {
ev.preventDefault();
count = 0;
endDistance = direction ? ev.touches[0].clientY : ev.touches[0].clientX;
slideStyle(getDragDistance());
});
// 触摸离开
nodeItem.addEventListener("touchend", () => {
endTime = Date.now();
// 判断是否点击
if (endState !== endDistance) {
judgeMove();
} else {
backLocation();
}
// 更新位置
endState = endDistance;
// 重新打开自动播
count = 0;
});
}
/**
* 输出回路:如果要回路的话前后增加元素
* @param {number} width 滚动容器的宽度
* @param {number} height 滚动容器的高度
*/
function outputLoop(width, height) {
const first = nodeItems[0].cloneNode(true), last = nodeItems[nodeItems.length - 1].cloneNode(true);
nodeItem.insertBefore(last, nodeItems[0]);
nodeItem.appendChild(first);
nodeItems.unshift(last);
nodeItems.push(first);
if (direction) {
nodeItem.style.top = `${-height}px`;
} else {
nodeItem.style.left = `${-width}px`;
}
}
/**
* 输出动态布局
* @param {number} width 滚动容器的宽度
* @param {number} height 滚动容器的高度
*/
function outputLayout(width, height) {
if (direction) {
for (let i = 0; i < nodeItems.length; i++) {
nodeItems[i].style.height = `${height}px`;
}
} else {
nodeItem.style.width = `${width * nodeItems.length}px`;
for (let i = 0; i < nodeItems.length; i++) {
nodeItems[i].style.width = `${width}px`;
}
}
}
/** 输出底部圆点 */
function outputPagination() {
let paginations = "";
nodePagination = node.querySelector(classNames[2]);
// 如果没有找到对应节点则创建一个
if (!nodePagination) {
nodePagination = document.createElement("div");
nodePagination.className = classNames[2].slice(1);
node.appendChild(nodePagination);
}
for (let i = 0; i < nodeItems.length; i++) {
paginations += `<div class="${classNames[3].slice(1)}"></div>`;
}
nodePagination.innerHTML = paginations;
nodePaginationItems = [...nodePagination.querySelectorAll(classNames[3])];
nodePagination.querySelector(classNames[3]).classList.add(classNames[4].slice(1));
}
/** 初始化动态布局 */
function initLayout() {
node = document.querySelector(params.el);
if (!node) return console.warn("没有可执行的节点!");
nodeItem = node.querySelector(classNames[0]);
if (!nodeItem) return console.warn(`缺少"${classNames[0]}"节点!`);
nodeItems = [...node.querySelectorAll(classNames[1])];
if (nodeItems.length == 0) return console.warn("滑动节点个数必须大于0!");
const moveWidth = node.offsetWidth, moveHeight = node.offsetHeight;
if (pagination) outputPagination();
if (isLoop) outputLoop(moveWidth, moveHeight);
outputLayout(moveWidth, moveHeight);
main(moveWidth, moveHeight);
}
/** 初始化参数 */
function initParams() {
if (typeof params !== "object") return console.warn("传参有误");
pagination = params.pagination || false;
direction = params.vertical || false;
autoPaly = params.autoPaly || false;
isLoop = params.loop || false;
moveTime = params.moveTime || 300;
interval = params.interval || 3000;
initLayout();
}
initParams();
}
Адрес исходного кода и отображение использования
3. Ленивая загрузка изображения
Нетрадиционная реализация, оптимальная производительность
/**
* 懒加载
* @description 可加载`<img>`、`<video>`、`<audio>`等一些引用资源路径的标签
* @param {object} params 传参对象
* @param {string?} params.lazyAttr 自定义加载的属性(可选)
* @param {"src"|"background"} params.loadType 加载的类型(默认为`src`)
* @param {string?} params.errorPath 加载失败时显示的资源路径,仅在`loadType`设置为`src`中可用(可选)
*/
function lazyLoad(params) {
const attr = params.lazyAttr || "lazy";
const type = params.loadType || "src";
/** 更新整个文档的懒加载节点 */
function update() {
const els = document.querySelectorAll(`[${attr}]`);
for (let i = 0; i < els.length; i++) {
const el = els[i];
observer.observe(el);
}
}
/**
* 加载图片
* @param {HTMLImageElement} el 图片节点
*/
function loadImage(el) {
const cache = el.src; // 缓存当前`src`加载失败时候用
el.src = el.getAttribute(attr);
el.onerror = function () {
el.src = params.errorPath || cache;
}
}
/**
* 加载单个节点
* @param {HTMLElement} el
*/
function loadElement(el) {
switch (type) {
case "src":
loadImage(el);
break;
case "background":
el.style.backgroundImage = `url(${el.getAttribute(attr)})`;
break;
}
el.removeAttribute(attr);
observer.unobserve(el);
}
/**
* 监听器
* [MDN说明](https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver)
*/
const observer = new IntersectionObserver(function(entries) {
for (let i = 0; i < entries.length; i++) {
const item = entries[i];
if (item.isIntersecting) {
loadElement(item.target);
}
}
})
update();
return {
observer,
update
}
}
существуетvue
используйте команду для использования
import Vue from "vue";
/** 添加一个加载`src`的指令 */
const lazySrc = lazyLoad({
lazyAttr: "vlazy",
errorPath: "./img/error.jpg"
})
Vue.directive("v-lazy", {
inserted(el, binding) {
el.setAttribute("vlazy", binding.value); // 跟上面的对应
lazySrc.observer.observe(el);
}
})
/** 添加一个加载`background`的指令 */
const lazyBg = lazyLoad({
lazyAttr: "vlazybg",
loadType: "background"
})
Vue.directive("v-lazybg", {
inserted(el, binding) {
el.setAttribute("vlazybg", binding.value); // 跟上面的对应
lazyBg.observer.observe(el);
}
})
Адрес исходного кода и отображение использования
4. Загрузить изображения
Это супер просто, нечего сказать
<!-- 先准备好一个input标签,然后设置type="file",最后挂载一个onchange事件 -->
<input class="upload-input" type="file" name="picture" onchange="upLoadImage(this)">
/**
* input上传图片
* @param {HTMLInputElement} el
*/
function upLoadImage(el) {
/** 上传文件 */
const file = el.files[0];
/** 上传类型数组 */
const types = ["image/jpg", "image/png", "image/jpeg", "image/gif"];
// 判断文件类型
if (types.indexOf(file.type) < 0) {
file.value = null; // 这里一定要清空当前错误的内容
return alert("文件格式只支持:jpg 和 png");
}
// 判断大小
if (file.size > 2 * 1024 * 1024) {
file.value = null;
return alert("上传的文件不能大于2M");
}
const formData = new FormData(); // 这个是传给后台的数据
formData.append("img", file); // 这里`img`是跟后台约定好的`key`字段
console.log(formData, file);
// 最后POST给后台,这里我用上面的方法
ajax({
url: "http://xxx.com/uploadImg",
method: "POST",
data: formData,
overtime: 8000,
success(res) {
console.log("上传成功", res);
},
fail(err) {
console.log("上传失败", err);
},
timeout() {
console.warn("XMLHttpRequest 请求超时 !!!");
}
});
}
преобразование base64 и статический предварительный просмотр
Сотрудничайте с интерфейсом для загрузки в фоновом режимеВозможно, для этого придется установить среду, потому что это проект на стороне сервера.
5. Потяните вниз, чтобы обновить компонент
Эффект перетаскивания см. вышеswiper
Способ реализации, эффект в выпадающем списке можете определить сами
// 这里我做的不是用 window 的滚动事件,而是用最外层的绑定触摸下拉事件去实现
// 好处是我用在Vue这类单页应用的时候,组件销毁时不用去解绑 window 的 scroll 事件
// 但是滑动到底部事件就必须要用 window 的 scroll 事件,这点需要注意
/**
* 下拉刷新组件
* @param {object} option 配置
* @param {HTMLElement} option.el 下拉元素(必选)
* @param {number} option.distance 下拉距离[px](可选)
* @param {number} option.deviation 顶部往下偏移量[px](可选)
* @param {string} option.loadIcon 下拉中的 icon html(可选)
*/
function dropDownRefresh(option) {
const doc = document;
/** 整体节点 */
const page = option.el;
/** 下拉距离 */
const distance = option.distance || 88;
/** 顶部往下偏移量 */
const deviation = option.deviation || 0;
/** 顶层节点 */
const topNode = doc.createElement("div");
/** 下拉时遮罩 */
const maskNode = doc.createElement("div");
topNode.innerHTML = `<div refresh-icon style="transition: .2s all;"><svg style="transform: rotate(90deg); display: block;" t="1570593064555" viewBox="0 0 1575 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="26089" width="48" height="48"><path d="M1013.76 0v339.968H484.115692V679.778462h529.644308v339.968l529.644308-485.612308v-48.600616L1013.76 0zM243.396923 679.857231h144.462769V339.968H243.396923V679.778462z m-240.797538 0h144.462769V339.968H2.599385V679.778462z" fill="#000000" fill-opacity=".203" p-id="26090"></path></svg></div><div refresh-loading style="display: none; animation: refresh-loading 1s linear infinite;">${option.loadIcon || '<p style="font-size: 15px; color: #666;">loading...</p>'}</div>`;
topNode.style.cssText = `width: 100%; height: ${distance}px; position: fixed; top: ${-distance + deviation}px; left: 0; z-index: 10; display: flex; flex-wrap: wrap; align-items: center; justify-content: center; box-sizing: border-box; margin: 0; padding: 0;`;
maskNode.style.cssText = "position: fixed; top: 0; left: 0; width: 100%; height: 100vh; box-sizing: border-box; margin: 0; padding: 0; background-color: rgba(0,0,0,0); z-index: 999;";
page.parentNode.insertBefore(topNode, page);
/**
* 设置动画时间
* @param {number} n 秒数
*/
function setAnimation(n) {
page.style.transition = topNode.style.transition = n + "s all";
}
/**
* 设置滑动距离
* @param {number} n 滑动的距离(像素)
*/
function setSlide(n) {
page.style.transform = topNode.style.transform = `translate3d(0px, ${n}px, 0px)`;
}
/** 下拉提示 icon */
const icon = topNode.querySelector("[refresh-icon]");
/** 下拉 loading 动画 */
const loading = topNode.querySelector("[refresh-loading]");
return {
/**
* 监听开始刷新
* @param {Function} callback 下拉结束回调
* @param {(n: number) => void} rangeCallback 下拉状态回调
*/
onRefresh(callback, rangeCallback = null) {
/** 顶部距离 */
let scrollTop = 0;
/** 开始距离 */
let startDistance = 0;
/** 结束距离 */
let endDistance = 0;
/** 最后移动的距离 */
let range = 0;
// 触摸开始
page.addEventListener("touchstart", function (e) {
startDistance = e.touches[0].pageY;
scrollTop = 1;
setAnimation(0);
});
// 触摸移动
page.addEventListener("touchmove", function (e) {
scrollTop = doc.documentElement.scrollTop === 0 ? doc.body.scrollTop : doc.documentElement.scrollTop;
// 没到达顶部就停止
if (scrollTop != 0) return;
endDistance = e.touches[0].pageY;
range = Math.floor(endDistance - startDistance);
// 判断如果是下滑才执行
if (range > 0) {
// 阻止浏览自带的下拉效果
e.preventDefault();
// 物理回弹公式计算距离
range = range - (range * 0.5);
// 下拉时icon旋转
if (range > distance) {
icon.style.transform = "rotate(180deg)";
} else {
icon.style.transform = "rotate(0deg)";
}
setSlide(range);
// 回调距离函数 如果有需要
if (typeof rangeCallback === "function") rangeCallback(range);
}
});
// 触摸结束
page.addEventListener("touchend", function () {
setAnimation(0.3);
// console.log(`移动的距离:${range}, 最大距离:${distance}`);
if (range > distance && range > 1 && scrollTop === 0) {
setSlide(distance);
doc.body.appendChild(maskNode);
// 阻止往上滑动
maskNode.ontouchmove = e => e.preventDefault();
// 回调成功下拉到最大距离并松开函数
if (typeof callback === "function") callback();
icon.style.display = "none";
loading.style.display = "block";
} else {
setSlide(0);
}
});
},
/** 结束下拉 */
end() {
maskNode.parentNode.removeChild(maskNode);
setAnimation(0.3);
setSlide(0);
icon.style.display = "block";
loading.style.display = "none";
}
}
}
Адрес исходного кода и отображение использования
6. Прокрутка монитора вниз
Метод всего из нескольких строк кода, кроме того, элемент мониторинга прокручивается вниз для справкипримечания к коду
/**
* 监听滚动到底部
* @param {object} options 传参对象
* @param {number} options.distance 距离底部多少像素触发(px)
* @param {boolean} options.once 是否为一次性(防止重复用)
* @param {() => void} options.callback 到达底部回调函数
*/
function onScrollToBottom(options) {
const { distance = 0, once = false, callback = null } = options;
const doc = document;
/** 滚动事件 */
function onScroll() {
/** 滚动的高度 */
let scrollTop = doc.documentElement.scrollTop === 0 ? doc.body.scrollTop : doc.documentElement.scrollTop;
/** 滚动条高度 */
let scrollHeight = doc.documentElement.scrollTop === 0 ? doc.body.scrollHeight : doc.documentElement.scrollHeight;
if (scrollHeight - scrollTop - distance <= window.innerHeight) {
if (typeof callback === "function") callback();
if (once) window.removeEventListener("scroll", onScroll);
}
}
window.addEventListener("scroll", onScroll);
// 必要时先执行一次
// onScroll();
}
Адрес исходного кода и отображение использования
7. Компонент воспроизведения звука
Здесь нужно объяснить сценарий применения: я сделалH5
Возникла проблема на странице активности (дождь из красных конвертов), то есть когда мобильный терминал быстро нажимает на узел и воспроизводит звук,aduio
Скорость воспроизведения вкладки будет сильно задерживаться. Позже я искал соответствующую информацию и нашел аудио API:new AudioContext
, и звук движка (создатель кокосов), который я использовал, когда делал мини-игру доAPI
это то же самое. После долгих поисков я нашел этоAPI
Очень мало материалов по использованию и руководствH5
Игровой движок люди будут использовать на нем, более подробно только на официальном сайте MDN, остальные основаны на этомAPI
изJavaScript库
, но функция, которую мне нужно использовать, относительно проста, то есть нажмите, чтобы воспроизвести без промедления. Таким образом, чтобы реализовать один на основеnew AudioContext
Общие аудиокомпоненты.
/**
* `AudioContext`音频组件
* [资料参考](https://www.cnblogs.com/Wayou/p/html5_audio_api_visualizer.html)
* @description 解决在移动端网页上标签播放音频延迟的方案 貌似`H5`游戏引擎也是使用这个实现
*/
function audioComponent() {
/**
* 音频上下文
* @type {AudioContext}
*/
const context = new (window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext)();
/**
* @type {AnalyserNode}
*/
const analyser = context.createAnalyser();;
/**
* @type {AudioBufferSourceNode}
*/
let bufferNode = null;
/**
* @type {AudioBuffer}
*/
let buffer = null;
/** 是否加载完成 */
let loaded = false;
analyser.fftSize = 256;
return {
/**
* 加载路径音频文件
* @param {string} url 音频路径
* @param {(res: AnalyserNode) => void} callback 加载完成回调
*/
loadPath(url, callback) {
const XHR = new XMLHttpRequest();
XHR.open("GET", url, true);
XHR.responseType = "arraybuffer";
// 先加载音频文件
XHR.onload = () => {
context.decodeAudioData(XHR.response, audioBuffer => {
// 最后缓存音频资源
buffer = audioBuffer;
loaded = true;
typeof callback === "function" && callback(analyser);
});
}
XHR.send(null);
},
/**
* 加载 input 音频文件
* @param {File} file 音频文件
* @param {(res: AnalyserNode) => void} callback 加载完成回调
*/
loadFile(file, callback) {
const FR = new FileReader();
// 先加载音频文件
FR.onload = e => {
const res = e.target.result;
// 然后解码
context.decodeAudioData(res, audioBuffer => {
// 最后缓存音频资源
buffer = audioBuffer;
loaded = true;
typeof callback === "function" && callback(analyser);
});
}
FR.readAsArrayBuffer(file);
},
/** 播放音频 */
play() {
if (!loaded) return console.warn("音频未加载完成 !!!");
// 这里有个问题,就是创建的音频对象不能缓存下来然后多次执行 start , 所以每次都要创建然后 start()
bufferNode = context.createBufferSource();
bufferNode.connect(analyser);
analyser.connect(context.destination);
bufferNode.buffer = buffer;
bufferNode.start(0);
},
/** 停止播放 */
stop() {
if (!bufferNode) return console.warn("音频未播放 !!!");
bufferNode.stop();
}
}
}
Адрес исходного кода и отображение использования
8. Глобальный мониторинг ошибок изображений и замена изображениями по умолчанию.
window.addEventListener("error", e => {
/** 默认`base64`图片 */
const defaultImg = 'data:image/png;base64,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';
/**
* @type {HTMLImageElement}
*/
const node = e.target;
if (node.nodeName && node.nodeName.toLocaleLowerCase() === "img") {
node.style.objectFit = "cover";
node.src = defaultImg;
}
}, true);
9. Функция копирования
я листаюClipboard.js
Основной код находится, когда исходный код этой подключаемой библиотекиsetSelectionRange(start: number, end: number)
, во всех функциях копирования, найденных на Baidu, отсутствует эта операция, поэтому найденный текстовый код копирования находится вios
а такжеIE
Его нельзя скопировать в некоторых браузерах.
/**
* 复制文本
* @param {string} text 复制的内容
* @param {() => void} success 成功回调
* @param {(tip: string) => void} fail 出错回调
*/
function copyText(text, success = null, fail = null) {
text = text.replace(/(^\s*)|(\s*$)/g, "");
if (!text) {
typeof fail === "function" && fail("复制的内容不能为空!");
return;
}
const id = "the-clipboard";
/**
* 粘贴板节点
* @type {HTMLTextAreaElement}
*/
let clipboard = document.getElementById(id);
if (!clipboard) {
clipboard = document.createElement("textarea");
clipboard.id = id;
clipboard.readOnly = true
clipboard.style.cssText = "font-size: 15px; position: fixed; top: -1000%; left: -1000%;";
document.body.appendChild(clipboard);
}
clipboard.value = text;
clipboard.select();
clipboard.setSelectionRange(0, text.length);
const state = document.execCommand("copy");
if (state) {
typeof success === "function" && success();
} else {
typeof fail === "function" && fail("复制失败");
}
}
10. Тип обнаружения
Все типы могут быть обнаружены
/**
* 检测类型
* @param {any} target 检测的目标
* @returns {"string"|"number"|"array"|"object"|"function"|"null"|"undefined"|"regexp"} 只枚举一些常用的类型
*/
function checkType(target) {
/** @type {string} */
const value = Object.prototype.toString.call(target);
const result = value.match(/\[object (\S*)\]/)[1];
return result.toLocaleLowerCase();
}
11. Отформатируйте дату (очень минимальный код)
/**
* 获取指定日期时间戳
* @param {number} time 毫秒数
*/
function getDateFormat(time = Date.now()) {
const date = new Date(time);
return `${date.toLocaleDateString()} ${date.toTimeString().slice(0, 8)}`;
}
12. Расчет десятичной точности JavaScript
/**
* 数字运算(主要用于小数点精度问题)
* @param {number} a 前面的值
* @param {"+"|"-"|"*"|"/"} type 计算方式
* @param {number} b 后面的值
* @example
* ```js
* // 可链式调用
* const res = computeNumber(1.3, "-", 1.2).next("+", 1.5).next("*", 2.3).next("/", 0.2).result;
* console.log(res);
* ```
*/
function computeNumber(a, type, b) {
/**
* 获取数字小数点的长度
* @param {number} n 数字
*/
function getDecimalLength(n) {
const decimal = n.toString().split(".")[1];
return decimal ? decimal.length : 0;
}
/**
* 修正小数点
* @description 防止出现 `33.33333*100000 = 3333332.9999999995` && `33.33*10 = 333.29999999999995` 这类情况做的处理
* @param {number} n
*/
const amend = (n, precision = 15) => parseFloat(Number(n).toPrecision(precision));
const power = Math.pow(10, Math.max(getDecimalLength(a), getDecimalLength(b)));
let result = 0;
a = amend(a * power);
b = amend(b * power);
switch (type) {
case "+":
result = (a + b) / power;
break;
case "-":
result = (a - b) / power;
break;
case "*":
result = (a * b) / (power * power);
break;
case "/":
result = a / b;
break;
}
result = amend(result);
return {
/** 计算结果 */
result,
/**
* 继续计算
* @param {"+"|"-"|"*"|"/"} nextType 继续计算方式
* @param {number} nextValue 继续计算的值
*/
next(nextType, nextValue) {
return computeNumber(result, nextType, nextValue);
}
};
}
13. Одна линияcss
приспособлениеrem
750 - ширина проектного чертежа: единица после этого непосредственно1:1
Используйте размер эскизного проекта, единица измеренияrem
html{ font-size: calc(100vw / 750); }
14. Простые в использовании методы форматирования дат
/**
* 格式化日期
* @param {string | number | Date} value 指定日期
* @param {string} format 格式化的规则
* @example
* ```js
* formatDate();
* formatDate(1603264465956);
* formatDate(1603264465956, "h:m:s");
* formatDate(1603264465956, "Y年M月D日");
* ```
*/
function formatDate(value = Date.now(), format = "Y-M-D h:m:s") {
const formatNumber = n => `0${n}`.slice(-2);
const date = new Date(value);
const formatList = ["Y", "M", "D", "h", "m", "s"];
const resultList = [];
resultList.push(date.getFullYear().toString());
resultList.push(formatNumber(date.getMonth() + 1));
resultList.push(formatNumber(date.getDate()));
resultList.push(formatNumber(date.getHours()));
resultList.push(formatNumber(date.getMinutes()));
resultList.push(formatNumber(date.getSeconds()));
for (let i = 0; i < resultList.length; i++) {
format = format.replace(formatList[i], resultList[i]);
}
return format;
}
15. Таргетинг на веб-страницу
Здесь используется позиционирование Baidu независимо от инкапсуляции кода, метода вызова или точности определения местоположения.微信sdk
Его так легко использовать, в том числе на любой веб-странице;
/**
* 插入脚本
* @param {string} link 脚本路径
* @param {Function} callback 脚本加载完成回调
*/
function insertScript(link, callback) {
const label = document.createElement("script");
label.src = link;
label.onload = function () {
if (label.parentNode) label.parentNode.removeChild(label);
if (typeof callback === "function") callback();
}
document.body.appendChild(label);
}
/**
* 获取定位信息
* @returns {Promise<{ city: string, districtName: string, province: string, longitude: number, latitude: number }>}
*/
function getLocationInfo() {
/**
* 使用百度定位
* @param {(value: any) => void} callback
*/
function useBaiduLocation(callback) {
const geolocation = new BMap.Geolocation({
maximumAge: 10
})
geolocation.getCurrentPosition(function(res) {
console.log("%c 使用百度定位 >>", "background-color: #4e6ef2; padding: 2px 6px; color: #fff; border-radius: 2px", res);
callback({
city: res.address.city,
districtName: res.address.district,
province: res.address.province,
longitude: Number(res.longitude),
latitude: Number(res.latitude)
})
})
}
return new Promise(function (resolve, reject) {
if (!window._baiduLocation) {
window._baiduLocation = function () {
useBaiduLocation(resolve);
}
// ak=你自己的key
insertScript("https://api.map.baidu.com/api?v=2.0&ak=66vCKv7PtNlOprFEe9kneTHEHl8DY1mR&callback=_baiduLocation");
} else {
useBaiduLocation(resolve);
}
})
}
16. Введите зарезервированные номера<input type="text">
Сценарий использования: когда пользователь вводит содержимое в поле ввода, фильтрация в реальном времени сохраняет отображаемое цифровое значение;
tips
: установить в Firefox<input type="number">
будут ошибки стиля
/**
* 输入只能是数字
* @param {string | number} value 输入的值
* @param {boolean} decimal 是否要保留小数
* @param {boolean} negative 是否可以为负数
*/
function inputOnlyNumber(value, decimal, negative) {
let result = value.toString().trim();
if (result.length === 0) return "";
const minus = (negative && result[0] == "-") ? "-" : "";
if (decimal) {
result = result.replace(/[^0-9.]+/ig, "");
let array = result.split(".");
if (array.length > 1) {
result = array[0] + "." + array[1];
}
} else {
result = result.replace(/[^0-9]+/ig, "");
}
return minus + result;
}
END
Выше приведены некоторые часто используемые функции, которыми можно поделиться, и они будут обновлены в будущем.Есть также некоторые другие функции, которые я считаю не важными, поэтому я не буду их публиковать.Если вам интересно, вы можете взглянуть.Адрес складаЕсли полезно, поставьте звезду