рд╣рдо рдлреНрд░рдВрдЯ-рдПрдВрдб рд▓рд┐рдЦрддреЗ рд╣реИрдВ рдЬреЛ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред рдпрд╛ рдХрд┐рд╕реА рдбреЗрд╡рд▓рдкрд░ рд╕реЗ рдЗрдВрдЬреАрдирд┐рдпрд░ рдХреИрд╕реЗ рдмрдиреЗред рднрд╛рдЧ 2

рдЗрд╕рд▓рд┐рдП, рджреВрд╕рд░реЗ рднрд╛рдЧ рдореЗрдВ рд╣рдо рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВред рдкрд╣рд▓реЗ, рдЪрд▓реЛ рдкреНрд░реМрджреНрдпреЛрдЧрд┐рдХреА рдкрд░ рдлреИрд╕рд▓рд╛ рдХрд░рддреЗ рд╣реИрдВред рдореИрдВ рд╡реЗрдм рдШрдЯрдХ рдЪреБрдирддрд╛ рд╣реВрдВред рдШрдЯрдХ рджреГрд╖реНрдЯрд┐рдХреЛрдг, рджреЗрд╢реА рдПрдкреАрдЖрдИ, рдкреБрди: рдЙрдкрдпреЛрдЧ рдФрд░ рдбрд┐рдмрдЧ рдХрд░рдирд╛ рдЖрд╕рд╛рди рд╣реИред

рдЪрд░рдг 1 - рдЕрдВрддрд┐рдо рдкрджреЛрдВ рдХрд╛ рд╡рд░реНрдгрди рдХрд░реЗрдВ


рд╣рдорд╛рд░реЗ рд╡рд░реНрдЪреБрдЕрд▓ рд╡реНрдпреВрдкреЛрд░реНрдЯ рдХреЗ рдЯреИрдЧ рдХреЛ рдХрд╕реНрдЯрдо-рд╡реНрдпреВрдкреЛрд░реНрдЯ рдХрд╣рд╛ рдЬрд╛рдПрдЧрд╛ред рдЗрд╕рд▓рд┐рдП, рдкрд╣рд▓реЗ рд╣рдо рд╡реНрдпреВрдкреЛрд░реНрдЯ рдХреЗ рд▓рд┐рдП рд╕рд╛рдорд╛рдиреНрдп рдЧреБрдгреЛрдВ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рддреЗ рд╣реИрдВ:

custom-viewport {
min-height: 50vh;
max-height: 100vh;
width: 100%;
position: absolute;
bottom: 0;
overflow: hidden;
transform-origin: 50% 100% 0;
}

рд╕реНрдерд┐рддрд┐ рдореЗрдВ рдкреНрд░рд╡реЗрд╢:

custom-viewport[data-mode = "inited"] {
transform: translateY(calc(100% - 50vh));
transition: transform 1s;
}

рдЦреЛрд▓рд╛ рд╕реНрдерд┐рддрд┐:

custom-viewport[data-mode = "opened"] {
transform: translateY(0);
transition: transform 1s;
overflow-y: scroll;
}

рд╣рдЯрд╛рдП рдЧрдП рд╕реНрдерд╛рди:

custom-viewport[data-mode = "deleted"] {
transform: translateY(100%);
transition: transform 1s;
}


рдЪрд░рдг 2 - рдХрд╕реНрдЯрдо-рд╡реНрдпреВрдкреЛрд░реНрдЯ рдШрдЯрдХ рд▓рд┐рдЦрдирд╛ рд╢реБрд░реВ рдХрд░реЗрдВ


class CustomViewport extends HTMLElement {
 constructor() {
  super();
 }
}

рд╣рдо рдбреНрд░реИрдЧрдЕрдк / рдбреНрд░реИрдЧрдбрд╛рдЙрди рдШрдЯрдирд╛рдУрдВ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рддреЗ рд╣реИрдВ

class CustomViewport extends HTMLElement {
 constructor() {
  super();
 }
 connectedCallback() {
  this.addEventListener("touchstart", ev => {
   this.firstTouch = ev.touches[0];
  });
  this.addEventListener("touchmove", ev => {
   this.deltaY = ev.touches[0].clientY - this.firstTouch.clientY;
   return this.deltaY > 0 ? this.dragDown(ev) : this.dragUp(ev);
  });
 }
 dragUp(ev) {}
 dragDown(ev) {}
}

рдпреЛрдЬрдирд╛рдмрджреНрдз рд░реВрдк рд╕реЗ, рдЙрдкрд░реЛрдХреНрдд рдХреЛрдб рдХреЛ рдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ рд╡рд░реНрдгрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред



рдЗрд╕рд▓рд┐рдП, рдЕрдм рд╣рдо рдбреНрд░реИрдЧрдЕрдк / рдбреНрд░реИрдЧрдбрд╛рдЙрди рдШрдЯрдирд╛рдУрдВ рдХреЗ рдмреАрдЪ рдЕрдВрддрд░ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдЕрдЧрд▓реА рдЙрдкрдпреЛрдЧрд┐рддрд╛ рдкрд╛рд╡рд░ рд░рд┐рдЬрд░реНрд╡ рдХреА рдЧрдгрдирд╛ рд╣реИред

class CustomViewport extends HTMLElement {
 constructor() {
  super();
  this.VIEWPORT_HEIGHT = window.innerHeight; // +
 }
 connectedCallback() {
  this.addEventListener("touchstart", ev => {
   this.firstTouch = ev.touches[0];
   const rect = this.getBoundingClientRect(); // +
   const { height, top } = rect; // +
   this.bottomOffsetBeforeDragging = (height + top) - this.VIEWPORT_HEIGHT; // +
  });
  this.addEventListener("touchmove", ev => {
   this.deltaY = ev.touches[0].clientY - this.firstTouch.clientY;
   return this.deltaY > 0 ? this.dragDown() : this.dragUp();
  });
 }
 dragUp() {}
 dragDown() {}
 isBottomOffset() { // +
   return (this.bottomOffsetBeforeDragging + this.deltaY) > 0; // +
 } // +
}

рдпрд╣рд╛рдВ рд╣рдореЗрдВ рдкрд╣рд▓реЗ рдпрд╛рдж рд╣реИ рдХрд┐ рдЖрдВрджреЛрд▓рди рд╢реБрд░реВ рд╣реЛрдиреЗ рдХреЗ рд╕рдордп рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдХрд┐рддрдирд╛ рдмрд┐рдЬрд▓реА рдЖрд░рдХреНрд╖рд┐рдд рдерд╛, рдФрд░ рдлрд┐рд░ рдмрд╕ рдЗрд╕ рдореВрд▓реНрдп рдореЗрдВ рдбреЗрд▓реНрдЯрд╛ рдЬреЛрдбрд╝реЗрдВ рдФрд░ рджреЗрдЦреЗрдВ рдХрд┐ рд╣рдо рдЖрдЧреЗ рдмрдврд╝ рд╕рдХрддреЗ рд╣реИрдВ рдпрд╛ рдирд╣реАрдВред

рджрд░рдЕрд╕рд▓ рд▓реЙрдЬрд┐рдХ рдбреНрд░реИрдЧрдЕрдк:

...
dragUp() {
 if(this.isBottomOffset()) {
  //  
  return;
 }
 this.style.transform = 'translateY(0)';
}
...

рд╣рдо рдПрдХ рддрд░реАрдХрд╛ рд▓рд┐рдЦрддреЗ рд╣реИрдВ рдЬреЛ рд╡реНрдпреВрдкреЛрд░реНрдЯ рдХреЛ рдЖрдЧреЗ рдмрдврд╝рд╛рдПрдЧрд╛:

class CustomViewport extends HTMLElement {
 constructor() {
  super();
  this.VIEWPORT_HEIGHT = window.innerHeight;
 }
 connectedCallback() {
  this.addEventListener("touchstart", ev => {
   this.firstTouch = ev.touches[0];
   const rect = this.getBoundingClientRect();
   const { height, top } = rect;
   this.bottomOffsetBeforeDragging = (height + top) - this.VIEWPORT_HEIGHT;
   this.lastPosY = this.bottomOffsetBeforeDragging - this.scrollTop; // +
  });
   ...
 }
translateY() { // +
  const pixels = this.deltaY + this.lastPosY; // +
  this.style.transform = `translateY(${pixels}px)`; // +
  this.style.transition = 'none'; // +
 } // +
...
}

рдЖрдЗрдП рд╣рдо рдЗрд╕ рдмрд╛рд░реЗ рдореЗрдВ рдЕрдзрд┐рдХ рд╡рд┐рд╕реНрддрд╛рд░ рд╕реЗ рдЬрд╛рдВрдЪ рдХрд░реЗрдВ рдХрд┐ рдпрд╣ рдХреНрдпрд╛ рд╣реИред рд░рд┐рдЬрд╝рд▓реЛрд╕рдкреА рдпрд╣ рдХреИрд╕реЗ рд╣реЛрддрд╛ рд╣реИ рдФрд░ рдЗрд╕рдХреА рдЧрдгрдирд╛ рдХреИрд╕реЗ рдХреА рдЬрд╛рддреА рд╣реИред рдЕрдЧрд░ рд╕реАрдПрд╕рдПрд╕ рдореЗрдВ рд╣рдордиреЗ рдЯреНрд░рд╛рдВрд╕рдлреЙрд░реНрдо рд▓рд┐рдЦрд╛: рдЯреНрд░рд╛рдВрд╕рд▓реА (рдХреИрд▓реНрдХ (100% - 50 рд╡реАрдПрдЪ)); рдЬрд╣рд╛рдВ 100% рд╡рд░реНрдЪреБрдЕрд▓ рд╡реНрдпреВрдкреЛрд░реНрдЯ рдХреА рдКрдВрдЪрд╛рдИ рд╣реИ, рдФрд░ 50vh рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд╡реНрдпреВрдкреЛрд░реНрдЯ рдХреА рдКрдВрдЪрд╛рдИ рд╕реЗ рдЖрдзреА рд╣реИ, рдФрд░ рдпрд╣ рд╕реНрдерд┐рддрд┐ рдХреЗ рд╕реНрдереИрддрд┐рдХ рд╡рд┐рд╡рд░рдг рдХреЗ рд▓рд┐рдП рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ рдлрд┐рдЯ рдмреИрдарддрд╛ рд╣реИ, рддреЛ рдЧрддрд┐рд╢реАрд▓рддрд╛ рдореЗрдВ рдЖрдВрджреЛрд▓рди рдХреА рдЧрдгрдирд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдирд┐рд░рдкреЗрдХреНрд╖ рдореВрд▓реНрдпреЛрдВ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдирд╛ рдЕрдзрд┐рдХ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╣реИ, рд╣рдо рдЗрди рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреЛ рдпрд╣рд╛рдВ рдХрд░ рд░рд╣реЗ рд╣реИрдВред

рддреЛ рдпрд╣ред рд░рд┐рдЬрд╝реЙрд░реНрдЯ рд╢реБрд░реВ рд╣реЛрдиреЗ рдкрд░ рдкрд┐рдХреНрд╕рд▓реНрд╕ рдореЗрдВ рд╡рд░реНрдЪреБрдЕрд▓ рд╡реНрдпреВрдкреЛрд░реНрдЯ рдХреЗ рдореВрд╡рдореЗрдВрдЯ рдХреА рдорд╛рддреНрд░рд╛ рд╣реЛрддреА рд╣реИ, рд╣рдо рдЗрд╕.deltaY рдХреЛ рдЗрд╕рдореЗрдВ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВ рдФрд░ рдирдпрд╛ рд╡реНрдпреВрдкреЛрд░реНрдЯ рдкреЛрдЬрд┐рд╢рди рдкреНрд░рд╛рдкреНрдд рдХрд░рддреЗ рд╣реИрдВред

рдЬрдм рд╕реЗ рд╣рдордиреЗ рдЧреБрдгреЛрдВ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд┐рдпрд╛ рд╣реИ:

bottom: 0;
transform-origin: 50% 100% 0;

рддрдм рд╡реНрдпреВрдкреЛрд░реНрдЯ рдХреА рдЧрддрд┐ рдХреЛ рдЧрд┐рдирдиреЗ рдХреЗ рд▓рд┐рдП рд╣рдорд╛рд░реА рд╕рдордиреНрд╡рдп рдкреНрд░рдгрд╛рд▓реА рдЙрд╕ рд░реВрдк рдХреЛ рд▓реЗ



рд▓реЗрдЧреА рдЬрд┐рд╕рдХрд╛ рд╣рдо рдбреНрд░реИрдЧрдбрд╛рдЙрди рд╡рд░реНрдгрди рдХрд░рддреЗ рд╣реИрдВ:

...
dragDown() {
 if(this.lastPosY < 0) {
  return;
 }
 this.translateY();
}
...

рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдбреНрд░реИрдЧ рдПрдВрдб рдЗрд╡реЗрдВрдЯ:

class CustomViewport extends HTMLElement {
 constructor() {
  super();
  this.VIEWPORT_HEIGHT = window.innerHeight;
 }
 connectedCallback() {
  this.addEventListener("touchend", ev => { // +
   const { mode: currentMode } = this.dataset; // +
   this.style = null; // +
   if (Math.abs(deltaY) < 10) { // +
    this.dataset.mode = currentMode; // +
    return; // +
   } // +
    if (deltaY > 0) { // +
     if (currentMode === "inited") { // +
       this.dataset.mode = "deleted"; // +
       return; // +
      } // +
      this.dataset.mode = "inited"; // +
      return; // +
    } // +
    this.dataset.mode = "opened"; // +
  }); // +
   ...

рдЕрдЧрд░ (Math.abs (рдбреЗрд▓реНрдЯрд╛) <10) рд▓рд╛рдЗрди рдореЗрдВ рд╣рдо рд╕рдВрдХреЗрдд рджреЗрддреЗ рд╣реИрдВ рдХрд┐ рдпрджрд┐ рдЖрдк 10 рдкрд┐рдХреНрд╕реЗрд▓ рд╕реЗ рдХрдо рдЪрд▓реЗ рдЧрдП рд╣реИрдВ, рддреЛ рд╡рд░реНрддрдорд╛рди рд╕реНрдерд┐рддрд┐ рдЫреЛрдбрд╝ рджреЗрдВред

рдирддреАрдЬрддрди, рд╣рдореЗрдВ рдПрдХ рдШрдЯрдХ рдЬреИрд╕рд╛ рдорд┐рд▓рдирд╛ рдЪрд╛рд╣рд┐рдП


class CustomViewport extends HTMLElement {
 constructor() {
  super();
  this.VIEWPORT_HEIGHT = window.innerHeight;
 }
 connectedCallback() {
  this.addEventListener("touchstart", ev => {
   this.firstTouch = ev.touches[0];
   const rect = this.getBoundingClientRect();
   const { height, top } = rect;
   this.bottomOffsetBeforeDragging = (height + top) - this.VIEWPORT_HEIGHT;
   this.lastPosY = this.bottomOffsetBeforeDragging - this.scrollTop;
  });
  this.addEventListener("touchmove", ev => {
   this.deltaY = ev.touches[0].clientY - this.firstTouch.clientY;
   return this.deltaY > 0 ? this.dragDown() : this.dragUp();
  });
  this.addEventListener("touchend", ev => {
   const { mode: currentMode } = this.dataset;
   this.style = null;
   if (Math.abs(this.deltaY) < 10) {
    this.dataset.mode = currentMode;
    return;
   }
    if (this.deltaY > 0) {
     if (currentMode === "inited") {
       this.dataset.mode = "deleted";
       return;
      }
      this.dataset.mode = "inited";
      return;
    }
    this.dataset.mode = "opened";
  });
 }
 dragUp() {
  if(this.isBottomOffset()) {
   this.translateY();
   return;
  }
  this.style.transform = 'translateY(0)';
}
 dragDown() {
   if(this.lastPosY < 0) {
   return;
  }
  this.translateY();
}
translateY() {
  const pixels = this.deltaY + this.lastPosY;
  this.style.transform = `translateY(${pixels}px)`;
  this.style.transition = 'none';
 }
 isBottomOffset() {
   return (this.bottomOffsetBeforeDragging + this.deltaY) > 0;
 }
}

customElements.define('custom-viewport', CustomViewport);

рдпрд╣ рдХреЛрдб рдкреВрд░реНрдг рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдирд╣реАрдВ рд╣реИ, рдмрд▓реНрдХрд┐ рдХреЗрд╡рд▓ рдПрдХ рдкреНрд░реЛрдЯреЛрдЯрд╛рдЗрдк рд╣реИред рд╕реНрдХреНрд░реЙрд▓ рдХрд╛ рдЕрдзрд┐рдХ рд╡рд┐рд╕реНрддреГрдд рдЕрдзреНрдпрдпрди, рдмрд╣рд╕, рдХрд┐рд╕реА рднреА рдЕрдиреНрдп рдЕрдиреБрдХреВрд▓рди, рдЯрдЪрд╕реНрдХреНрд░реАрди - рдкрд╛рдардХ рдХреЗ рд▓рд┐рдП рдЫреЛрдбрд╝ рджрд┐рдпрд╛ рдЧрдпрд╛ред

All Articles