Skip to content

Latest commit

 

History

History
158 lines (116 loc) · 4.54 KB

README.md

File metadata and controls

158 lines (116 loc) · 4.54 KB

How to Recreate the Iconic Pegman from Google Maps

dev.to

Demo

CodePen

Post

This is a little snippet I’ve wanted to try for quite some time. I finally got around to recreating this iconic feature from Google Maps: the ability to drag and drop this little guy, known as Pegman, to switch to Street View.

HTML

The structure is straightforward. I have:

  • A #map to serve as the container (with a background-image applied).
  • A #pegman-container positioned in the bottom-right corner.
  • The #pegman element itself for the draggable character.
<div id="map">
  <div id="pegman-container">
    <div id="pegman"> </div>
  </div>
</div>

CSS

Positioning everything is simple, as you can see in the source code above, but the most important part is the rotate property. This is dynamically updated using JavaScript:

#pegman {
  /* other styles */
  rotate: var(--r);
}

The rotate value changes based on user interaction, which we’ll dive into next.

Javascript

Everything here revolves around user interaction. JavaScript handles:

  • Listening for mouse events.
  • Updating Pegman’s position and rotation dynamically.
  • Adding smooth animations for better UX.
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;

// Event listeners
pegman.addEventListener('mousedown', onMouseDown);
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);

Let’s break down each function:

onMouseDown

This starts the drag by recording the initial mouse position and toggling the isDragging flag.

const onMouseDown = (e) => {
  isDragging = true;
  initialX = e.clientX;
  initialY = e.clientY;
};

onMouseMove

This handles Pegman’s movement and rotation during the drag. It ensures:

  • Rotation stays within a range (to avoid extreme angles like Superman flying).
  • The rotate value updates dynamically using --r.
const onMouseMove = (e) => {
  if (!isDragging) return;
  // only runs if the onMouseMove was fired

  const dy = e.clientY - initialY;
  const dx = e.clientX - initialX;

  // Limit rotation range
  let rx = Math.max(-maxDegrees, Math.min(maxDegrees, dx - lastX));
  pegman.setAttribute('style', `--r: ${rx}deg`);

  // Animate Pegman's position
  pegman.animate({ translate: `${dx}px ${dy}px` }, {
    duration: 100,
    fill: 'forwards',
  });

  // Reset rotation after inactivity
  clearTimeout(inactivityTimeout);
  inactivityTimeout = setTimeout(() => {
    lastX = dx;
    pegman.setAttribute('style', `--r: 0deg`);
  }, timeout);
};

onMouseUp

This resets Pegman’s state once the user stops dragging:

  • Rotation resets to 0.
  • Pegman smoothly returns to its original position.
const onMouseUp = () => {
  isDragging = false;

  // Reset rotation
  pegman.setAttribute('style', `--r: 0`);

  // Animate Pegman back to its original position
  pegman.animate({ translate: `0px 0px` }, {
    duration: 500,
    fill: 'forwards',
    easing: 'ease',
  });

  // Clear residual state
  inactivityTimeout = setTimeout(() => {
    lastX = 0;
  }, timeout);
};

Final Thoughts

This snippet highlights how simple animations and interactivity can recreate an iconic user experience. Key takeaways:

  • Using CSS custom properties (like --r) keeps styling dynamic and manageable.
  • Rotation limits ensure a polished, natural feel for user interactions.
  • Timeouts and easing animations add realism to the motion.

Feel free to fork the CodePen, make changes and let me know what you think! 🚀🗺️

By the way...

I'm using the original sprites from google, which you can see here and make experiments:

Did you know? ✨ The Google Maps location I used is the real Santa Claus Village! Fun fact: Santa’s Finnish name is Joulupukki, and he originally from Finland 🎅🏻🎄