import Swiper from './swiper-bundle.esm.browser.min.js'; import { CountUp } from './countUp.min.js'; /** 屏幕宽度 */ export const WINDTH = innerWidth; /** 屏幕高度 */ export const HEIGHT = innerHeight; export function onEnter(inputEl, callback) { $(inputEl).focus((e) => { $(document).keyup((e) => { if (e.keyCode == 13) { const value = $(inputEl).val(); callback(value); } }); }); $(inputEl).focusout(() => { $(document).off('keyup'); }); } export function gsapAni() { if (!window.gsap) return; gsap.registerPlugin(ScrollTrigger); let bottom = 100; if (WINDTH < 980) { bottom = 20; } ScrollTrigger.batch('[data-scroll]', { toggleClass: 'inview', start: `top bottom-=${bottom}px`, end: `bottom top+=${bottom}px`, }); ScrollTrigger.batch('[data-inview]', { toggleClass: 'inview', }); const ro = new ResizeObserver((entries) => { ScrollTrigger.refresh(); }); // 观察一个或多个元素 ro.observe(document.querySelector('body')); } /** * 吸顶 * @param {String} boxEl 容器css选择器 * @param {String} El 吸顶元素css选择器 */ export function sticky(boxEl, El, TOP = 0) { const box = $(boxEl); const el = $(El); if (box.length === 0) return; const boxH = box.outerHeight(true); const elH = el.outerHeight(true); const start = box.offset().top - TOP; const end = boxH - elH + start; box.css('position', 'relative'); handleScroll({ scroll(st) { if (st >= start) { el.css({ position: 'fixed', top: `${TOP}px`, }); } else { el.css('position', 'static'); } if (st >= end) { el.css({ position: 'absolute', top: `${end - start}px`, }); } }, }); } export function inBox() { const box = document.querySelectorAll('.inbox'); box.forEach((el) => { const text = el.textContent.split('\n'); const dom = text.map((e) => { return `
${e}
`.trim(); }); el.innerHTML = dom.join(''); }); } export function fadeOut(el) { el.style.opacity = 1; (function fade() { if ((el.style.opacity -= 0.03) < 0) { el.style.display = 'none'; } else { requestAnimationFrame(fade); } })(); } export function fadeIn(el, display) { el.style.opacity = 0; el.style.display = display || 'block'; (function fade() { var val = parseFloat(el.style.opacity); if (!((val += 0.04) > 1)) { el.style.opacity = val; requestAnimationFrame(fade); } })(); } /** * 点击目标非指定元素及其子元素时触发回调 * @param {JqueryEl} node 指定元素 * @param {Function} callback 回调 */ export function awayClick(node, callback) { $(document).on('click', (e) => { const lg = node.parent().find(e.target).length; if (lg === 0) { callback && (typeof callback == 'function' ? callback() : console.error('callback 必须是 function')); } }); } /** * countUp * @param {string} className 需要countUp的元素类名 默认 .count */ export function countUp(className = '.count') { const els = document.querySelectorAll(className); els.forEach((el) => { const number = Number(el.textContent); new CountUp(el, number, { enableScrollSpy: true, separator: '', }); }); } /** * 文件下载 * 查找a[download],重写下载逻辑,避免图片在新标签页打开 */ export function resetDownload() { const body = document.querySelector('body'); window.resetDownloadMethod = function (e) { e.preventDefault(); const url = this.href; const split = url.split('/'); const name = split[split.length - 1]; const x = new XMLHttpRequest(); x.open('GET', url, true); x.responseType = 'blob'; x.onload = function (e) { const url = window.URL.createObjectURL(x.response); const a = document.createElement('a'); a.href = url; a.download = decodeURI(name); a.click(); }; x.send(); }; const reset = () => { const files = document.querySelectorAll('a[download]'); files.forEach((el) => { el.removeEventListener('click', window.resetDownloadMethod); el.addEventListener('click', window.resetDownloadMethod); }); }; reset(); const observer = new MutationObserver(reset); observer.observe(body, { childList: true, subtree: true, }); } /** * 控制元素显示隐藏 * @param {String} el 被控元素 class */ export function changeDisplay(el) { if ($(el).css('display') == 'none') { $(el).fadeIn(); } else { $(el).hide(); } } /** * 复制链接 */ export function copyLink() { const el = $('.copylink'); el.click(() => { const link = window.location.href; const res = copyToClipboard(link); res .then(() => { alert('已成功将链接复制到剪切板'); }) .catch(() => { alert('链接复制失败, 请手动复制链接: ', link); }); }); } /** * 移动端横向菜单选中项定位 */ export function scrollX() { const box = $('.nav, [sx], .crumbs'); const active = box.find('.active,.on'); const w = document.body.offsetWidth; if (w < 980) { if (box.length === 0) return; box.scrollLeft(active.position().left - box.position().left - w * 0.1); } } /** * @typedef swiperOptions * @prop {swiperOptions} mobile 移动端设置 * @prop {{ * [key: number]: swiperOptions * }} breakpoints 断点 * @prop {number | 'auto'} slidesPerView 展示个数 * @prop {number} spaceBetween 间距 * @prop {'horizontal'|'vertical'} 水平或垂直 * @prop {'slide'|'fade'} effect * @prop {number} speed * @prop {boolean} loop * @prop {boolean | { * disableOnInteraction: boolean * }} autoplay * @prop {number} delay * @prop {boolean} autovideoplay * @prop {boolean} observer * @prop {{ * nextEl: string * prevEl: string * }} navigation * @prop {{ * init: (swiper) => void * }} on */ /** * 设置swiper * @param {String} boxEl swiper容器 * @param {swiperOptions} opts 选项 */ export function setSwiper(boxEl, opts = {}) { const box = document.querySelector(boxEl); if (box === null) return null; const { length } = [...box.children].filter((e) => { if (e.classList.contains('swiper-wrapper')) { return true; } }); if (length == 0) return null; let options = { direction: 'horizontal', speed: 1500, loop: false, autoplay: false, delay: 4000, noSwiping: true, noSwipingClass: 'no-swiping', slideActiveClass: 'active', normalizeSlideIndex: false, }; if (WINDTH < 980) { options = Object.assign(options, opts.mobile); } if (opts.effect === 'fade') { options = { ...options, fadeEffect: { crossFade: true, }, }; } if (opts.autoplay) { opts.autoplay = Object.assign( { delay: opts.delay || options.delay, }, opts.autoplay ); } if (opts.observer) { opts = { ...opts, observer: true, observeParents: true, observeSlideChildren: true, }; } let final = Object.assign(options, opts); if (WINDTH < 980) { final = Object.assign(options, { ...opts, ...opts.mobile }); } const swiper = new Swiper(boxEl, final); if (opts.autovideoplay) { let timer = null; function next() { swiperNext(swiper); } function videoslidechange() { const videos = swiper.el.querySelectorAll('video'); const video = () => swiper.slides[swiper.realIndex]?.querySelector('video'); clearTimeout(timer); if (video()) { video().play(); video().removeEventListener('ended', next); video().addEventListener('ended', next); } else { videos.forEach((e) => { e.pause(); e.currentTime = 0; }); timer = setTimeout(() => { swiperNext(swiper); }, options.delay || 4000); } } videoslidechange(); swiper.on('slideChange', videoslidechange); } return swiper; } export function swiperNext(swiper) { const index = swiper.realIndex; if (index === swiper.slides.length - 1) { swiper.slideTo(0); } else { swiper.slideNext(); } } export function swiperPrev(swiper) { const index = swiper.realIndex; if (index === 0) { swiper.slideTo(swiper.slides.length - 1); } else { swiper.slideTo(index - 1); } } /** * 禁止图片拖拽 */ export function imgDraggable() { const imgs = $('img'); imgs.attr('draggable', false).css('user-select', 'none'); } /** * 监听大小变化 * @param {css} el 需要监听的元素 css 选择器 * @param {function} callback mutation => */ export function onResizeObserver(el, callback) { const ro = new ResizeObserver((mutation) => { mutation.forEach((e) => callback(e)); }); ro.observe(document.querySelector(el)); } /** * 监听dom的类名和样式变化 * @param {Node} node 需要监听的节点 * @param {function} callback mutation => */ export function onAttrObserver(node, callback) { const AttrObserver = new MutationObserver((mutationsList) => { mutationsList.forEach(callback); }); AttrObserver.observe(node, { attributes: true, attributeOldValue: true, attributeFilter: ['class', 'style'], }); } /** * 处理弹窗 * @param {ClassName} cls 元素类名 * @returns {open, close} */ export function handlePop(cls) { const pop = $(cls); const close = () => pop.fadeOut(); const open = () => pop.fadeIn(); pop.click((e) => { const target = $(e.target); if (target.closest('.con').length === 0) { close(); } }); return { open, close }; } /** * 处理滚动 * @param {Object} funcs {scroll: func, up: func, down: func} */ export function handleScroll(funcs) { let position = $(window).scrollTop(); $(window).on( 'scroll', throttle( () => { const st = $(window).scrollTop(); typeof funcs.scroll === 'function' && funcs.scroll(st); if (st > position) { typeof funcs.down === 'function' && funcs.down(st); } else { typeof funcs.up === 'function' && funcs.up(st); } position = st; }, 50, 1 ) ); } /** * 返回顶部 * @param {cssSelect} $el 能通过css选择器查找到的点击元素 [to-top] */ export function toTop($el) { $el = $el || '[to-top]'; const el = $($el); el.on('click', () => { $('html,body').stop().animate({ scrollTop: 0 }, 600); }); const hide = () => el.fadeOut(); let timer = null; const show = (flex = false) => { clearTimeout(timer); if (el.css('display') === 'none') { if (flex) { el.css('display', 'flex').hide().fadeIn(); } else { el.fadeIn(); } } timer = setTimeout(() => { hide(); }, 2000); }; return { show, hide, el, }; } /** * 防抖 * @param func 函数 * @param wait 延迟执行毫秒数 * @param immediate true 表立即执行,false 表非立即执行 */ export function debounce(func, wait, immediate) { let timeout; return function () { let context = this; let args = arguments; if (timeout) clearTimeout(timeout); if (immediate) { let callNow = !timeout; timeout = setTimeout(() => { timeout = null; }, wait); if (callNow) func.apply(context, args); } else { timeout = setTimeout(() => { func.apply(context, args); }, wait); } }; } /** * 节流 * @param func 函数 * @param wait 延迟执行毫秒数 * @param type 1 表时间戳版,2 表定时器版 */ export function throttle(func, wait, type) { let previous; if (type === 1) { previous = 0; } else if (type === 2) { let timeout; } return function () { let context = this; let args = arguments; if (type === 1) { let now = Date.now(); if (now - previous > wait) { func.apply(context, args); previous = now; } } else if (type === 2) { if (!timeout) { timeout = setTimeout(() => { timeout = null; func.apply(context, args); }, wait); } } }; }