diff --git a/Day 13 (prototyping)/Campus Visit Prototype.epgz b/Day 13 (prototyping)/Campus Visit Prototype.epgz new file mode 100644 index 0000000..b39d541 Binary files /dev/null and b/Day 13 (prototyping)/Campus Visit Prototype.epgz differ diff --git a/Day 13 (prototyping)/UI design principles.txt b/Day 13 (prototyping)/UI design principles.txt new file mode 100644 index 0000000..7477a14 --- /dev/null +++ b/Day 13 (prototyping)/UI design principles.txt @@ -0,0 +1,13 @@ +UI design principles - mostly adapted from https://maze.co/collections/ux-ui-design/ui-design-principles/ + * Consistency (within the application and with similar applications) + * Provide easy navigation (logical hierarchy: navigating menus or tools for more options; multiple ways to carry out an action where it's natural to do so) + * Provide informative feedback (e.g. password strength) + * Design workflows to yield closure (e.g. "purchase complete", "file saved") + * Prevent user errors (e.g. "are you sure you want to exit without saving?") + * Allow users ways to reverse their actions easily (e.g. undo, redo, cancel, and exit) + * Avoid surprises and changes in behavior + * Minimize the user's memory load (avoid having to remember things in order to perform a task, e.g. "I forgot that I already bought that book.") + * Aim for clarity (quickly and easily understand what something does) + * Use visual hierarchy (size, color, font, white space, and negative space) to illustrate what is important and what groups together + * Design for flexibility: Accommodate different user skill levels (e.g. mouse vs keyboard shortcuts, advanced features, filters) and disabilities (color contrast, screen reader, easy tab key navigation, for web: different devices) + * Lead the user to formulate a good mental model of what happens internally diff --git a/Day 13 (prototyping)/boxes.png b/Day 13 (prototyping)/boxes.png new file mode 100644 index 0000000..78f2d1f Binary files /dev/null and b/Day 13 (prototyping)/boxes.png differ diff --git a/Day 13 (prototyping)/clickable prototype/Resources/expand.png b/Day 13 (prototyping)/clickable prototype/Resources/expand.png new file mode 100644 index 0000000..a906329 Binary files /dev/null and b/Day 13 (prototyping)/clickable prototype/Resources/expand.png differ diff --git a/Day 13 (prototyping)/clickable prototype/Resources/fit.png b/Day 13 (prototyping)/clickable prototype/Resources/fit.png new file mode 100644 index 0000000..497b3fc Binary files /dev/null and b/Day 13 (prototyping)/clickable prototype/Resources/fit.png differ diff --git a/Day 13 (prototyping)/clickable prototype/Resources/script.js b/Day 13 (prototyping)/clickable prototype/Resources/script.js new file mode 100644 index 0000000..5040fa0 --- /dev/null +++ b/Day 13 (prototyping)/clickable prototype/Resources/script.js @@ -0,0 +1,264 @@ +function scaleMap(page, r) { + var map = page.querySelector("map"); + var container = page.querySelector(".ImageContainer > .Links"); + if (!container) { + container = document.createElement("div"); + container.setAttribute("class", "Links"); + page.querySelector(".ImageContainer").appendChild(container); + } else { + container.innerHTML = ""; + } + + map.querySelectorAll("area").forEach(function (area) { + var original = area._originalCoords || area.getAttribute("coords"); + area._originalCoords = original; + var coords = original.split(/\,/).map(function (v) { + return Math.round(parseFloat(v) / r); + }); + + area.setAttribute("coords", coords.join(",")); + var a = document.createElement("a"); + a.style.left = convertRatio(coords[0]) + "px"; + a.style.top = convertRatio(coords[1]) + "px"; + a.style.width = convertRatio(coords[2] - coords[0]) + "px"; + a.style.height = convertRatio(coords[3] - coords[1]) + "px"; + a.setAttribute("href", area.getAttribute("href")); + + container.appendChild(a); + }); +} +function convertRatio(value) { + return parseFloat(value); // * window.devicePixelRatio; +} + +function fitImages() { + var pages = document.querySelectorAll("body > div.Page"); + var W = 0; + var H = 0; + + var activePage = null; + + pages.forEach(function (page) { + if (page.offsetWidth == 0 || page.offsetHeight == 0) return; + + W = page.offsetWidth - (window.useExpandedMode ? 200 : 40); + H = page.offsetHeight - 40; + activePage = page; + }); + + if (activePage && window.lastActivePage != activePage) { + window.useExpandedMode = false; + invalidateExpandMode("dontFit"); + window.lastSize = null; + + document.body.querySelectorAll(".TOC > div").forEach(function (item) { + var matched = item.classList.contains("Page_" + activePage.id); + if (matched) { + document.title = item._name + " - " + window.originalTitle; + item.classList.add("Focused"); + item.focus(); + } else { + item.classList.remove("Focused"); + } + }); + + window.lastActivePage = activePage; + } + + + if (W && H) { + if (window.lastSize && window.lastSize.W == W && window.lastSize.H == H) return; + + var imgs = document.querySelectorAll("body > div.Page img"); + imgs.forEach(function (img) { + var r = window.useExpandedMode ? Math.min(img.naturalWidth / W, img.naturalHeight / H) : Math.max(img.naturalWidth / W, img.naturalHeight / H); + + if (r < 1 && !window.useExpandedMode) r = 1; + var w = Math.round(img.naturalWidth / r); + var h = Math.round(img.naturalHeight / r); + + img.style.width = w + "px"; + img.style.height = h + "px"; + + img.setAttribute("width", w); + img.setAttribute("height", h); + + var page = img; + while (!page.classList.contains("Page")) page = page.parentNode; + + scaleMap(page, r * (img._originalWidth / img.naturalWidth)); + }); + + window.lastSize = {W: W, H: H}; + } +} +function checkActivePage() { + var pages = document.querySelectorAll("body > div.Page"); + var found = false; + var firstPage = null; + pages.forEach(function (page) { + if (!firstPage) firstPage = page; + if (page.offsetWidth != 0 && page.offsetHeight != 0) found = true; + }); + + if (!found && firstPage) { + location.hash = "#" + firstPage.id; + fitImages(); + } +} + +function workingThreadFunction() { + try { + try { + fitImages(); + } catch (e) { + console.error(e); + } + try { + checkActivePage(); + } catch (e) { + console.error(e); + } + } catch (e) { + console.error(e); + } finally { + window.setTimeout(workingThreadFunction, 300); + } +} + + +var idleTimeout = null; +function handleMouseMove() { + if (!document.body.classList.contains("Active")) { + document.body.classList.add("Active"); + } + + if (idleTimeout) window.clearTimeout(idleTimeout); + idleTimeout = window.setTimeout(function () { + document.body.classList.remove("Active"); + idleTimeout = null; + }, 400); +} + +var THUMB_WIDTH = 250; +var THUMB_HEIGHT = 160; +var THUMB_DISPLAY_SIZE = 160; + +function buildThumbnail(url, callback) { + var image = new Image(); + image.onload = function () { + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + + var r = Math.max(image.width / THUMB_WIDTH, image.height / THUMB_HEIGHT); + var w = image.width / r, h = image.height / r; + canvas.width = w; + canvas.height = h; + + ctx.drawImage(image, 0, 0, w, h); + + callback(canvas.toDataURL('image/png'), w, h); + }; + + image.src = url; +} + +function generateTOC() { + var toc = document.createElement("div"); + toc.setAttribute("class", "TOC"); + var pages = document.querySelectorAll("body > div.Page"); + pages.forEach(function (page) { + var title = page.querySelector("h2"); + var img = page.querySelector(".ImageContainer img"); + + var item = document.createElement("div"); + var imageWrapper = document.createElement("a"); + var itemImage = document.createElement("img"); + + item.classList.add("Page_" + page.id); + item.setAttribute("tabindex", 0); + item._name = title.textContent; + + imageWrapper.setAttribute("href", "#" + page.id); + + item.appendChild(imageWrapper); + var name = document.createElement("strong"); + name.innerHTML = title.innerHTML; + + toc.appendChild(item); + + buildThumbnail(img.src, function (dataUrl, w, h) { + var r = Math.max(w / THUMB_DISPLAY_SIZE, h / THUMB_DISPLAY_SIZE); + var w = w / r, h = h / r; + + imageWrapper.appendChild(itemImage); + itemImage.style.width = w + "px"; + itemImage.style.height = h + "px"; + itemImage.src = dataUrl; + + imageWrapper.appendChild(name); + }); + }); + + document.body.appendChild(toc); +} + +function invalidateExpandMode(dontFit) { + if (window.useExpandedMode) { + document.body.classList.add("ExpandMode"); + } else { + document.body.classList.remove("ExpandMode"); + } + + if (!dontFit) fitImages(); +} + +function boot() { + window.originalTitle = document.title; + document.addEventListener("mousemove", handleMouseMove); + var style = document.createElement("link"); + style.setAttribute("rel", "stylesheet"); + style.setAttribute("href", "Resources/style.css"); + document.querySelector("head").appendChild(style); + + var imgs = document.querySelectorAll("body > div.Page img"); + imgs.forEach(function (img) { + img._originalWidth = parseInt(img.getAttribute("width"), 10); + img._originalHeight = parseInt(img.getAttribute("height"), 10); + }); + + + generateTOC(); + workingThreadFunction(); + + + window.zoomToggleButton = document.createElement("button"); + window.zoomToggleButton.classList.add("ToggleZoomButton"); + window.zoomToggleButton.setAttribute("title", "Toggle expand/fit mode"); + document.body.appendChild(window.zoomToggleButton); + + window.zoomToggleButton.addEventListener("click", function () { + window.useExpandedMode = window.useExpandedMode ? false : true; + window.lastSize = null; + invalidateExpandMode(); + }, false); +} + +window.onload = boot; + + + + + + + + + + + + + + + + + diff --git a/Day 13 (prototyping)/clickable prototype/Resources/style.css b/Day 13 (prototyping)/clickable prototype/Resources/style.css new file mode 100644 index 0000000..ab9db18 --- /dev/null +++ b/Day 13 (prototyping)/clickable prototype/Resources/style.css @@ -0,0 +1,169 @@ +/* + place extra stylesheet here +*/ + +body, html { + margin: 0px; + padding: 0px; + overflow: hidden; + font-family: "Product Sans", "Liberation Sans", Arial, sans-serif; +} + +#documentMetadata, +#documentTitle { + display: none; +} + +.Page { + position: absolute; + left: 230px; + top: 0px; + right: 0px; + bottom: 0px; + background: #00000077; + text-align: center; +} +body:not(.ExpandMode) .Page { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} +body.ExpandMode .Page { + overflow: auto; + padding: 20px; + text-align: center; +} +.Page:not(:target) { + display: none !important; +} + +.Page > h2 { + display: none; +} +.Page .ImageContainer { + box-shadow: 0px 0px 1em #00000077; +} +body.ExpandMode .Page .ImageContainer { + display: inline-block; + margin-bottom: 20px; +} +.Page .ImageContainer { + position: relative; +} +.Page .ImageContainer > .Links > a { + position: absolute; + background-color: transparent; + transition: background-color 0.2s ease; +} +body.Active .Page .ImageContainer > .Links > a { + background-color: #E79A0F11; +} +.Page .ImageContainer > .Links > a:hover { + background-color: #FFFFFF55 !important; +} + +.TOC { + position: absolute; + top: 0px; + left: 0px; + bottom: 0px; + width: 230px; + display: flex; + flex-direction: column; + + overflow-y: auto; + overflow-x: visible; + z-index: 2; + background: #444; +} +.TOC > div { + display: flex; + flex-direction: column; +} +.TOC > div.Focused > a { + background: #5294E2; +} +.TOC > div:not(.Focused):hover > a { + background: #5294E266; +} +.TOC > div:not(.Focused):not(:hover) { + opacity: 0.7; +} +.TOC > div + div { + margin-top: 1em; +} + +.TOC > div > a { + flex: 1 1 auto; + display: flex; + flex-direction: column; + align-items: center; + padding: 1em; + background: transparent; + transition: background 0.2s ease; + text-decoration: none; +} +.TOC > div > a > img { + display: block; + border: solid 2px #FFF; + box-shadow: 0px 0px 5px #000; +} +.TOC > div > a > strong { + display: block; + text-align: center; + color: #FFF; + font-weight: normal; + margin-top: 0.3em; +} + +.ToggleZoomButton { + position: fixed; + top: 0.5em; + right: 0.5em; + width: 1.5em; + height: 1.5em; + padding: 0.3em; + box-sizing: content-box; + background: url(expand.png) 50% 50% no-repeat #CCCCCC; + background-origin: content-box; + background-size: contain; + + border: solid 1px #777; +} +.ToggleZoomButton:active { + margin-top: 1px; + margin-left: 1px; +} +.ToggleZoomButton:not(:hover) { + opacity: 0.3; +} +body.ExpandMode .ToggleZoomButton { + background-image: url(fit.png) +} + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Day 13 (prototyping)/clickable prototype/index.html b/Day 13 (prototyping)/clickable prototype/index.html new file mode 100644 index 0000000..c2fd39d --- /dev/null +++ b/Day 13 (prototyping)/clickable prototype/index.html @@ -0,0 +1,2 @@ +
+Exported at: Wed Sep 18 2024 09:38:34 GMT-0400 (Eastern Daylight Time)