)
}
}
)
(
}
{
)
)
(
)
(
(
{
}
)
(
)
}
)
)
{
(
(
)
)
}
)
(
}

Quick Touch Events 2 (easing)

  1. // no scrolling on mobile
  2. document.addEventListener('touchmove', e => e.preventDefault(), {
  3.   passive: false
  4. });
  5.  
  6. const hasTouch =
  7.   navigator.maxTouchPoints != null && navigator.maxTouchPoints > 0;
  8. // looking forward to `navigator?.maxTouchPoints > 0`
  9. // being better supported
  10.  
  11. function touchify(e) {
  12.   const touch = [];
  13.   touch.x = touch.y = 0;
  14.  
  15.   if (e.touches != null && e.touches.length > 0) {
  16.     touch.x = e.touches[0].clientX;
  17.     touch.y = e.touches[0].clientY;
  18.     for (let i = 0; i < e.touches.length; i++) {
  19.       touch[i] = e.touches[i];
  20.     }
  21.   } else {
  22.     touch.x = e.clientX;
  23.     touch.y = e.clientY;
  24.     touch[0] = { clientX: e.clientX, clientY: e.clientY };
  25.   }
  26.   return touch;
  27. }
  28.  
  29. let moveOrTouchMove;
  30.  
  31. if (hasTouch) {
  32.   moveOrTouchMove = 'touchmove';
  33. } else {
  34.   moveOrTouchMove = 'mousemove';
  35. }
  36.  
  37. function dot(x, y, radius, color) {
  38.   const el = document.createElement('div');
  39.   const size = `${radius * 2}px`;
  40.   Object.assign(el.style, {
  41.     position: 'absolute',
  42.     left: `${x}px`,
  43.     top: `${y}px`,
  44.     width: size,
  45.     height: size,
  46.     transform: `translate(${-radius}px, ${-radius}px)`,
  47.     borderRadius: '50%',
  48.     background: color
  49.   });
  50.   el.classList.add('dot');
  51.   document.body.appendChild(el);
  52.   return el;
  53. }
  54.  
  55. let dotX = 100,
  56.   dotY = 100,
  57.   touchX = 200,
  58.   touchY = 150,
  59.   damp = 12,
  60.   dotEl = dot(dotX, dotY, 20, 'red');
  61.  
  62. document.addEventListener(moveOrTouchMove, e => {
  63.   const { x, y } = touchify(e);
  64.   touchX = x;
  65.   touchY = y;
  66. });
  67.  
  68. function loop() {
  69.   dotX += (touchX - dotX) / damp;
  70.   dotY += (touchY - dotY) / damp;
  71.   Object.assign(dotEl.style, {
  72.     left: `${dotX}px`,
  73.     top: `${dotY}px`
  74.   });
  75.   window.requestAnimationFrame(loop);
  76. }
  77. loop();

Move your mouse on desktop or your finger on mobile – watch the red dot follow…

This is a simpler version of some of the things used in yesterdays post – just a quick way to normalize touch events – just one of many ways to do this – for simple stuff this is my goto.

If you want to try it out on its own page – take a look here (specifically good for trying it on mobile).

You’ll probably want a to use a meta tag like this – for the full effect.

  1. <meta name="viewport" content="width=device-width, initial-scale=1.0">

Quick Touch Events

  1. // no scrolling on mobile
  2. document.addEventListener("touchmove", e => e.preventDefault(), {
  3.   passive: false
  4. });
  5.  
  6. // has more than one touch point
  7. const hasTouch =
  8.   navigator.maxTouchPoints != null && navigator.maxTouchPoints > 0;
  9.  
  10. function touchify(e) {
  11.   const touch = [];
  12.   touch.x = touch.y = 0;
  13.  
  14.   if (e.touches != null && e.touches.length > 0) {
  15.     touch.x = e.touches[0].clientX;
  16.     touch.y = e.touches[0].clientY;
  17.     for (let i = 0; i < e.touches.length; i++) {
  18.       touch[i] = e.touches[i];
  19.     }
  20.   } else {
  21.     touch.x = e.clientX;
  22.     touch.y = e.clientY;
  23.     touch[0] = { clientX: e.clientX, clientY: e.clientY };
  24.   }
  25.   return touch;
  26. }
  27.  
  28. let clickOrTouchEnd,
  29.   downOrTouchStart,
  30.   enterOrTouchStart,
  31.   moveOrTouchMove,
  32.   upOrTouchEnd,
  33.   leaveOrTouchEnd;
  34.  
  35. if (hasTouch) {
  36.   clickOrTouchEnd = "touchend";
  37.   downOrTouchStart = "touchstart";
  38.   enterOrTouchStart = "touchstart";
  39.   moveOrTouchMove = "touchmove";
  40.   upOrTouchEnd = "touchend";
  41.   leaveOrTouchEnd = "touchend";
  42. } else {
  43.   clickOrTouchEnd = "click";
  44.   downOrTouchStart = "mousedown";
  45.   enterOrTouchStart = "mouseenter";
  46.   moveOrTouchMove = "mousemove";
  47.   upOrTouchEnd = "mouseup";
  48.   leaveOrTouchEnd = "mouseleave";
  49. }
  50.  
  51. function dot(x, y, radius, color) {
  52.   const el = document.createElement("div");
  53.   const size = `${radius * 2}px`;
  54.   Object.assign(el.style, {
  55.     position: "absolute",
  56.     left: `${x}px`,
  57.     top: `${y}px`,
  58.     width: size,
  59.     height: size,
  60.     transform: `translate(${-radius}px, ${-radius}px)`,
  61.     borderRadius: "50%",
  62.     background: color
  63.   });
  64.   el.classList.add("dot");
  65.   document.body.appendChild(el);
  66.   return el;
  67. }
  68.  
  69. let down = false;
  70. let lastTouch;
  71. document.addEventListener(downOrTouchStart, e => {
  72.   const { x, y } = touchify(e);
  73.   dot(x, y, 30, `rgba(255, 0, 0, 0.2)`);
  74.   down = true;
  75. });
  76.  
  77. document.addEventListener(moveOrTouchMove, e => {
  78.   if (down) {
  79.     const touches = touchify(e);
  80.     // draw more than one touch
  81.     for (let i = 0; i < touches.length; i++) {
  82.       const touch = touches[i];
  83.       dot(touch.clientX, touch.clientY, 10, `rgba(0, 0, 155, 0.2)`);
  84.     }
  85.  
  86.     lastTouch = touches[0];
  87.   }
  88. });
  89.  
  90. document.addEventListener(upOrTouchEnd, e => {
  91.   if (down) {
  92.     dot(lastTouch.clientX, lastTouch.clientY, 20, `rgba(0, 155, 0, 0.2)`);
  93.     down = false;
  94.   }
  95. });

Draw with your mouse by clicking, holding down and dragging…

I often use some variation of this depending on the project… This snippet normalizes touch events in a simple way, prevents scrolling and gives an example of how to handle multi-touch.

You’ll probably want a to use a meta tag like this – for the full effect.

  1. <meta name="viewport" content="width=device-width, initial-scale=1.0">

Point DIV at Another DIV (Angle Between Two Points)

  1. const dot = document.createElement('div')
  2. Object.assign(dot.style, {
  3.   position: 'absolute',
  4.   left: '100px', 
  5.   top: '100px',
  6.   width: '10px', 
  7.   height: '10px',
  8.   transform: 'translate(-5px, -5px)',
  9.   background: 'black'
  10. });
  11. document.body.appendChild(dot);
  12.  
  13. const pointer = document.createElement('div');
  14. Object.assign(pointer.style, {
  15.   position: 'absolute',
  16.   left: '-10px', 
  17.   top: '-5px',
  18.   width: '20px', 
  19.   height: '10px',
  20.   background: 'red'
  21. });
  22. document.body.appendChild(pointer);
  23.  
  24. // desktop only (`touchmove` needed for mobile)
  25. document.addEventListener('mousemove', (e) => {
  26.   const dx = parseFloat(dot.style.left) - e.clientX;
  27.   const dy = parseFloat(dot.style.top) - e.clientY;
  28.   const angle = Math.atan2(dy, dx) / Math.PI * 180;
  29.  
  30.   pointer.style.transform = `
  31.     translate(${e.clientX}px, ${e.clientY}px)
  32.     rotate(${angle}deg)
  33.   `;
  34. });

Moving your mouse around the page, you’ll notice the red div always points at the black div. (more…)

No Scrolling on Mobile

  1. document.addEventListener('touchmove', 
  2.   e => e.preventDefault(), { passive: false });
  3.   document.body.innerHTML = 'Hi, no page scrolling here...';

It’s common to want to prevent page scrolling on mobile. Here is an easy way to do it.

Rectangle Path with SVG

  1. const rectPath = rect =>
  2.   `M ${rect.left} ${rect.top}
  3.   L ${rect.right} ${rect.top}
  4.     ${rect.right} ${rect.bottom}
  5.     ${rect.left} ${rect.bottom}
  6.     ${rect.left} ${rect.top} `;
  7.  
  8. const el = document.body.appendChild(
  9.   document.createElement('div'));
  10.  
  11. el.innerHTML = `
  12.   <svg width="100%" height="100%" viewBox="0 0 550 496">
  13.     <path d="
  14.     ${rectPath({left: 20, top: 10, right: 100, bottom: 100})}
  15.     ${rectPath({left: 50, top: 50, right: 200, bottom: 200})}
  16.     ${rectPath({left: 150, top: 20, right: 250, bottom: 100})}
  17.     ${rectPath({left: 150, top: 120, right: 250, bottom: 230})}
  18.     " stroke="black" fill="red" fill-rule="evenodd" vector-effect="non-scaling-stroke"/>
  19.  
  20.     <path d="
  21.     ${rectPath({left: 10, top: 220, right: 100, bottom: 300})}
  22.     ${rectPath({left: 20, top: 250, right: 150, bottom: 350})}
  23.     " stroke="white" fill="#64a7ff" fill-rule="nonzero" vector-effect="non-scaling-stroke"/>
  24.  
  25.     <path d="
  26.     ${rectPath({left: 350, top: 10, right: 450, bottom: 150})}
  27.     " fill="#2e9997" />
  28.   </svg>
  29.   <style>
  30.     svg, div, body, html {
  31.       overflow: visible; 
  32.       height: 100%; 
  33.       width: 100%;
  34.       margin: 0; padding: 0;
  35.     }
  36.   </style>
  37. `;

In this snippet rectPath creates a rectangle with “move to” and “line to” commands to be used in conjunction with SVG paths.

// javascript // paths // svg
snippet.zone /// {s/z}