Add Navigation Arrow and simplify static page
This commit is contained in:
+186
-35
@@ -7,13 +7,31 @@
|
||||
position: null,
|
||||
triggered: new Set(),
|
||||
tracking: false,
|
||||
activePage: null
|
||||
activePage: null,
|
||||
routeProgressIndex: null,
|
||||
deviceHeading: null
|
||||
};
|
||||
|
||||
function escapeHtml(value) {
|
||||
return $('<div>').text(value ?? '').html();
|
||||
}
|
||||
|
||||
function activate(selector, handler) {
|
||||
$(document)
|
||||
.on('pointerup', selector, function (event) {
|
||||
const pointer = event.originalEvent;
|
||||
if (pointer && pointer.isPrimary === false) return;
|
||||
if (pointer?.pointerType === 'mouse' && pointer.button !== 0) return;
|
||||
event.preventDefault();
|
||||
handler.call(this, event);
|
||||
})
|
||||
.on('keydown', selector, function (event) {
|
||||
if (event.key !== 'Enter' && event.key !== ' ') return;
|
||||
event.preventDefault();
|
||||
handler.call(this, event);
|
||||
});
|
||||
}
|
||||
|
||||
function navigate(pageSelector, options = {}) {
|
||||
const target = $(pageSelector);
|
||||
if (!target.length) throw new Error(`Unbekannte Ansicht: ${pageSelector}`);
|
||||
@@ -49,19 +67,20 @@
|
||||
function renderRouteItems(listSelector, routes, emptyText) {
|
||||
const list = $(listSelector).empty();
|
||||
if (!routes.length) {
|
||||
list.append(`<li class="empty-list">${escapeHtml(emptyText)}</li>`);
|
||||
list.append(`<li><p>${escapeHtml(emptyText)}</p></li>`);
|
||||
return;
|
||||
}
|
||||
|
||||
routes.forEach(route => {
|
||||
const proximity = route.proximityM == null
|
||||
? ''
|
||||
: `<span class="route-distance">${escapeHtml(ns.Distance.format(route.proximityM))} entfernt</span>`;
|
||||
: `<small>${escapeHtml(ns.Distance.format(route.proximityM))} entfernt</small>`;
|
||||
|
||||
list.append(`
|
||||
<li>
|
||||
<button class="card-link" type="button" data-route-id="${route.id}">
|
||||
<span class="card-title">${escapeHtml(route.name)}</span>
|
||||
<span class="card-description">${escapeHtml(route.schoolName || route.description || '')}</span>
|
||||
<button type="button" data-route-id="${route.id}">
|
||||
<strong>${escapeHtml(route.name)}</strong>
|
||||
<span>${escapeHtml(route.schoolName || route.description || '')}</span>
|
||||
${proximity}
|
||||
</button>
|
||||
</li>
|
||||
@@ -88,8 +107,11 @@
|
||||
: 'Für Empfehlungen in der Nähe ist ein Standort erforderlich. Alle Routen bleiben unten auswählbar.';
|
||||
|
||||
renderRouteItems('#nearby-route-list', nearbyRoutes, nearbyEmptyText);
|
||||
renderRouteItems('#all-route-list', allRoutes,
|
||||
searchTerm ? 'Keine Route entspricht dem Suchbegriff.' : 'Es sind noch keine Routen vorhanden.');
|
||||
renderRouteItems(
|
||||
'#all-route-list',
|
||||
allRoutes,
|
||||
searchTerm ? 'Keine Route entspricht dem Suchbegriff.' : 'Es sind noch keine Routen vorhanden.'
|
||||
);
|
||||
|
||||
$('#all-routes-summary').text(
|
||||
searchTerm
|
||||
@@ -117,9 +139,43 @@
|
||||
});
|
||||
}
|
||||
|
||||
function hideNavigation() {
|
||||
$('#navigation-footer').prop('hidden', true);
|
||||
$('#app-shell').removeClass('has-navigation');
|
||||
}
|
||||
|
||||
function showNavigation(message = 'Position wird ermittelt', distance = '–') {
|
||||
$('#navigation-mode').text(message);
|
||||
$('#navigation-distance').text(distance);
|
||||
$('#navigation-footer').prop('hidden', false);
|
||||
$('#app-shell').addClass('has-navigation');
|
||||
}
|
||||
|
||||
function updateTrackingButton() {
|
||||
const button = $('#start-tracking');
|
||||
button.button('option', {
|
||||
label: state.tracking ? 'GPS-Begleitung stoppen' : 'GPS-Begleitung starten',
|
||||
icon: state.tracking ? 'ui-icon-stop' : 'ui-icon-pin-s'
|
||||
});
|
||||
}
|
||||
|
||||
function stopTracking() {
|
||||
state.tracking = false;
|
||||
state.routeProgressIndex = null;
|
||||
state.deviceHeading = null;
|
||||
ns.Geo.stop();
|
||||
ns.Orientation.stop();
|
||||
$('#tracking-status').prop('hidden', true).text('');
|
||||
hideNavigation();
|
||||
updateTrackingButton();
|
||||
}
|
||||
|
||||
function renderRoute(route) {
|
||||
if (state.tracking) stopTracking();
|
||||
|
||||
state.route = route;
|
||||
state.triggered.clear();
|
||||
state.routeProgressIndex = null;
|
||||
$('#route-title').text(route.name);
|
||||
$('#route-description').text(route.description || '');
|
||||
$('#route-distance').text(ns.Distance.format(route.distanceM));
|
||||
@@ -128,16 +184,16 @@
|
||||
|
||||
const list = $('#poi-list').empty();
|
||||
if (!route.pois.length) {
|
||||
list.append('<li class="empty-list">Diese Route enthält noch keine Stationen.</li>');
|
||||
list.append('<li><p>Diese Route enthält noch keine Stationen.</p></li>');
|
||||
return;
|
||||
}
|
||||
|
||||
route.pois.forEach(poi => {
|
||||
list.append(`
|
||||
<li>
|
||||
<button class="card-link" type="button" data-poi-id="${poi.id}">
|
||||
<span class="card-title">${escapeHtml(poi.title)}</span>
|
||||
<span class="card-description">${escapeHtml(poi.description || '')}</span>
|
||||
<button type="button" data-poi-id="${poi.id}">
|
||||
<strong>${escapeHtml(poi.title)}</strong>
|
||||
<span>${escapeHtml(poi.description || '')}</span>
|
||||
</button>
|
||||
</li>
|
||||
`);
|
||||
@@ -153,10 +209,99 @@
|
||||
if (automatic && poi.audioUrl) ns.AudioPlayer.play().catch(() => {});
|
||||
}
|
||||
|
||||
function nearestRoutePointIndex(position) {
|
||||
const points = state.route?.points || [];
|
||||
if (!points.length) return null;
|
||||
|
||||
let start = 0;
|
||||
let end = points.length;
|
||||
if (state.routeProgressIndex != null) {
|
||||
start = Math.max(0, state.routeProgressIndex - 3);
|
||||
end = Math.min(points.length, state.routeProgressIndex + 201);
|
||||
}
|
||||
|
||||
let nearestIndex = start;
|
||||
let nearestDistance = Number.POSITIVE_INFINITY;
|
||||
for (let index = start; index < end; index += 1) {
|
||||
const distance = ns.Distance.meters(position, points[index]);
|
||||
if (distance < nearestDistance) {
|
||||
nearestDistance = distance;
|
||||
nearestIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
return nearestIndex;
|
||||
}
|
||||
|
||||
function updateNavigation(position) {
|
||||
const points = state.route?.points || [];
|
||||
if (!state.tracking || !points.length) return;
|
||||
|
||||
const nearestIndex = nearestRoutePointIndex(position);
|
||||
if (nearestIndex == null) return;
|
||||
|
||||
state.routeProgressIndex = state.routeProgressIndex == null
|
||||
? nearestIndex
|
||||
: Math.max(state.routeProgressIndex, nearestIndex);
|
||||
|
||||
const lastIndex = points.length - 1;
|
||||
let targetIndex = Math.min(lastIndex, state.routeProgressIndex + 1);
|
||||
let target = points[targetIndex];
|
||||
let distance = ns.Distance.meters(position, target);
|
||||
const reachedRadius = Math.max(4, Math.min(12, Math.round(position.accuracy || 4)));
|
||||
|
||||
while (targetIndex < lastIndex && distance <= reachedRadius) {
|
||||
state.routeProgressIndex = targetIndex;
|
||||
targetIndex += 1;
|
||||
target = points[targetIndex];
|
||||
distance = ns.Distance.meters(position, target);
|
||||
}
|
||||
|
||||
if (targetIndex === lastIndex && distance <= reachedRadius) {
|
||||
state.routeProgressIndex = lastIndex;
|
||||
$('#navigation-arrow').css('transform', 'rotate(0deg)');
|
||||
showNavigation('Ziel der Route erreicht', '0 m');
|
||||
$('#navigation-footer').attr('aria-label', 'Ziel der Route erreicht');
|
||||
return;
|
||||
}
|
||||
|
||||
const bearing = ns.Distance.bearing(position, target);
|
||||
const movementHeading = Number.isFinite(position.heading) && Number(position.speed) > 0.5
|
||||
? position.heading
|
||||
: null;
|
||||
const heading = state.deviceHeading ?? movementHeading;
|
||||
const rotation = heading == null
|
||||
? bearing
|
||||
: ns.Distance.normalizeDegrees(bearing - heading);
|
||||
|
||||
$('#navigation-arrow').css('transform', `rotate(${rotation.toFixed(1)}deg)`);
|
||||
showNavigation(
|
||||
heading == null ? 'Norden oben · nächster Routenpunkt' : 'Nächster Routenpunkt',
|
||||
`${Math.round(distance)} m`
|
||||
);
|
||||
$('#navigation-footer').attr(
|
||||
'aria-label',
|
||||
`Nächster Routenpunkt in ${Math.round(distance)} Metern`
|
||||
);
|
||||
}
|
||||
|
||||
function updateHeading(heading) {
|
||||
if (state.deviceHeading == null) {
|
||||
state.deviceHeading = heading;
|
||||
} else {
|
||||
const delta = ((heading - state.deviceHeading + 540) % 360) - 180;
|
||||
state.deviceHeading = ns.Distance.normalizeDegrees(state.deviceHeading + delta * 0.25);
|
||||
}
|
||||
|
||||
if (state.position) updateNavigation(state.position);
|
||||
}
|
||||
|
||||
function evaluatePosition(position) {
|
||||
state.position = position;
|
||||
if (!state.route || !state.tracking) return;
|
||||
|
||||
updateNavigation(position);
|
||||
|
||||
let nearest = null;
|
||||
state.route.pois.forEach(poi => {
|
||||
if (state.triggered.has(poi.id)) return;
|
||||
@@ -165,8 +310,8 @@
|
||||
});
|
||||
|
||||
$('#tracking-status').text(
|
||||
`GPS aktiv · Genauigkeit ${Math.round(position.accuracy)} m` +
|
||||
(nearest ? ` · nächste Station ${Math.round(nearest.distance)} m` : '')
|
||||
`GPS aktiv · Genauigkeit ${Math.round(position.accuracy)} m`
|
||||
+ (nearest ? ` · nächste Station ${Math.round(nearest.distance)} m` : '')
|
||||
);
|
||||
|
||||
if (nearest && nearest.distance <= nearest.poi.triggerRadiusM) {
|
||||
@@ -176,22 +321,33 @@
|
||||
}
|
||||
}
|
||||
|
||||
function updateTrackingButton() {
|
||||
const button = $('#start-tracking');
|
||||
button.button('option', {
|
||||
label: state.tracking ? 'GPS-Begleitung stoppen' : 'GPS-Begleitung starten',
|
||||
icon: state.tracking ? 'ui-icon-stop' : 'ui-icon-pin-s'
|
||||
});
|
||||
async function startTracking() {
|
||||
state.tracking = true;
|
||||
state.routeProgressIndex = null;
|
||||
state.deviceHeading = null;
|
||||
updateTrackingButton();
|
||||
$('#tracking-status').prop('hidden', false).text('GPS wird gestartet.');
|
||||
showNavigation();
|
||||
|
||||
ns.Geo.start(
|
||||
evaluatePosition,
|
||||
error => $('#tracking-status').text(`GPS-Fehler: ${error.message}`)
|
||||
);
|
||||
|
||||
const orientationAvailable = await ns.Orientation.start(updateHeading);
|
||||
if (!orientationAvailable && state.tracking) {
|
||||
$('#navigation-mode').text('Norden oben · nächster Routenpunkt');
|
||||
}
|
||||
}
|
||||
|
||||
function bindEvents() {
|
||||
$(document).on('input', '#route-search', renderRoutes);
|
||||
|
||||
$(document).on('click', '#clear-route-search', function () {
|
||||
activate('#clear-route-search', function () {
|
||||
$('#route-search').val('').trigger('input').trigger('focus');
|
||||
});
|
||||
|
||||
$(document).on('click', '[data-route-id]', async function () {
|
||||
activate('[data-route-id]', async function () {
|
||||
const button = $(this);
|
||||
button.prop('disabled', true);
|
||||
try {
|
||||
@@ -205,26 +361,20 @@
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', '[data-poi-id]', function () {
|
||||
activate('[data-poi-id]', function () {
|
||||
const poi = state.route?.pois.find(item => item.id === Number($(this).data('poi-id')));
|
||||
if (poi) showPoi(poi, false);
|
||||
});
|
||||
|
||||
$(document).on('click', '[data-page-target]', function () {
|
||||
activate('[data-page-target]', function () {
|
||||
const target = $(this).data('page-target');
|
||||
if (history.state?.previous === target) history.back();
|
||||
else navigate(target, { replace: true });
|
||||
});
|
||||
|
||||
$(document).on('click', '#start-tracking', function () {
|
||||
state.tracking = !state.tracking;
|
||||
updateTrackingButton();
|
||||
$('#tracking-status').toggle(state.tracking);
|
||||
if (state.tracking) {
|
||||
ns.Geo.start(evaluatePosition, error => $('#tracking-status').text(`GPS-Fehler: ${error.message}`));
|
||||
} else {
|
||||
ns.Geo.stop();
|
||||
}
|
||||
activate('#start-tracking', async function () {
|
||||
if (state.tracking) stopTracking();
|
||||
else await startTracking();
|
||||
});
|
||||
|
||||
window.addEventListener('popstate', event => {
|
||||
@@ -249,8 +399,8 @@
|
||||
const position = await ns.Geo.current();
|
||||
state.position = position;
|
||||
$('#location-hint').text(
|
||||
`Standort ermittelt (Genauigkeit ${Math.round(position.accuracy)} m). ` +
|
||||
`Nahe Routen werden bis ${ns.Config.routeRadiusKm} km empfohlen.`
|
||||
`Standort ermittelt (Genauigkeit ${Math.round(position.accuracy)} m). `
|
||||
+ `Nahe Routen werden bis ${ns.Config.routeRadiusKm} km empfohlen.`
|
||||
);
|
||||
state.routes = state.routes.map(route => ({
|
||||
...route,
|
||||
@@ -266,6 +416,7 @@
|
||||
initializeRouteSearch();
|
||||
renderRoutes();
|
||||
updateTrackingButton();
|
||||
hideNavigation();
|
||||
}
|
||||
};
|
||||
}(window.Wegwichtel, window.jQuery));
|
||||
|
||||
Vendored
+10
-2
@@ -71,7 +71,7 @@
|
||||
initializeJqueryUi();
|
||||
mark('jquery-ui', 'done');
|
||||
|
||||
for (const module of ['config', 'api-client', 'distance', 'geolocation', 'slideshow', 'audio-player', 'app']) {
|
||||
for (const module of ['config', 'api-client', 'distance', 'geolocation', 'orientation', 'slideshow', 'audio-player', 'app']) {
|
||||
await loadScript(`js/${module}.js`, () => Boolean(window.Wegwichtel));
|
||||
}
|
||||
mark('modules', 'done');
|
||||
@@ -90,6 +90,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
retry.addEventListener('click', () => location.reload());
|
||||
function retryBoot(event) {
|
||||
if (event.type === 'keydown' && event.key !== 'Enter' && event.key !== ' ') return;
|
||||
if (event.type === 'pointerup' && event.pointerType === 'mouse' && event.button !== 0) return;
|
||||
event.preventDefault();
|
||||
location.reload();
|
||||
}
|
||||
|
||||
retry.addEventListener('pointerup', retryBoot);
|
||||
retry.addEventListener('keydown', retryBoot);
|
||||
boot();
|
||||
}());
|
||||
|
||||
+24
-3
@@ -1,12 +1,33 @@
|
||||
(function (ns) {
|
||||
'use strict';
|
||||
|
||||
const radians = value => value * Math.PI / 180;
|
||||
const degrees = value => value * 180 / Math.PI;
|
||||
const normalizeDegrees = value => (value % 360 + 360) % 360;
|
||||
|
||||
ns.Distance = {
|
||||
meters(a, b) {
|
||||
const dLat = radians(b.lat - a.lat); const dLon = radians(b.lon - a.lon);
|
||||
const h = Math.sin(dLat / 2) ** 2 + Math.cos(radians(a.lat)) * Math.cos(radians(b.lat)) * Math.sin(dLon / 2) ** 2;
|
||||
const dLat = radians(b.lat - a.lat);
|
||||
const dLon = radians(b.lon - a.lon);
|
||||
const h = Math.sin(dLat / 2) ** 2
|
||||
+ Math.cos(radians(a.lat)) * Math.cos(radians(b.lat)) * Math.sin(dLon / 2) ** 2;
|
||||
return 2 * 6371000 * Math.asin(Math.sqrt(h));
|
||||
},
|
||||
format(meters) { return meters < 1000 ? `${Math.round(meters)} m` : `${(meters / 1000).toFixed(1)} km`; }
|
||||
|
||||
bearing(a, b) {
|
||||
const lat1 = radians(a.lat);
|
||||
const lat2 = radians(b.lat);
|
||||
const dLon = radians(b.lon - a.lon);
|
||||
const y = Math.sin(dLon) * Math.cos(lat2);
|
||||
const x = Math.cos(lat1) * Math.sin(lat2)
|
||||
- Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
|
||||
return normalizeDegrees(degrees(Math.atan2(y, x)));
|
||||
},
|
||||
|
||||
normalizeDegrees,
|
||||
|
||||
format(meters) {
|
||||
return meters < 1000 ? `${Math.round(meters)} m` : `${(meters / 1000).toFixed(1)} km`;
|
||||
}
|
||||
};
|
||||
}(window.Wegwichtel));
|
||||
|
||||
@@ -1,18 +1,61 @@
|
||||
(function (ns) {
|
||||
'use strict';
|
||||
|
||||
let watchId = null;
|
||||
function normalize(position) { return { lat: position.coords.latitude, lon: position.coords.longitude, accuracy: position.coords.accuracy, timestamp: position.timestamp }; }
|
||||
|
||||
function normalize(position) {
|
||||
return {
|
||||
lat: position.coords.latitude,
|
||||
lon: position.coords.longitude,
|
||||
accuracy: position.coords.accuracy,
|
||||
altitude: position.coords.altitude,
|
||||
heading: position.coords.heading,
|
||||
speed: position.coords.speed,
|
||||
timestamp: position.timestamp
|
||||
};
|
||||
}
|
||||
|
||||
ns.Geo = {
|
||||
current() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!navigator.geolocation) return reject(new Error('Dieses Gerät unterstützt keine Geolokalisierung.'));
|
||||
navigator.geolocation.getCurrentPosition(p => resolve(normalize(p)), reject, { enableHighAccuracy: true, timeout: ns.Config.geolocationTimeoutMs, maximumAge: 30000 });
|
||||
if (!navigator.geolocation) {
|
||||
reject(new Error('Dieses Gerät unterstützt keine Geolokalisierung.'));
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
position => resolve(normalize(position)),
|
||||
reject,
|
||||
{
|
||||
enableHighAccuracy: true,
|
||||
timeout: ns.Config.geolocationTimeoutMs,
|
||||
maximumAge: 30000
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
start(callback, onError) {
|
||||
this.stop();
|
||||
watchId = navigator.geolocation.watchPosition(p => callback(normalize(p)), onError, { enableHighAccuracy: true, timeout: 20000, maximumAge: 5000 });
|
||||
if (!navigator.geolocation) {
|
||||
onError?.(new Error('Dieses Gerät unterstützt keine Geolokalisierung.'));
|
||||
return;
|
||||
}
|
||||
|
||||
watchId = navigator.geolocation.watchPosition(
|
||||
position => callback(normalize(position)),
|
||||
onError,
|
||||
{
|
||||
enableHighAccuracy: true,
|
||||
timeout: 20000,
|
||||
maximumAge: 5000
|
||||
}
|
||||
);
|
||||
},
|
||||
stop() { if (watchId != null) navigator.geolocation.clearWatch(watchId); watchId = null; }
|
||||
|
||||
stop() {
|
||||
if (watchId != null) navigator.geolocation.clearWatch(watchId);
|
||||
watchId = null;
|
||||
}
|
||||
};
|
||||
}(window.Wegwichtel));
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
(function (ns) {
|
||||
'use strict';
|
||||
|
||||
let listener = null;
|
||||
const eventNames = ['deviceorientationabsolute', 'deviceorientation'];
|
||||
let callback = null;
|
||||
|
||||
function normalizeDegrees(value) {
|
||||
return (value % 360 + 360) % 360;
|
||||
}
|
||||
|
||||
function readHeading(event) {
|
||||
if (Number.isFinite(event.webkitCompassHeading)) {
|
||||
return normalizeDegrees(event.webkitCompassHeading);
|
||||
}
|
||||
|
||||
if ((event.absolute || event.type === 'deviceorientationabsolute') && Number.isFinite(event.alpha)) {
|
||||
return normalizeDegrees(360 - event.alpha);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function requestPermission() {
|
||||
const orientation = window.DeviceOrientationEvent;
|
||||
if (!orientation) return false;
|
||||
|
||||
if (typeof orientation.requestPermission === 'function') {
|
||||
return (await orientation.requestPermission()) === 'granted';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ns.Orientation = {
|
||||
async start(onHeading) {
|
||||
this.stop();
|
||||
callback = onHeading;
|
||||
|
||||
let permitted = false;
|
||||
try {
|
||||
permitted = await requestPermission();
|
||||
} catch {
|
||||
permitted = false;
|
||||
}
|
||||
|
||||
if (!permitted) return false;
|
||||
|
||||
listener = event => {
|
||||
const heading = readHeading(event);
|
||||
if (heading != null) callback?.(heading);
|
||||
};
|
||||
|
||||
eventNames.forEach(name => window.addEventListener(name, listener, true));
|
||||
return true;
|
||||
},
|
||||
|
||||
stop() {
|
||||
if (listener) eventNames.forEach(name => window.removeEventListener(name, listener, true));
|
||||
listener = null;
|
||||
callback = null;
|
||||
}
|
||||
};
|
||||
}(window.Wegwichtel));
|
||||
+44
-2
@@ -3,6 +3,23 @@
|
||||
|
||||
let images = [];
|
||||
let index = 0;
|
||||
let swipe = null;
|
||||
|
||||
function activate(selector, handler) {
|
||||
$(document)
|
||||
.on('pointerup', selector, function (event) {
|
||||
const pointer = event.originalEvent;
|
||||
if (pointer && pointer.isPrimary === false) return;
|
||||
if (pointer?.pointerType === 'mouse' && pointer.button !== 0) return;
|
||||
event.preventDefault();
|
||||
handler.call(this, event);
|
||||
})
|
||||
.on('keydown', selector, function (event) {
|
||||
if (event.key !== 'Enter' && event.key !== ' ') return;
|
||||
event.preventDefault();
|
||||
handler.call(this, event);
|
||||
});
|
||||
}
|
||||
|
||||
function refreshButton(selector, disabled) {
|
||||
const button = $(selector).prop('disabled', disabled);
|
||||
@@ -30,17 +47,42 @@
|
||||
index = 0;
|
||||
render();
|
||||
},
|
||||
|
||||
next() {
|
||||
if (images.length) index = (index + 1) % images.length;
|
||||
render();
|
||||
},
|
||||
|
||||
previous() {
|
||||
if (images.length) index = (index - 1 + images.length) % images.length;
|
||||
render();
|
||||
}
|
||||
};
|
||||
|
||||
activate('#slide-next', () => ns.Slideshow.next());
|
||||
activate('#slide-prev', () => ns.Slideshow.previous());
|
||||
|
||||
$(document)
|
||||
.on('click', '#slide-next', () => ns.Slideshow.next())
|
||||
.on('click', '#slide-prev', () => ns.Slideshow.previous());
|
||||
.on('pointerdown', '#slideshow', function (event) {
|
||||
if ($(event.target).closest('button').length) return;
|
||||
const pointer = event.originalEvent;
|
||||
if (!pointer?.isPrimary) return;
|
||||
swipe = { id: pointer.pointerId, x: pointer.clientX, y: pointer.clientY };
|
||||
})
|
||||
.on('pointerup pointercancel', '#slideshow', function (event) {
|
||||
const pointer = event.originalEvent;
|
||||
if (!swipe || pointer?.pointerId !== swipe.id) return;
|
||||
if (event.type === 'pointercancel') {
|
||||
swipe = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const deltaX = pointer.clientX - swipe.x;
|
||||
const deltaY = pointer.clientY - swipe.y;
|
||||
swipe = null;
|
||||
|
||||
if (Math.abs(deltaX) < 45 || Math.abs(deltaX) <= Math.abs(deltaY)) return;
|
||||
if (deltaX < 0) ns.Slideshow.next();
|
||||
else ns.Slideshow.previous();
|
||||
});
|
||||
}(window.Wegwichtel, window.jQuery));
|
||||
|
||||
Reference in New Issue
Block a user