补充主题静态资源与归档模板
This commit is contained in:
1 parent
1268a55dd0
commit
3247db7ed1
265 files changed
+59174
No files matched your search
@@ -0,0 +1,256 @@
|
||||
/**
|
||||
* AKSRT Mini Touch — General Direction Gesture Engine
|
||||
*
|
||||
* Full direction gesture (top/bottom/left/right) via atan2 angle filtering,
|
||||
* real-time translate3d follow + cursor, onStart/onIng/onEnd callbacks,
|
||||
* compatible with swiper/scroll-x containers.
|
||||
*
|
||||
* Supports:
|
||||
* - [data-aksrt-swipe] left/right swipe triggers
|
||||
* - [data-aksrt-pull-dismiss] pull-to-dismiss with opacity tracking
|
||||
* - [data-mini-touch="mobile-nav"] mobile menu right-swipe close
|
||||
* - aksrt.minitouch(el, opts) programmatic API (from aksrt-runtime.js)
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var MIN_SWIPE_DISTANCE = 40;
|
||||
var MAX_SWIPE_TIME = 300;
|
||||
|
||||
function getDirection(angle) {
|
||||
if (angle >= -45 && angle < 45) return 'right';
|
||||
if (angle >= 45 && angle < 135) return 'bottom';
|
||||
if (angle >= -135 && angle < -45) return 'top';
|
||||
return 'left';
|
||||
}
|
||||
|
||||
// ── Declarative swipe: [data-aksrt-swipe] ──
|
||||
|
||||
function bindSwipe(container) {
|
||||
if (container.dataset.aksrtSwipeBound) return;
|
||||
container.dataset.aksrtSwipeBound = '1';
|
||||
|
||||
var startX = 0, startY = 0, startTime = 0;
|
||||
var moving = false;
|
||||
|
||||
container.addEventListener('touchstart', function (e) {
|
||||
if (e.touches.length !== 1) return;
|
||||
startX = e.touches[0].clientX;
|
||||
startY = e.touches[0].clientY;
|
||||
startTime = Date.now();
|
||||
moving = true;
|
||||
}, { passive: true });
|
||||
|
||||
container.addEventListener('touchmove', function (e) {
|
||||
if (!moving) return;
|
||||
var dx = e.touches[0].clientX - startX;
|
||||
var dy = e.touches[0].clientY - startY;
|
||||
if (Math.abs(dy) > Math.abs(dx) * 1.5) moving = false;
|
||||
}, { passive: true });
|
||||
|
||||
container.addEventListener('touchend', function (e) {
|
||||
if (!moving) return;
|
||||
moving = false;
|
||||
var ct = e.changedTouches ? e.changedTouches[0] : e;
|
||||
var dx = ct.clientX - startX;
|
||||
var dt = Date.now() - startTime;
|
||||
|
||||
if (dt > MAX_SWIPE_TIME || Math.abs(dx) < MIN_SWIPE_DISTANCE) return;
|
||||
if (Math.abs(ct.clientY - startY) > Math.abs(dx)) return;
|
||||
|
||||
var direction = dx > 0 ? 'right' : 'left';
|
||||
var selector = container.getAttribute('data-aksrt-swipe-' + direction);
|
||||
if (selector) {
|
||||
var target = container.querySelector(selector) || document.querySelector(selector);
|
||||
if (target) { target.click(); return; }
|
||||
}
|
||||
|
||||
container.dispatchEvent(new CustomEvent('aksrt:swipe', {
|
||||
detail: { direction: direction, distance: Math.abs(dx) },
|
||||
bubbles: true
|
||||
}));
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
// ── Pull to dismiss with real-time opacity tracking ──
|
||||
|
||||
function bindPullDismiss(el) {
|
||||
if (el.dataset.aksrtPullBound) return;
|
||||
el.dataset.aksrtPullBound = '1';
|
||||
|
||||
var startY = 0, currentY = 0, pulling = false;
|
||||
|
||||
el.addEventListener('touchstart', function (e) {
|
||||
if (e.touches.length !== 1) return;
|
||||
// Disable pull when zoomed (scale > 1)
|
||||
var img = el.querySelector('img');
|
||||
if (img) {
|
||||
var transform = getComputedStyle(img).transform;
|
||||
if (transform && transform !== 'none') {
|
||||
var parts = transform.match(/matrix\(([^)]+)\)/);
|
||||
if (parts) {
|
||||
var vals = parts[1].split(',').map(Number);
|
||||
var scaleX = Math.sqrt(vals[0] * vals[0] + vals[1] * vals[1]);
|
||||
if (scaleX > 1.05) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (el.scrollTop > 0) return;
|
||||
startY = e.touches[0].clientY;
|
||||
currentY = startY;
|
||||
pulling = true;
|
||||
}, { passive: true });
|
||||
|
||||
el.addEventListener('touchmove', function (e) {
|
||||
if (!pulling) return;
|
||||
currentY = e.touches[0].clientY;
|
||||
var dy = currentY - startY;
|
||||
if (dy > 0) {
|
||||
var opacity = Math.max(0.2, (150 - dy) / 80);
|
||||
el.style.transform = 'translate3d(0, ' + (dy * 0.5) + 'px, 0)';
|
||||
el.style.opacity = String(Math.min(1, opacity));
|
||||
}
|
||||
}, { passive: true });
|
||||
|
||||
el.addEventListener('touchend', function () {
|
||||
if (!pulling) return;
|
||||
pulling = false;
|
||||
var dy = currentY - startY;
|
||||
|
||||
if (dy > 120) {
|
||||
el.style.transform = 'translate3d(0, ' + (dy + 100) + 'px, 0)';
|
||||
el.style.opacity = '0';
|
||||
el.style.transition = 'transform 0.2s ease-out, opacity 0.2s ease-out';
|
||||
setTimeout(function () {
|
||||
el.dispatchEvent(new CustomEvent('aksrt:pull-dismiss', { bubbles: true }));
|
||||
el.style.transform = '';
|
||||
el.style.opacity = '';
|
||||
el.style.transition = '';
|
||||
}, 200);
|
||||
} else {
|
||||
el.style.transition = 'transform 0.2s ease-out, opacity 0.2s ease-out';
|
||||
el.style.transform = '';
|
||||
el.style.opacity = '';
|
||||
setTimeout(function () { el.style.transition = ''; }, 200);
|
||||
}
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
// ── [data-mini-touch="..."] declarative gesture ──
|
||||
|
||||
function bindMiniTouch(el) {
|
||||
if (el.dataset.miniTouchBound) return;
|
||||
el.dataset.miniTouchBound = '1';
|
||||
|
||||
var type = el.getAttribute('data-mini-touch');
|
||||
|
||||
if (type === 'mobile-nav') {
|
||||
bindMobileNavSwipe(el);
|
||||
} else if (type === 'bottom-close') {
|
||||
bindBottomClose(el);
|
||||
}
|
||||
}
|
||||
|
||||
function bindMobileNavSwipe(el) {
|
||||
var startX = 0, currentX = 0, active = false;
|
||||
|
||||
el.addEventListener('touchstart', function (e) {
|
||||
if (e.touches.length !== 1) return;
|
||||
startX = e.touches[0].clientX;
|
||||
currentX = startX;
|
||||
active = true;
|
||||
}, { passive: true });
|
||||
|
||||
el.addEventListener('touchmove', function (e) {
|
||||
if (!active) return;
|
||||
currentX = e.touches[0].clientX;
|
||||
var dx = currentX - startX;
|
||||
if (dx > 0) {
|
||||
el.style.transform = 'translateX(' + dx + 'px)';
|
||||
el.style.opacity = String(Math.max(0.3, 1 - dx / 300));
|
||||
}
|
||||
}, { passive: true });
|
||||
|
||||
el.addEventListener('touchend', function () {
|
||||
if (!active) return;
|
||||
active = false;
|
||||
var dx = currentX - startX;
|
||||
if (dx > 80) {
|
||||
el.style.transform = 'translateX(100%)';
|
||||
el.style.opacity = '0';
|
||||
setTimeout(function () {
|
||||
el.classList.remove('is-open');
|
||||
el.style.transform = '';
|
||||
el.style.opacity = '';
|
||||
document.body.classList.remove('nav-open');
|
||||
}, 200);
|
||||
} else {
|
||||
el.style.transition = 'transform 0.2s, opacity 0.2s';
|
||||
el.style.transform = '';
|
||||
el.style.opacity = '';
|
||||
setTimeout(function () { el.style.transition = ''; }, 200);
|
||||
}
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
function bindBottomClose(el) {
|
||||
var startY = 0, currentY = 0, active = false;
|
||||
|
||||
el.addEventListener('touchstart', function (e) {
|
||||
if (e.touches.length !== 1) return;
|
||||
startY = e.touches[0].clientY;
|
||||
currentY = startY;
|
||||
active = true;
|
||||
}, { passive: true });
|
||||
|
||||
el.addEventListener('touchmove', function (e) {
|
||||
if (!active) return;
|
||||
currentY = e.touches[0].clientY;
|
||||
var dy = currentY - startY;
|
||||
if (dy > 0) {
|
||||
el.style.transform = 'translateY(' + dy + 'px)';
|
||||
}
|
||||
}, { passive: true });
|
||||
|
||||
el.addEventListener('touchend', function () {
|
||||
if (!active) return;
|
||||
active = false;
|
||||
var dy = currentY - startY;
|
||||
if (dy > 80) {
|
||||
el.dispatchEvent(new CustomEvent('aksrt:pull-dismiss', { bubbles: true }));
|
||||
}
|
||||
el.style.transform = '';
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
// ── Auto-init ──
|
||||
|
||||
function init() {
|
||||
document.querySelectorAll('[data-aksrt-swipe]').forEach(bindSwipe);
|
||||
document.querySelectorAll('[data-aksrt-pull-dismiss]').forEach(bindPullDismiss);
|
||||
document.querySelectorAll('[data-mini-touch]').forEach(bindMiniTouch);
|
||||
|
||||
document.addEventListener('aksrt:lightbox-open', function (e) {
|
||||
var lightbox = e.target;
|
||||
if (lightbox) bindPullDismiss(lightbox);
|
||||
});
|
||||
}
|
||||
|
||||
// Register for rehydration
|
||||
if (window.AksrtFrontend) {
|
||||
AksrtFrontend.register('aksrt-mini-touch', function (root) {
|
||||
(root || document).querySelectorAll('[data-aksrt-swipe]').forEach(bindSwipe);
|
||||
(root || document).querySelectorAll('[data-aksrt-pull-dismiss]').forEach(bindPullDismiss);
|
||||
(root || document).querySelectorAll('[data-mini-touch]').forEach(bindMiniTouch);
|
||||
});
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
})();
|
||||
Reference in new issue
Block a user