Sliding up and down Menu with Rope

Written by @kerixa 13 May 2020

Have you ever noticed that the menus can be slide down by a rope like real screens in the cinema? It is very interesting to put such menus in your website, isn't it?

Code Snippet:

                                                
                                                <!-- this script is provided by https://www.javascriptfreecode.com coded by: Kerixa Inc. -->
<style>
html{
	height: 100%;
}

body{
	font-family: Arial;
	margin: 0px;
	height: 100%;
	--color: #2196F3;
	--bgColor: #424242;
	color: var(--color);
	background-color: var(--bgColor);
}

*{
	-webkit-touch-callout: none; /* iOS Safari */
		  -webkit-user-select: none; /* Safari */
		   -khtml-user-select: none; /* Konqueror HTML */
			 -moz-user-select: none; /* Old versions of Firefox */
			  -ms-user-select: none; /* Internet Explorer/Edge */
				  user-select: none;
}

#nav{
	position: fixed;
	transform: translateY(-70vh);
	overflow: visible;
}

canvas{
	display: none;
}

#nav > #content{
	box-sizing: border-box;
	margin: 0px;
	padding: 20vh 10px 10vh 10px;
	height: 70vh;
	box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
	background-color: #616161;
}

#nav > #content > h1{
	color: #eee;
	margin: 0px;
	text-align: center;
}

#nav > #content > div{
	margin: 30px auto;
	display: flex;
	justify-content: center;
	align-items: center;
}

#nav > #content a{
	margin: 10px;
	font-size: 20px;
}

#nav > #rope{
	width: 100vw;
	height: 100vh;
	display: block;
}

#nav > #rope > circle{
	fill: var(--bgColor);
	stroke: var(--color);
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
}

#nav > #rope > path{
	stroke: var(--color);
}

#page{
	width: 100%;
	height: 100%;
	display: flex;
	justify-content: center;
	align-items: center;
	color: var(--textColor);
}

#page > h1{
	font-size: 50px;
}
</style>

<div id="nav">
<div id="content">
<h1>Menu & Navigation</h1>
<div>
<a data-color="#2196F3" style="color: #2196F3" href="">Blue Page</a>
<a data-color="#F44336" style="color: #F44336" href="">Red Page</a>
<a data-color="#8BC34A" style="color: #8BC34A" href="">Green Page</a>
<a data-color="#FF9800" style="color: #FF9800" href="">Orange Page</a>
</div>
</div>
<svg id="rope" xmlns="http://www.w3.org/2000/svg">
<path fill="transparent" stroke-width="10px" stroke-linejoin="round" />
<circle stroke-width="10px" r="20" id="handle"></circle>
</svg>
</div>
<div id="page">
<h1>Pull</h1>
</div>

<script src='https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.14.2/matter.min.js'></script>
<script id="rendered-js">
class PhysicsEngine {
  constructor() {
    this.navclosedPos = -30;
    this.navHeight = 50;
    this.canvasWidth = 500;
    this.canvasHeight = 600;
    this.initWorld();
  }

  initWorld() {
    const engine = Matter.Engine.create();
    const render = Matter.Render.create({
      element: document.body,
      engine: engine });


    let bodies = this.createBodies();
    let constraints = this.createConstraints();
    Matter.World.add(engine.world, [...bodies, ...constraints]);

    Matter.Engine.run(engine);
    Matter.Render.run(render);
  }

  createBodies() {
    this.nav = Matter.Bodies.rectangle(this.canvasWidth / 2, this.navclosedPos, this.canvasWidth, this.navHeight, { isSensor: true, inertia: Infinity, mass: 0.1 });
    this.rope = this.createRope();
    this.handle = this.rope.bodies[this.rope.bodies.length - 1];
    return [this.nav, this.rope];
  }

  createRope() {
    const ropeParts = Matter.Body.nextGroup(true);
    const rope = Matter.Composites.stack(this.canvasWidth / 2, this.navclosedPos, 8, 1, -30, 0, (x, y) => {
      return Matter.Bodies.circle(x, y, 15, {
        collisionFilter: { group: ropeParts } });

    });

    Matter.Composites.chain(rope, 0, 0.2, 0, -0.2, { stiffness: 1, damping: 0.6, length: 3 });
    return rope;
  }

  createConstraints() {
    this.fixMenuToTop = Matter.Constraint.create({
      bodyA: this.nav,
      pointA: { x: 0, y: this.navHeight / 2 },
      pointB: { x: this.canvasWidth / 2, y: this.navclosedPos },
      stiffness: 0.5,
      damping: 0.1,
      length: 0 });


    this.fixMenuToBottom = Matter.Constraint.create({
      bodyA: this.nav,
      pointA: { x: 0, y: this.navHeight / 2 },
      pointB: { x: this.canvasWidth / 2, y: 0 },
      stiffness: 0.01,
      damping: 0.1,
      length: 0 });


    const fixRopeToMenu = Matter.Constraint.create({
      bodyA: this.nav,
      pointA: { x: 0, y: this.navHeight - 20 },
      bodyB: this.rope.bodies[0],
      stiffness: 1,
      length: 0 });


    this.fixMouseToHandle = Matter.Constraint.create({
      bodyA: this.handle,
      pointB: { x: 0, y: 0 },
      stiffness: 0.000000000000001,
      length: 0 });


    return [this.fixMenuToTop, this.fixMenuToBottom, fixRopeToMenu, this.fixMouseToHandle];
  }

  grabHandle(x, y) {
    this.moveHandle(x, y);
    this.fixMouseToHandle.stiffness = 1;
  }

  moveHandle(x, y) {
    this.fixMouseToHandle.pointB.x = x;
    this.fixMouseToHandle.pointB.y = y;
  }

  releaseHandle() {
    this.fixMouseToHandle.stiffness = 0.000000000000001;
  }}


class Nav {
  constructor() {
    this.physicsEngine = new PhysicsEngine();
    this.canvasWidth = this.physicsEngine.canvasWidth;
    this.canvasHeight = this.physicsEngine.canvasHeight;

    this.navElm = document.getElementById('content');
    this.ropeContainer = document.getElementById('rope');
    this.ropeElm = this.ropeContainer.querySelector('path');
    this.handleElm = document.getElementById('handle');
    this.pullText = document.querySelector('#page > h1');
    for (let link of Array.from(document.querySelectorAll('a'))) {
      link.addEventListener('click', e => {
        e.preventDefault();
        this.navigateTo(link.getAttribute('data-color'));
      });
    }

    this.handleElm.addEventListener('mousedown', this.grab.bind(this));
    document.body.addEventListener('mousemove', this.move.bind(this));
    document.body.addEventListener('mouseup', this.release.bind(this));

    this.handleElm.addEventListener('touchstart', this.grab.bind(this));
    document.body.addEventListener('touchmove', this.move.bind(this), { passive: false });
    document.body.addEventListener('touchend', this.release.bind(this));

    this.grabbed = false;
    this.isOpen = false;
    this.shouldOpen = false;
    this.inTransition = false;

    this.onResize();
    window.addEventListener('resize', this.onResize.bind(this));

    this.render();
  }

  onResize() {
    this.width = window.innerWidth;
    this.height = window.innerHeight;
    this.ropeContainer.setAttribute('viewport', `0 0 ${this.width} ${this.height}`);
    this.navOpenPos = this.height / 10 * 6;
    this.physicsEngine.fixMenuToBottom.pointB.y = this.getCanvasY(this.navOpenPos);
  }

  grab(e) {
    let x = e.clientX || e.touches[0].clientX;
    let y = e.clientY || e.touches[0].clientY;
    this.grabbed = true;
    this.physicsEngine.grabHandle(this.getCanvasX(x), this.getCanvasY(y));
    this.inTransition = false;
    console.log(this.grabbed);
  }

  move(e) {
    e.preventDefault();

    let x = e.clientX || e.touches[0].clientX;
    let y = e.clientY || e.touches[0].clientY;
    let navPos = this.getScreenY(this.physicsEngine.nav.position.y + this.physicsEngine.navHeight / 2);
    if (navPos >= 40 && !this.isOpen && !this.inTransition) {
      this.release();
      this.open();
    } else
    if ((navPos >= this.navOpenPos + 25 || navPos <= this.navOpenPos - 10) && this.isOpen && !this.inTransition) {
      this.release();
      this.close();

    } else
    if (this.grabbed)
    this.physicsEngine.moveHandle(this.getCanvasX(x), this.getCanvasY(y));
  }

  release() {
    if (this.grabbed) {
      this.physicsEngine.releaseHandle();
      this.grabbed = false;
    }
  }

  open() {
    this.shouldOpen = true;
    this.inTransition = true;
  }

  close() {
    this.shouldOpen = false;
    this.inTransition = true;
  }

  render() {
    window.requestAnimationFrame(this.render.bind(this));

    if (this.shouldOpen && !this.isOpen) {
      if (this.physicsEngine.fixMenuToTop.stiffness >= 0.01) {
        this.physicsEngine.fixMenuToTop.stiffness -= 0.02;
        this.physicsEngine.fixMenuToBottom.stiffness += 0.02;
      } else

      this.isOpen = true;
    }

    if (!this.shouldOpen && this.isOpen) {
      if (this.physicsEngine.fixMenuToTop.stiffness <= 0.5) {
        this.physicsEngine.fixMenuToTop.stiffness += 0.03;
        this.physicsEngine.fixMenuToBottom.stiffness -= 0.03;
      } else

      this.isOpen = false;
    }

    let path = `M ${this.width / 2} ${this.getScreenY(this.physicsEngine.nav.position.y)}`;

    for (let body of this.physicsEngine.rope.bodies) {
      path += `L ${this.getScreenX(body.position.x)} ${this.getScreenY(body.position.y)}`;
    }

    let lastBody = this.physicsEngine.rope.bodies[this.physicsEngine.rope.bodies.length - 1];
    this.handleElm.setAttribute('cx', this.getScreenX(lastBody.position.x));
    this.handleElm.setAttribute('cy', this.getScreenY(lastBody.position.y));

    this.ropeElm.setAttribute('d', path);
    //console.log(this.physicsEngine.nav.position.y);
    this.navElm.style.transform = `translate(${0}px, ${this.getScreenY(this.physicsEngine.nav.position.y + this.physicsEngine.navHeight / 2)}px)`;
  }

  getScreenX(canvasX) {
    return canvasX / this.canvasWidth * this.width;
  }

  getScreenY(canvasY) {
    return canvasY / this.canvasHeight * this.height;
  }

  getCanvasX(screenX) {
    return screenX / this.width * this.canvasWidth;
  }

  getCanvasY(screenY) {
    return screenY / this.height * this.canvasHeight;
  }

  navigateTo(page) {
    document.body.style.setProperty('--color', page);
    this.close();
  }}


const nav = new Nav();
//# sourceURL=pen.js
    </script><a target='_blank' href='https://www.javascriptfreecode.com' style='font-size: 8pt; text-decoration: none'>JavaScript Best Codes</a>                                                
                                            

Example:


About @kerixa

I am Krishna Eydat. I studied Software Engineering at University of Waterloo in Canada. I lead a few tech companies. I am passionate about the way we are connected. I would like to be part of something big or be the big deal!

K

Comments


Here you can leave us commments. Let us know what you think about this code tutorial!

0 / 300

TRENDING POST
1
2
3
4
5
VISITORS
Online Users: 12
Recent Members: admin_js, bloxio, yqaice, flooketsu, phuang_test
advertisement 2