Skip to content

Commit

Permalink
using the in-house developed Leaflet_FullScreen_Button plugin for the…
Browse files Browse the repository at this point in the history
… corresponding functionality
  • Loading branch information
sergeiown committed Jul 29, 2024
1 parent 21f56a3 commit e4ba426
Show file tree
Hide file tree
Showing 3 changed files with 227 additions and 83 deletions.
251 changes: 181 additions & 70 deletions js/fullScreen.js
Original file line number Diff line number Diff line change
@@ -1,87 +1,198 @@
/* Copyright (c) 2023-2024 Serhii I. Myshko
https://github.com/sergeiown/Map_with_Marker_Clusters/blob/main/LICENSE */

export function toggleFullScreen(element) {
function handleFullScreenRequest(promise) {
promise
.then(() => {
updateFullScreenButton(true);
})
.catch((err) => {
console.error(`Error attempting to enable full-screen mode: ${err.message} (${err.name})`);
});
class FullScreenButton extends L.Control {
options = {
position: 'topleft',
title: 'Toggle fullscreen',
enterFullScreenIcon: null,
exitFullScreenIcon: null,
enterFullScreenTitle: 'Enter fullscreen mode',
exitFullScreenTitle: 'Exit fullscreen mode',
onFullScreenChange: null,
showNotification: true,
};

onAdd(map) {
const container = L.DomUtil.create('div', 'leaflet-control-fullscreen leaflet-bar leaflet-control');
container.title = this.options.title;

this._updateIcon(container, false);

container.onclick = () => {
this.toggleFullScreen(map);
};

this._eventHandlers = {
fullscreenchange: this._throttle(() => this._handleFullScreenChange(container, map), 100),
keydown: this._preventF11Default.bind(this),
};

this._addEventListeners();

return container;
}

onRemove() {
this._removeEventListeners();
}

function handleFullScreenExit(promise) {
promise
.then(() => {
updateFullScreenButton(false);
})
.catch((err) => {
console.error(`Error attempting to disable full-screen mode: ${err.message} (${err.name})`);
async toggleFullScreen(map) {
const mapContainer = map.getContainer();
const isFullScreen = this._isFullScreen(mapContainer);

try {
await this._toggleFullScreenElement(mapContainer, !isFullScreen);
this._updateIcon(this._container, !isFullScreen);
this._handleFullScreenChange(mapContainer, map);
} catch (err) {
console.error(`Error switching to full-screen mode: ${err.message} (${err.name})`);
if (this.options.showNotification) {
this._showNotification(`Error switching to full-screen mode`, mapContainer);
}
}
}

async _toggleFullScreenElement(element, enterFullScreen = true) {
const methods = {
enter: ['requestFullscreen', 'mozRequestFullScreen', 'webkitRequestFullscreen', 'msRequestFullscreen'],
exit: ['exitFullscreen', 'mozCancelFullScreen', 'webkitExitFullscreen', 'msExitFullscreen'],
};

const target = enterFullScreen ? element : document;

for (const method of methods[enterFullScreen ? 'enter' : 'exit']) {
if (target[method]) {
await target[method]();
return;
}
}

element.classList.toggle('pseudo-fullscreen', enterFullScreen);

map.invalidateSize();
}

_handleFullScreenChange(container, map) {
const isFullScreen = this._isFullScreen(container);
this._updateIcon(container, isFullScreen);
this._container.title = isFullScreen ? this.options.exitFullScreenTitle : this.options.enterFullScreenTitle;

if (typeof this.options.onFullScreenChange === 'function') {
if (this._isHandlingFullScreenChange) return;
this._isHandlingFullScreenChange = true;

requestAnimationFrame(() => {
this.options.onFullScreenChange(isFullScreen);
this._isHandlingFullScreenChange = false;
});
}

if (this.options.showNotification) {
this._showNotification(
isFullScreen ? 'Full-screen mode is ON' : 'Full-screen mode is OFF',
map.getContainer()
);
}
}

if (
!document.fullscreenElement &&
!document.mozFullScreenElement &&
!document.webkitFullscreenElement &&
!document.msFullscreenElement
) {
if (element.requestFullscreen) {
handleFullScreenRequest(element.requestFullscreen());
} else if (element.mozRequestFullScreen) {
handleFullScreenRequest(element.mozRequestFullScreen()); // Firefox
} else if (element.webkitRequestFullscreen) {
handleFullScreenRequest(element.webkitRequestFullscreen()); // Chrome, Safari, Opera
} else if (element.msRequestFullscreen) {
handleFullScreenRequest(element.msRequestFullscreen()); // IE/Edge
} else {
console.error('Fullscreen API is not supported by this browser.');
_preventF11Default(event) {
if (event.key === 'F11') {
event.preventDefault();
this.toggleFullScreen(map);
}
} else {
if (document.exitFullscreen) {
handleFullScreenExit(document.exitFullscreen());
} else if (document.mozCancelFullScreen) {
handleFullScreenExit(document.mozCancelFullScreen());
} else if (document.webkitExitFullscreen) {
handleFullScreenExit(document.webkitExitFullscreen());
} else if (document.msExitFullscreen) {
handleFullScreenExit(document.msExitFullscreen());
} else {
console.error('Fullscreen API is not supported by this browser.');
}

_updateIcon(container, isFullScreen) {
const enterFullScreenIconDefault = `
<svg width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5 6a1 1 0 011-1h2a1 1 0 000-2H6a3 3 0 00-3 3v2a1 1 0 002 0V6zM5 18a1 1 0 001 1h2a1 1 0 110 2H6a3 3 0 01-3-3v-2a1 1 0 112 0v2zM18 5a1 1 0 011 1v2a1 1 0 102 0V6a3 3 0 00-3-3h-2a1 1 0 100 2h2zM19 18a1 1 0 01-1 1h-2a1 1 0 100 2h2a3 3 0 003-3v-2a1 1 0 10-2 0v2z" fill="#000"/></svg>
`;

const exitFullScreenIconDefault = `
<svg width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9 4a1 1 0 00-2 0v2.5a.5.5 0 01-.5.5H4a1 1 0 000 2h2.5A2.5 2.5 0 009 6.5V4zM9 20a1 1 0 11-2 0v-2.5a.5.5 0 00-.5-.5H4a1 1 0 110-2h2.5A2.5 2.5 0 019 17.5V20zM16 3a1 1 0 00-1 1v2.5A2.5 2.5 0 0017.5 9H20a1 1 0 100-2h-2.5a.5.5 0 01-.5-.5V4a1 1 0 00-1-1zM15 20a1 1 0 102 0v-2.5a.5.5 0 01.5-.5H20a1 1 0 100-2h-2.5a2.5 2.5 0 00-2.5 2.5V20z" fill="#000"/></svg>
`;

const iconUrl = isFullScreen
? this.options.exitFullScreenIcon || `data:image/svg+xml;base64,${btoa(exitFullScreenIconDefault)}`
: this.options.enterFullScreenIcon || `data:image/svg+xml;base64,${btoa(enterFullScreenIconDefault)}`;

if (container.classList.contains('leaflet-control-fullscreen')) {
container.style.backgroundImage = `url('${iconUrl}')`;
}
}
}

function updateButtonOnFullScreenChange() {
updateFullScreenButton(
!!document.fullscreenElement ||
!!document.mozFullScreenElement ||
!!document.webkitFullscreenElement ||
!!document.msFullscreenElement
);
}
_addEventListeners() {
document.addEventListener('fullscreenchange', this._eventHandlers.fullscreenchange);
document.addEventListener('mozfullscreenchange', this._eventHandlers.fullscreenchange);
document.addEventListener('webkitfullscreenchange', this._eventHandlers.fullscreenchange);
document.addEventListener('MSFullscreenChange', this._eventHandlers.fullscreenchange);
document.addEventListener('keydown', this._eventHandlers.keydown);
}

function updateFullScreenButton(isFullScreen) {
const fullScreenButton = document.querySelector('.full-screen-button');
if (fullScreenButton) {
if (isFullScreen) {
fullScreenButton.title = 'Exit full screen mode';
fullScreenButton.querySelector('img').src = './markers/general_screen.svg';
} else {
fullScreenButton.title = 'Set full screen mode';
fullScreenButton.querySelector('img').src = './markers/full_screen.svg';
_removeEventListeners() {
if (this._eventHandlers) {
document.removeEventListener('fullscreenchange', this._eventHandlers.fullscreenchange);
document.removeEventListener('mozfullscreenchange', this._eventHandlers.fullscreenchange);
document.removeEventListener('webkitfullscreenchange', this._eventHandlers.fullscreenchange);
document.removeEventListener('MSFullscreenChange', this._eventHandlers.fullscreenchange);
document.removeEventListener('keydown', this._eventHandlers.keydown);
delete this._eventHandlers;
}
}
}

document.addEventListener('keydown', (event) => {
if (event.key === 'F11') {
event.preventDefault();
_throttle(func, limit) {
let inThrottle;
return function () {
const args = arguments;
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}
});
document.addEventListener('fullscreenchange', updateButtonOnFullScreenChange);
document.addEventListener('mozfullscreenchange', updateButtonOnFullScreenChange);
document.addEventListener('webkitfullscreenchange', updateButtonOnFullScreenChange);
document.addEventListener('MSFullscreenChange', updateButtonOnFullScreenChange);

_isFullScreen(element) {
return (
document.fullscreenElement ||
document.mozFullScreenElement ||
document.webkitFullscreenElement ||
document.msFullscreenElement ||
element.classList.contains('pseudo-fullscreen')
);
}

_showNotification(message, mapContainer) {
if (this._notificationElement) {
mapContainer.removeChild(this._notificationElement);
this._notificationElement = null;
clearTimeout(this._notificationTimeout);
}

const notification = L.DomUtil.create('div', '', mapContainer);
notification.id = 'map-notification';
notification.innerText = message;
mapContainer.appendChild(notification);

this._notificationElement = notification;

this._notificationTimeout = setTimeout(() => {
notification.style.transition = 'opacity 1s';
notification.style.opacity = '0';

setTimeout(() => {
if (notification.parentElement) {
mapContainer.removeChild(notification);
}
this._notificationElement = null;
}, 1000);
}, 3000);
}
}

L.control.fullScreenButton = function (options) {
return new FullScreenButton(options);
};

export default L.control.fullScreenButton;
20 changes: 7 additions & 13 deletions js/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import * as layers from '../js/layers.js';
import { isMobile } from '../js/mobileDetector.js';
import { createControlButton } from '../js/buttons.js';
import { addLegend } from '../js/legend.js';
import { toggleFullScreen } from '../js/fullScreen.js';
import { openPopupWindow } from '../js/popupWindow.js';
import { updateControlStyle, updateLayer, gradualOpacityAnimation } from '../js/mapUtils.js';
import '../js/fullScreen.js';

export function initializeMap() {
const initialZoom = isMobile ? 5 : 6;
Expand All @@ -27,19 +27,13 @@ export function initializeMap() {
});
map.addControl(new centerButton());

// Creation of full screen button for desktop device
// Creat a full screen button
if (!isMobile) {
const fullScreenButton = createControlButton({
position: 'topleft',
title: 'Set full screen mode',
imageSrc: './markers/full_screen.svg',
extraClass: 'full-screen-button',
onClick: function () {
const mapContainer = document.getElementById('map');
toggleFullScreen(mapContainer);
},
});
map.addControl(new fullScreenButton());
L.control
.fullScreenButton({
position: 'topleft',
})
.addTo(map);
}

// Create a button to call the external map frame
Expand Down
39 changes: 39 additions & 0 deletions js/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,45 @@ export function addStyles() {
border: 1px solid rgb(150, 150, 150);
border-radius: 5px;
}
.pseudo-fullscreen {
background-color: #ffffff;
top: 0;
left: 0;
position: fixed !important;
width: 100% !important;
height: 100% !important;
z-index: 9999 !important;
}
.leaflet-control-fullscreen {
background-color: rgb(245, 245, 245);
background-size: 40px 40px;
background-repeat: no-repeat;
background-position: center;
width: 50px;
height: 50px;
border: 2px solid rgb(150, 150, 150) !important;
border-radius: 5px;
cursor: pointer;
z-index: 9999;
}
#map-notification {
position: absolute;
left: 50%;
bottom: 20px;
transform: translateX(-50%);
padding: 10px 20px;
background-color: #00000099;
color: #ffffff;
font-size: 1rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
border-radius: 5px;
z-index: 9999;
}
`;
document.head.appendChild(styleTag);
Expand Down

0 comments on commit e4ba426

Please sign in to comment.