Skip to content

Instantly share code, notes, and snippets.

@landsurveyorsunited
Created January 12, 2022 22:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save landsurveyorsunited/140fe14213227d182ceb755fac01acc2 to your computer and use it in GitHub Desktop.
Save landsurveyorsunited/140fe14213227d182ceb755fac01acc2 to your computer and use it in GitHub Desktop.
Tape Measure Scrollbar
<main>
<section id="section-1">
<div class="main-title">
<center><table>
<tbody>
<tr>
<td><details class="modal"><summary>🧠</summary><div> <a href="#contact" target="load">🧠</a><div class="frameContainer"></div></details></td>
<td><details class="modal"><summary>Sign In</summary><div> <a href="#contact" target="load">♻️</a><div class="frameContainer"></div></details></td>
</tr>
</tbody>
</table></center>
<h1>Land Surveyors United</h1><h2>Next Generation Land Surveying Improvement Community</h2>
</div>
<a class="nav-anchor bottom" href="#section-2"><span class="mdi mdi-chevron-down"></span></a>
</section>
<section id="section-2">
<a class="nav-anchor top" href="#section-1"><span class="mdi mdi-chevron-up"></span></a>
<div class="main-title">
<center><table>
<tbody>
<tr>
<td><details class="modal"><summary>Survey Jobs</summary><div> <a href="https://jobs.landsurveyorsunited.org" target="load">Land Surveying Jobs</a><div class='embed-container'><iframe src='https://landsurveyorsunited.com/jobs' style='border:0'></iframe></div><div><div class="frameContainer"></div></div></details></td>
<td><details class="modal"><summary>Equipment</summary><div> <a href="#contact" target="load">Land Surveying Equipment</a><div class='embed-container'><iframe src='https://landsurveyorsunited.com/megashop' style='border:0'></iframe></div><div class="frameContainer"></div></details></td>
<td><details class="modal"><summary>Mentorship</summary><div> <a href="/mentorship" target="load">Mentorship in Land Surveying</a><div class='embed-container'><iframe src='https://landsurveyorsunited.com/mentorship/board' style='border:0'></iframe></div><div class="frameContainer"></div></details></td>
<td><details class="modal"><summary>Forums</summary><div> <a href="/forum/topics" target="load">Forums</a><div class='embed-container'><iframe src='https://landsurveyorsunited.com/forum/topics' style='border:0'></iframe></div><div class="frameContainer"></div></details></td>
<td><details class="modal"><summary>Directory</summary><div> <a href="#contact" target="load">💹</a><div class="frameContainer"></div></details></td>
<td><details class="modal"><summary>Resources</summary><div> <a href="#contact" target="load">🏖️</a><div class="frameContainer"></div></details></td>
<td><details class="modal"><summary>Events</summary><div> <a href="#contact" target="load">🍛</a><div class="frameContainer"></div></details></td>
<td><details class="modal"><summary>Articles</summary><div> <a href="#contact" target="load">☀️</a><div class="frameContainer"></div></details></td>
<td><details class="modal"><summary>💧</summary><div> <a href="#contact" target="load">💧</a><div class="frameContainer"></div></details></td>
<td><details class="modal"><summary>♻️</summary><div> <a href="#contact" target="load">♻️</a><div class="frameContainer"></div></details></td>
</tr>
</tbody>
</table></center>
<!-- DivTable.com -->
</div>
<a class="nav-anchor bottom" href="#section-3"><span class="mdi mdi-chevron-down"></span></a>
</section>
<section id="section-3">
<a class="nav-anchor top" href="#section-2"><span class="mdi mdi-chevron-up"></span></a>
<div class="main-title">
<center><table>
<tbody>
<tr>
<td><details class="modal"><summary>Events</summary><div> <a href="#contact" target="load">🍛</a><div class="frameContainer"></div></details></td>
<td><details class="modal"><summary>Articles</summary><div> <a href="#contact" target="load">☀️</a><div class="frameContainer"></div></details></td>
<td><details class="modal"><summary>💧</summary><div> <a href="#contact" target="load">💧</a><div class="frameContainer"></div></details></td>
<td><details class="modal"><summary>♻️</summary><div> <a href="#contact" target="load">♻️</a><div class="frameContainer"></div></details></td>
</tr>
</tbody>
</table></center>
<small>(Resize the page)</small>
</div>
<a class="nav-anchor bottom" href="#section-4"><span class="mdi mdi-chevron-down"></span></a>
</section>
<section id="section-4">
<a class="nav-anchor top" href="#section-3"><span class="mdi mdi-chevron-up"></span></a>
<div class="main-title">
<center><table>
<tbody>
<tr>
<td><details class="modal"><summary>🧠</summary><div> <a href="#contact" target="load">🧠</a><div class="frameContainer"></div></details></td>
<td><details class="modal"><summary>Sign In</summary><div> <a href="#contact" target="load">♻️</a><div class="frameContainer"></div></details></td>
</tr>
</tbody>
</table></center>
</div>
</section>
</main>
<div id="progress">
<div id="tape-body"></div>
<div id="tape-measure"></div>
</div>
const tapeBody = document.getElementById("tape-body");
const tapeMeasure = document.getElementById("tape-measure");
const main = document.getElementsByTagName('main')[0];
const touch = "ontouchstart" in window ||
(window.DocumentTouch && document instanceof DocumentTouch);
let offsetY = 0;
const cache = {
viewport: {},
rects: [],
node: {}
};
// init
window.addEventListener("load", init);
function init() {
// update the cache and check scroll position
recache();
// hide the scrollbar
const barWidth = getScrollBarWidth();
main.style.paddingRight = `${barWidth}px`;
// allow dragging
tapeBody.addEventListener(touch ? "touchstart" : "mousedown", down);
// throttle the scroll callback for performance
main.addEventListener("scroll", touch ? scrollCheck : throttle(scrollCheck, 10));
// debounce the resize callback for performance
window.addEventListener("resize", debounce(recache, 50));
};
const move = (e) => {
const x = (touch ? e.touches[0].pageY : e.pageY) - offsetY;
const offset = getScrollOffset();
const height = cache.document.height - cache.viewport.height;
const width = cache.viewport.height - cache.node.height;
const progress = (x / width) * height;
main.scrollTop = progress;
}
const up = (e) => {
main.style.scrollBehavior = "";
if (touch) {
document.removeEventListener("touchmove", move);
document.removeEventListener("touchend", up);
document.removeEventListener("touchcancel", up);
} else {
document.removeEventListener("mousemove", move);
document.removeEventListener("mouseup", up);
}
}
const down = (e) => {
recache();
offsetY = (touch ? e.touches[0].pageY : e.pageY) - cache.node.top;
// setting scrollTop/Left with "scroll-behavior: smooth" causes monster jank
main.style.scrollBehavior = "auto";
if (touch) {
document.addEventListener("touchmove", move);
document.addEventListener("touchend", up);
document.addEventListener("touchcancel", up);
} else {
document.addEventListener("mousemove", move);
document.addEventListener("mouseup", up);
}
e.preventDefault();
}
// update the cache and check scroll position
function recache() {
// cache the viewport dimensions
cache.viewport = {
width: window.innerWidth,
height: window.innerHeight
};
cache.document = {
height: main.scrollHeight,
width: main.scrollWidth,
};
cache.node = tapeBody.getBoundingClientRect();
scrollCheck();
}
// check whether a node is at or above the horizontal halfway mark
function scrollCheck() {
const offset = getScrollOffset();
const height = cache.document.height - cache.viewport.height;
const width = cache.viewport.height - cache.node.height;
const progress = (offset.y / height) * width;
tapeBody.style.transform = `translate3d(0, ${progress}px,0)`;
tapeMeasure.style.height = `${progress}px`;
};
// get the scroll offsets
function getScrollOffset() {
return {
x: main.scrollLeft,
y: main.scrollTop
};
};
// throttler
function throttle(fn, limit, context) {
let wait;
return function() {
context = context || this;
if (!wait) {
fn.apply(context, arguments);
wait = true;
return setTimeout(function() {
wait = false;
}, limit);
}
};
};
// debouncer
function debounce(fn, limit, u) {
let e;
return function() {
const i = this;
const o = arguments;
const a = u && !e;
clearTimeout(e),
(e = setTimeout(function() {
(e = null), u || fn.apply(i, o);
}, limit)),
a && fn.apply(i, o);
};
}
/**
* Get native scrollbar width
* @return {Number} Scrollbar width
*/
function getScrollBarWidth() {
const db = document.body;
const div = document.createElement("div");
let t = 0;
return div.style.cssText = "width: 100; height: 100; overflow: scroll; position: absolute; top: -9999;", document.body.appendChild(div), t = div.offsetWidth - div.clientWidth, document.body.removeChild(div), t
};
$url: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/86186/";
body {
overflow: hidden;
width: 100vw;
height: 100vh;
margin: 0;
}
main {
width: 100vw;
height: 100vh;
overflow-x: hidden;
overflow-y: scroll;
box-sizing: content-box;
scroll-behavior: smooth;
section {
position: relative;
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background-size: cover;
background-position: center center;
&:nth-child(1) {
background-image: url("https://storage.ning.com/topology/rest/1.0/file/get/8759407491?profile=original");
.nav-anchor {
animation: 1000ms ease 0ms forwards infinite notice-me;
}
}
&:nth-child(2) {
background-image: url("https://storage.ning.com/topology/rest/1.0/file/get/9992470700?profile=original");
.main-title {
color: rgba(255, 255, 255, .85);
}
}
&:nth-child(3) {
background-image: url("https://storage.ning.com/topology/rest/1.0/file/get/9992471896?profile=original");
}
&:nth-child(4) {
background-image: url("https://storage.ning.com/topology/rest/1.0/file/get/9992472455?profile=original");
.main-title {
color: rgba(255, 255, 255, .85);
}
}
}
}
.main-title {
font-size: 1.5vw;
font-family: "Alfa Slab One";
color: rgba(26, 26, 26, .85);
text-align: center;
}
h1 small {
font-size: 15px;
}
h2 {font-size:11px}
.nav-anchor {
position: absolute;
color: #fff;
font-size: 50px;
&.top {
top: 10px;
}
&.bottom {
bottom: 10px;
}
}
#progress {
position: fixed;
bottom: 0;
right: 0;
width: 50px;
height: 100%;
}
#tape-body {
background-image: url("#{$url}tape2.png");
position: absolute;
right: 0;
top: 0;
width: 50px;
height: 76px;
background-size: cover;
z-index: 10;
cursor: grab;
}
#tape-measure {
position: absolute;
right: 12px;
top: 0;
height: 0;
width: 24px;
background-color: #fada34;
z-index: 9;
background-image: url("#{$url}tape2.jpg");
background-size: 100% auto;
}
@keyframes notice-me {
0%, 100% {
transform: translate3d(0,-5px,0);
}
50% {
transform: translate3d(0,5px,0);
}
}
:root {
--gutter-horizontal: 10vw;
--gutter-vertical: 10vh;
}
img {
max-width: 100%;
}
details + details {
margin-top: 1em;
}
details.modal summary {
background: #fff;
color:#000066;
border: 2px solid black;
cursor: pointer;
display: inline-block;
padding: .25em .5em;
}
details.modal summary::-webkit-details-marker {
display: none;
}
details.modal summary::before {
content: "";
display: inline-block;
padding-right: .25em;
}
details[open].modal summary::before {
content: "Close";
}
details[open].modal {
overflow: auto;
padding: 1em;
position: fixed;
z-index: 10;
}
details[open].modal,
details[open].modal::after {
bottom: 10vh;
bottom: var(--gutter-vertical);
left: 10vw;
left: var(--gutter-horizontal);
right: 10vw;
right: var(--gutter-horizontal);
top: 10vh;
top: var(--gutter-vertical);
}
details[open].modal::before,
details[open].modal::after {
content: "";
position: fixed;
z-index: -1;
}
details[open].modal::before {
background: rgba(0, 0, 0, .75);
bottom: 0;
left: 0;
right: 0;
top: 0;
}
details[open].modal::after {
background: #fff;
}
.embed-container { position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; max-width: 100%; } .embed-container iframe, .embed-container object, .embed-container embed { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
.frameContainer{
position: fixed;
height: 100%;
top:0px;
left: 300px;
right:0px;
bottom:0px;
z-index:1;
}
.main_iframe{
height: 100%;
width: 100%;
border:none;
background:#3366ff;
}
<link href="https://cdn.materialdesignicons.com/2.3.54/css/materialdesignicons.min.css" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css?family=Alfa+Slab+One" rel="stylesheet" />

Tape Measure Scrollbar

Simple tape measure scrollbar for a woodshop project I'm working on.

Might be janky on mobile at the moment. Also, scrollbars may be visible on some devices.

Tested on the latest versions of Firefox, Chrome and Edge.

Demo with my Pageable lib: https://codepen.io/Mobius1/pen/zbzWba which allows fast implementation.

A Pen by JFarrow on CodePen.

License.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment