Search code examples
javascriptcoordinate

JavaScript: Is it possible to create multiple canvases (varying size/using absolute coordinates) without "stretching" the coordinate system?


Currently, I am attempting to shadow out all of the screen except a specific element using rectangles drawn on multiple canvases. I want to block clicks on the window except for the non shadowed-out element. To do this, I tried creating four canvases each taking up a different part of the screen leaving the desired element unobstructed. To get the size of the element I am using getBoundingClientRect() and basing my shadowing rectangles off of this and scrollwidth/height. The first canvas+rectangle draws perfectly, after that the rest will not draw where I want them to (they appear at the bottom of the page) despite setting the position: absolute. I am wondering if it has something to do with the coordinate system being stretched when I set the height and width of each canvas.

Diagram of Desired Effect Here is my attempt to create the four canvases using JavaScript. I pass an object generated from getBoundingClientRect() with properities, top,left,right,bottom,height,width each corresponding to the element that is to remain unshadowed. Below code is from the file focusClick.js

function shadowOutScreen(boundingRect){
    var pageWidth = document.body.scrollWidth;
    var pageHeight = document.body.scrollHeight;
    //create four quadrants to cover all of screen except spotlight
//quadrant1: top strip
    var dynamicQuadrant1 = document.createElement("canvas");
    var body = document.getElementById("body");
    body.appendChild(dynamicQuadrant1);
    dynamicQuadrant1.setAttribute("width",pageWidth);
    dynamicQuadrant1.setAttribute("height",boundingRect.top);
    dynamicQuadrant1.style.pointerEvents = "auto";
    dynamicQuadrant1.style.zIndex = "1";
    dynamicQuadrant1.style.position = "absolute";
    dynamicQuadrant1.style.left = 0;
    dynamicQuadrant1.style.top = 0;
    dynamicQuadrant1.style.pointerEvents = "auto";
    var context = dynamicQuadrant1.getContext("2d");
    context.fillStyle = "rgba(38,38,38,.8)";
    context.fillRect(0,0,dynamicQuadrant1.width,dynamicQuadrant1.height);
//quadrant2: bottom strip
    var dynamicQuadrant2 = document.createElement("canvas");
    body.appendChild(dynamicQuadrant2);
    dynamicQuadrant2.setAttribute("width",pageWidth);
    dynamicQuadrant2.setAttribute("height",(pageHeight-boundingRect.bottom));
    dynamicQuadrant2.style.pointerEvents = "auto";
    dynamicQuadrant2.style.zIndex = "2";
    dynamicQuadrant2.style.position = "absolute";
    dynamicQuadrant2.style.left = 0;
    dynamicQuadrant2.style.top = boundingRect.bottom;
    dynamicQuadrant2.style.pointerEvents = "auto";
    var context2 = dynamicQuadrant2.getContext("2d");
    context2.fillStyle = "rgba(38,38,38,.8)";
    context2.fillRect(0,0,dynamicQuadrant2.width,dynamicQuadrant2.height);
//quadrant3: left side strip
    var dynamicQuadrant3 = document.createElement("canvas");
    body.appendChild(dynamicQuadrant3);
    dynamicQuadrant3.setAttribute("width",boundingRect.left); 
    dynamicQuadrant3.setAttribute("height",boundingRect.height);
    dynamicQuadrant3.style.pointerEvents = "auto";
    dynamicQuadrant3.style.position = "absolute";
    dynamicQuadrant3.style.left = 0;
    dynamicQuadrant3.style.top = boundingRect.top;
    dynamicQuadrant3.style.pointerEvents = "auto";
    var context3 = dynamicQuadrant3.getContext("2d");
    context3.fillStyle = "rgba(38,38,38,.8)";
    context3.fillRect(0,0,dynamicQuadrant3.width,dynamicQuadrant3.height); 

//quadrant4: right side strip
    var dynamicQuadrant4 = document.createElement("canvas");
    body.appendChild(dynamicQuadrant4);
    dynamicQuadrant4.setAttribute("width",pageWidth-boundingRect.right+9);
    dynamicQuadrant4.setAttribute("height",boundingRect.height);
    dynamicQuadrant4.style.pointerEvents = "auto";
    dynamicQuadrant4.style.position = "absolute";
    dynamicQuadrant4.style.left = boundingRect.right;
    dynamicQuadrant4.style.top = boundingRect.top;
    dynamicQuadrant4.style.pointerEvents = "auto";
    var context4 = dynamicQuadrant4.getContext("2d");
    context4.fillStyle = "rgba(38,38,38,.8)";
    context4.fillRect(0,0,dynamicQuadrant4.width,dynamicQuadrant4.height); 
}

Some Basic HTML that this can be run on. Contained in the file focusClick.html

<!DOCTYPE html>

<!-- HTML designed to create page to practice scrolling with -->


<html>
<head>
    <meta http-equiv="content-type" content="text/html" charset="utf-8" />  
    <link rel="stylesheet" href=pageStyle2.css>
</head>

<body id="body">
    <style>="position:absolute" </style>
    <!-- <canvas id="canvas"></canvas> -->
    <!-- <canvas id="canvas2"></canvas> -->

<!-- ************************************* -->
<!-- ************************************* -->
<!-- Div for page class -->
<!-- ************************************* -->

<div class="page">
<header>
   <h1>Page Navigation Demo: Jump to ID</h1>
</header>
<nav>
  <ul>
    <li><a href="javascript:spotlightLondon();">London</a></li>
    <li><a href="javascript:spotlightParis();">Paris</a></li>
    <li><a href="javascript:spotlightTokyo();">Tokyo</a></li>
    <li><a href="javascript:goToBottom();">Rock Bottom</a></li>
  </ul>
</nav>

<article>
  <h1>Three Cities</h1>
  <p>This page is a demonstration of how a guided page tour could be integrated into a Opti Pod Format.</p>
  <p>This is just a proof of concept and does not represent pod compatability. This is the simple scrolling version, future versions will integrated selective highlightening and designated ghosting protocols.</p>
</article>

<footer></footer>
</div>

<!-- ************************************* -->
<!-- ************************************* -->
<!-- Div for containerLond class -->
<!-- ************************************* -->

<div class="containerLond" id="London">

<nav>
  <ul>
    <li><a href="javascript:goToParis();">Paris</a></li>
    <li><a href="javascript:goToTokyo();">Tokyo</a></li>
    <li><a href="javascript:goToBottom();">Rock Bottom</a></li>
  </ul>
</nav>

<article id="LondonArticle">
  <h1>London</h1>
  <p>London is the capital city of England. It is the most populous city in the  United Kingdom, with a metropolitan area of over 13 million inhabitants.</p>
  <p>Standing on the River Thames, London has been a major settlement for two millennia, its history going back to its founding by the Romans, who named it Londinium.</p>
</article>

<footer></footer>

</div>

<!-- ************************************* -->
<!-- ************************************* -->
<!-- Div for containerParis class -->
<!-- ************************************* -->

<div class="containerParis" id="Paris">

<nav>
  <ul>
    <li><a href="javascript:goToLondon();">London</a></li>
    <li><a href="javascript:goToTokyo();">Tokyo</a></li>
    <li><a href="javascript:goToBottom();">Rock Bottom</a></li>
  </ul>
</nav>

<article>
  <h1>Paris</h1>
  <p>Paris is the capital city of France. It is a historic center of art and inovation in Europe with 2.24 million inhabitants.</p>
  <p>Paris is located in the north-bending arc of the river Seine. Paris was founded around the end of the 3rd century BC by the Gauls who were called Parisii. In 52 BC Julius Caesar's legions conquered the territory, founding the Roman city, Lutetia on the earlier settlement. </p>
</article>
<footer></footer>

</div>

<!-- ************************************* -->
<!-- ************************************* -->
<!-- Div for containerTokyo class -->
<!-- ************************************* -->

<div class="containerTokyo" id="Tokyo">
<nav>
  <ul>
    <li><a href="javascript:goToLondon();">London</a></li>
    <li><a href="javascript:goToParis();">Paris</a></li>
    <li><a href="javascript:goToBottom();">Rock Bottom</a></li>
  </ul>
</nav>

<article>
  <h1>Tokyo</h1>
  <p>Tokyo is the capital city of Japan. It is the largest city on the island and has a population of 9.273 million.</p>
  <p>. History tells that on February 11th 660 BC the first emperor of Japan (Jinmu) founded the nation. </p>
</article>
<footer></footer>
</div>

<!-- Item To Be added to bottom -->

<img src="Three_horizontal_stripes.svg.png" alt="Stripes" width=100%>

<div class="bottomOfPage" id="bottom">

<nav>
  <ul>
    <li><a href="javascript:goToLondon();">London</a></li>
    <li><a href="javascript:goToParis();">Paris</a></li>
    <li><a href="javascript:goToTokyo();">Tokyo</a></li>
  </ul>
</nav>
<article>
 <h1>Welcome To Rock Bottom</h1>
 <p>Rock bottom is located just south of bikini bottom, home to some odd language variants that are nearly impossible for outsiders to understand.</p>
</article>

</div>
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"></script>
<script src="focusClick.js"></script>
</body>
</html>

and some accompanying css to be put in styleSheet2.css file

/*Just added the body padding-bottom so scrolling is possible*/

body {
    padding-bottom: 2px;
    background-color: gray;
}

/*************************
page Section of Style Code
**************************/

.page {
    width: 100%;
    border: 1px solid slategray;
    background-color: gray;
}

header, footer {
    padding: 1em;
    color: white;
    background-color: black;
    clear: left;
    text-align: center;
}

nav {
    float: left;
    max-width: 160px;
    margin: 0;
    padding: 1eml
}

nav ul {
    list-style-type: none;
    padding: 0;
}

nav ul a {
    text-decoration: none;
}

.page > article {
    margin-left: 200px;
    border-left: 1px solid black;
    padding: 1em;
    overflow: hidden;
}




/****************************
London Section of Style Code
 ***************************/

.containerLond {
    width: 100%;
    border: 1px solid slategray;
    background-color: #7FBF3F;
}


.containerLond > article {
    margin-left: 200px;
    border-left: 1px solid black;
    padding: 1em;
    overflow: hidden;
}



/***************************
Paris Section of Style Code
***************************/

.containerParis {
    width: 100%;
    border: 1px solid slategray;
    background-color: #3F7FBF;

}


.containerParis > article {
    margin-left: 200px;
    border-left: 1px solid black;
    padding: 1em;
    overflow: hidden;
}

/*Tokyo Section of Style Code*/ 

.containerTokyo {
    width: 100%;
    border: 1px solid slategray;
    background-color: #BF3F3F;
}

.containerTokyo > article {
    margin-left: 200px;
    border-left: 1px solid black;
    padding: 1em;
    overflow: hidden;
}

.bottomOfPage {
    width: 100%;
    border: 1px solid slategray;
    background-color: #444444;
}

.bottomOfPage > article {
    margin-left: 200px;
    border-left: 1px solid black;
    color: black;
    padding: 1em;
    overflow: hidden;
}


.expose {
    position:relative;
}

#overlay {
    background:rgba(0,0,0,0.5);
    display:none;
    width:100%; 
    height:100%;
    position:absolute; 
    top:0; 
    left:0; 
    z-index:99998;
}

Solution

  • CSS style properties require units. Since getBoundingClientRect position properties are numbers (of pixels), you need to append "px" to them when setting style properties.

    For example (just one of many), changing

    dynamicQuadrant2.style.top = boundingRect.bottom;
    

    to

    dynamicQuadrant2.style.top = boundingRect.bottom; + "px";
    

    locates the top of the bottom shading element at the bottom of the article.

    (The top quadrant shader works because, effectively at least, units are not required for zero values).


    After solving the position of the shade elements problem, consider posting on Codereview for further feedback on ways to improve working code. For example the use of canvas elements instead of div elements is questionable (as mentioned in comments).

    To avoid down votes in the future, please review this help center article on How to create a Minimal, Complete, and Verifiable example, and this introductory article covering how to add code snippets that can be run in posts. Trying to run the question code provided above in the editor would have revealed that code for spotlightLondon was missing.