<div id="map">
<div id="pegman-container">
<div id="pegman"> </div>
</div>
</div>
<!-- pre render images -->
<img src="https://maps.gstatic.com/tactile/pegman_v3/santa/runway-2x.png" height="0" width="0" />
<img src="https://maps.gstatic.com/tactile/pegman_v3/santa/dropping-2x.png" height="0" width="0" />
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--map-image: url('https://res.cloudinary.com/rodrigoantunes/image/upload/v1734020140/santa_fh4gfz.png');
--pegman-initial: url('https://maps.gstatic.com/tactile/pegman_v3/santa/runway-2x.png');
--pegman-moving: url('https://maps.gstatic.com/tactile/pegman_v3/santa/dropping-2x.png');
}
body {
overflow: hidden
}
#map {
width: 100%;
height: 100dvh;
background-color: bisque;
background-image: var(--map-image);
background-size: cover;
background-position: center center;
position: relative;
display: flex;
justify-content: flex-end;
align-items: flex-end;
padding: 15px;
}
#pegman-container {
position: absolute;
background-color: #fff;
height: 70px;
aspect-ratio: 1;
display: flex;
justify-content: center;
align-items: center;
border-radius: 4px;
box-shadow: 2px 2px 4px rgba(0,0,0,0.3)
}
#pegman {
width: 35px;
height: 55px;
background-image: var(--pegman-initial);
background-size: 55px;
background-position: -10px -65px;
rotate: var(--r);
transform-origin: 50% 0%;
transition: rotate 200ms;
cursor: grab;
}
#pegman:active {
background-size: 100px;
background-image: var(--pegman-moving);
cursor: grabbing;
background-position: -25px -35px;
}
const pegman = document.querySelector('#pegman');
let isDragging = false;
let initialX = 0;
let initialY = 0;
let inactivityTimeout;
let lastX = 0;
const timeout = 25;
const maxDegrees = 50;
const onMouseDown = (e) => {
isDragging = true;
initialX = e.clientX;
initialY = e.clientY;
};
const onMouseMove = (e) => {
if (!isDragging) return;
const dy = e.clientY - initialY;
const dx = e.clientX - initialX;
let rx = Math.max(-maxDegrees, Math.min(maxDegrees, dx - lastX));
pegman.setAttribute('style', `--r: ${rx}deg`);
pegman.animate({ translate: `${dx}px ${dy}px` }, {
duration: 100,
fill: 'forwards',
});
clearTimeout(inactivityTimeout);
inactivityTimeout = setTimeout(() => {
lastX = dx;
pegman.setAttribute('style', `--r: ${0}deg`);
}, timeout);
};
const onMouseUp = () => {
isDragging = false;
pegman.setAttribute('style', `--r: ${0}`);
pegman.animate({ translate: `0px 0px` }, {
duration: 500,
fill: 'forwards',
easing: 'ease',
});
inactivityTimeout = setTimeout(() => {
lastX = 0;
}, timeout);
};
pegman.addEventListener('mousedown', onMouseDown);
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.