{"id":4335,"date":"2023-03-27T10:37:06","date_gmt":"2023-03-27T07:37:06","guid":{"rendered":"https:\/\/jenniina.fi\/?p=4335"},"modified":"2023-08-26T14:18:41","modified_gmt":"2023-08-26T11:18:41","slug":"interactive-javascript-typescript-snippets","status":"publish","type":"post","link":"https:\/\/jenniina.fi\/finnish\/interactive-javascript-typescript-snippets\/","title":{"rendered":"Interactive JavaScript snippets"},"content":{"rendered":"<section class=\"wp-block-group alignfull is-style-no-margin-vertical has-palette-2-color has-palette-1-background-color has-text-color has-background\" style=\"padding-bottom:20px\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\"><div class=\"alignwide-wrap\" data-block=\"columns\">\n<div class=\"wp-block-columns alignwide\">\n<div class=\"wp-block-column\"><nav aria-label=\"breadcrumbs\" class=\"rank-math-breadcrumb\"><p><span class=\"last\">Etusivu<\/span><\/p><\/nav>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h1 class=\"has-text-align-center has-palette-2-color has-text-color wp-block-heading\" id=\"title\">Interactive JavaScript Snippets<\/h1>\n\n\n\n<div class=\"wp-block-columns\">\n<div class=\"wp-block-column\">\n<p class=\"has-text-align-left\">Snippets I made in vanilla JavaScript\/TypeScript to add fully keyboard accessible, interactive JavaScript decorations to a section of a website. Refactored from the designs in my <a href=\"https:\/\/react-az.jenniina.fi\/\" data-type=\"page\" data-id=\"36\">React sub-site<\/a> hero sections. Quantity, position and colors are randomized with JavaScript, appearance is set with CSS.  For the sake of accessibility, I made the elements into unordered list elements that are labelled eye 1, eye 2 etc. <\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div><\/div><\/section>\n\n\n\n<section class=\"wp-block-group alignfull topclip bottomclip marginreset is-style-no-margin-vertical has-palette-1-color has-accent-background-color has-text-color has-background\" style=\"padding-top:16vw;padding-right:0px;padding-bottom:6vw;padding-left:0px\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\">\n<div class=\"wp-block-columns\">\n<div class=\"wp-block-column\">\n<h2 class=\"has-text-align-center topline wp-block-heading\">Alien\/Jelly Eyes<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Features<\/h3>\n\n\n\n<ul class=\"resetliststyle wp-block-list\">\n<li>Remove with click\/tap, or with Enter when focused<\/li>\n\n\n\n<li>Runs exit animation before removal<\/li>\n\n\n\n<li>Rotate to face cursor<\/li>\n\n\n\n<li>Hover\/focus animation<\/li>\n\n\n\n<li>Pointer-enter direction aware movement<\/li>\n\n\n\n<li>Deployed by giving a container the id &#8220;snippet-eyes&#8221; and the class &#8220;snippet-container&#8221;<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<\/div><\/section>\n\n\n\n<section class=\"wp-block-group alignfull snippet-section snippet-container is-style-no-margin-vertical\" id=\"snippet-eyes\" style=\"padding-bottom:60vh\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\"><div class=\"alignwide-wrap\" data-block=\"columns\">\n<div class=\"wp-block-columns alignwide\">\n<div class=\"wp-block-column\"><\/div>\n<\/div>\n<\/div><\/div><\/section>\n\n\n\n<section class=\"wp-block-group alignfull topclip bottomclip is-style-no-margin-vertical has-palette-1-color has-accent-background-color has-text-color has-background\" style=\"padding-top:16vw;padding-right:0px;padding-bottom:6vw;padding-left:0px\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\">\n<div class=\"wp-block-columns\">\n<div class=\"wp-block-column\">\n<h2 class=\"has-text-align-center topline wp-block-heading\">Geometric<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Features<\/h3>\n\n\n\n<ul class=\"resetliststyle wp-block-list\">\n<li>Remove with click\/tap, or with Enter when focused <\/li>\n\n\n\n<li>Runs exit animation before removal<\/li>\n\n\n\n<li>Hover\/focus animation<\/li>\n\n\n\n<li>Pointer-enter direction aware movement<\/li>\n\n\n\n<li>Deployed by giving a container the id &#8220;snippet-geometric&#8221; and the class &#8220;snippet-container&#8221;<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<\/div><\/section>\n\n\n\n<section class=\"wp-block-group alignfull snippet-section snippet-container is-style-no-margin-vertical\" id=\"snippet-geometric\" style=\"padding-bottom:60vh\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\"><div class=\"alignwide-wrap\" data-block=\"columns\">\n<div class=\"wp-block-columns alignwide\">\n<div class=\"wp-block-column\"><\/div>\n<\/div>\n<\/div><\/div><\/section>\n\n\n\n<section class=\"wp-block-group alignfull topclip bottomclip is-style-no-margin-vertical has-palette-1-color has-accent-background-color has-text-color has-background\" style=\"padding-top:16vw;padding-bottom:6vw\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\">\n<div class=\"wp-block-columns\">\n<div class=\"wp-block-column\">\n<h2 class=\"has-text-align-center topline wp-block-heading\">Bubbles<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Features<\/h3>\n\n\n\n<ul class=\"resetliststyle wp-block-list\">\n<li>Remove with click\/tap, or with Enter when focused<\/li>\n\n\n\n<li>Runs exit animation before removal<\/li>\n\n\n\n<li>Hover\/focus animation jiggle and filter<\/li>\n\n\n\n<li>Pointer-enter direction aware movement<\/li>\n\n\n\n<li>Deployed by giving a container the id &#8220;snippet-bubbles&#8221; and the class &#8220;snippet-container&#8221;<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<\/div><\/section>\n\n\n\n<section class=\"wp-block-group alignfull snippet-section snippet-container is-style-no-margin-vertical\" id=\"snippet-bubbles\" style=\"padding-top:0px;padding-bottom:60vh\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\"><div class=\"alignwide-wrap\" data-block=\"columns\">\n<div class=\"wp-block-columns alignwide\">\n<div class=\"wp-block-column\"><\/div>\n<\/div>\n<\/div><\/div><\/section>\n\n\n\n<section class=\"wp-block-group alignfull topclip bottomclip is-style-no-margin-vertical has-palette-1-color has-accent-background-color has-text-color has-background\" style=\"padding-top:14vw;padding-bottom:8vw\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\"><div class=\"alignwide-wrap\" data-block=\"columns\">\n<div class=\"wp-block-columns alignwide\">\n<div class=\"wp-block-column\">\n<h2 class=\"has-text-align-center wp-block-heading\">Some of the Code<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Moving the target <\/h3>\n\n\n\n<p>Making the target move up, down, left or right, depending on the direction that the pointer enters the target area.<\/p>\n\n\n<div class=\"jla-accordion\"><details><summary class=\"jla-accordion-title\"><span>Movement Code<\/span><\/summary><div class=\"jla-accordion-body\"><pre class=\"wp-block-code\"><code>\/\/Move items up, down, left or right, depending on the direction they're approached from:\nconst movingItem = (e: PointerEvent) =&gt; {\n    const target = e.target as HTMLElement\n    const targetRight = window.getComputedStyle(target).getPropertyValue(\"right\");\n    const targetTop = window.getComputedStyle(target).getPropertyValue(\"top\");\n    const containerRect = target.closest(\".snippet-container\")?.getBoundingClientRect();\n    const from = getEnterDirection(e)\n    switch (from) {\n        case 'top':\n            setTimeout(() =&gt; {\n                if (containerRect) target.style.top = `${(parseFloat(targetTop) \/ containerRect.height) * 100 + 1}%`\n                else target.style.top = `${parseFloat(targetTop) + 10}px`\n            }, 100);\n            break;\n        case 'right':\n            setTimeout(() =&gt; {\n                if (containerRect) target.style.right = `${(parseFloat(targetRight) \/ containerRect.width) * 100 + 1}%`\n                else target.style.right = `${parseFloat(targetRight) + 10}px`\n            }, 100);\n            break;\n        case 'bottom':\n            setTimeout(() =&gt; {\n                if (containerRect) target.style.top = `${(parseFloat(targetTop) \/ containerRect.height) * 100 - 1}%`\n                else target.style.top = `${parseFloat(targetTop) - 10}px`\n            }, 100);\n            break;\n        case 'left':\n            setTimeout(() =&gt; {\n                if (containerRect) target.style.right = `${(parseFloat(targetRight) \/ containerRect.width) * 100 - 1}%`\n                else target.style.right = `${parseFloat(targetRight) - 10}px`\n            }, 100);\n    }\n}\n\n\/\/Determine the pointer enter direction:\nfunction getEnterDirection(e: PointerEvent) {\n    const ref = e.target as HTMLElement\n    const { width, height, top, left } = ref.getBoundingClientRect();\n    const l = e.pageX - (left + window.pageXOffset),\n        t = e.pageY - (top + window.pageYOffset),\n        xx = width &gt; height ? (height \/ width) : 1,\n        x = (l - (width \/ 2) * xx),\n        yy = height &gt; width ? (width \/ height) : 1,\n        y = (t - (height \/ 2) * yy),\n        d = Math.round(Math.atan2(y, x) \/ 1.57079633 + 5) % 4;\n    switch (d) {\n        case 0: return 'top';\n        case 1: return 'right';\n        case 2: return 'bottom';\n        case 3: return 'left';\n        default: return ''\n    }\n};<\/code><\/pre>\n<\/div><\/details><\/div>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Rotate Eyes<\/h3>\n\n\n\n<p>Making the eyes rotate to follow the pointer<\/p>\n\n\n<div class=\"jla-accordion\"><details><summary class=\"jla-accordion-title\"><span>Rotation code<\/span><\/summary><div class=\"jla-accordion-body\"><pre class=\"wp-block-code\"><code>\/\/Making the eyes rotate to follow the pointer:\nconst follow = (e: PointerEvent) =&gt; {\n    const eyes = &#091;...document.querySelectorAll&lt;HTMLSpanElement&gt;('.js-eye .inner')]\n    if (eyes) {\n        eyes.forEach(eye =&gt; {\n            const rect = eye.getBoundingClientRect()\n            const x = rect.left + rect.width \/ 2\n            const y = rect.top + rect.height \/ 2\n            const rotation = radianToAngle(e.clientX, e.clientY, x, y)\n            eye.style.transform = `rotate(${rotation}deg)`\n        })\n    }\n}\nfunction radianToAngle(cx: number, cy: number, ex: number, ey: number) {\n    const dy = ey - cy,\n        dx = ex - cx,\n        rad = Math.atan2(dy, dx),\n        deg = rad * 180 \/ Math.PI\n    return deg\n}\nwindow.addEventListener('pointermove', follow)<\/code><\/pre>\n<\/div><\/details><\/div>\n\n\n\n<div style=\"height:180px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p class=\"has-text-align-center\">See also other interactive JavaScript examples at my <a href=\"https:\/\/jenniina.fi\/finnish\/draggable-blobs\/\" data-type=\"post\" data-id=\"3630\">accessible draggable blobs app<\/a> ja <a href=\"https:\/\/jenniina.fi\/finnish\/pong\/\" data-type=\"post\" data-id=\"4243\">pong javascript game<\/a>.<\/p>\n<\/div>\n<\/div>\n<\/div><\/div><\/section>","protected":false},"excerpt":{"rendered":"<div class=\"entry-summary\">\nSnippets I made in vanilla JavaScript\/TypeScript to add fully keyboard accessible, interactive JavaScript decorations to a section of a website\n<\/div>\n<div class=\"link-more-container\"><a href=\"https:\/\/jenniina.fi\/finnish\/interactive-javascript-typescript-snippets\/\" class=\"link-more\" aria-label=\"Continue reading Interactive JavaScript snippets\">More &raquo;<\/a><\/div>","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[9,6],"tags":[],"class_list":["post-4335","post","type-post","status-publish","format-standard","hentry","category-javascript","category-web-design","entry","entry-type-post"],"_links":{"self":[{"href":"https:\/\/jenniina.fi\/finnish\/wp-json\/wp\/v2\/posts\/4335","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jenniina.fi\/finnish\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jenniina.fi\/finnish\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jenniina.fi\/finnish\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jenniina.fi\/finnish\/wp-json\/wp\/v2\/comments?post=4335"}],"version-history":[{"count":77,"href":"https:\/\/jenniina.fi\/finnish\/wp-json\/wp\/v2\/posts\/4335\/revisions"}],"predecessor-version":[{"id":5610,"href":"https:\/\/jenniina.fi\/finnish\/wp-json\/wp\/v2\/posts\/4335\/revisions\/5610"}],"wp:attachment":[{"href":"https:\/\/jenniina.fi\/finnish\/wp-json\/wp\/v2\/media?parent=4335"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jenniina.fi\/finnish\/wp-json\/wp\/v2\/categories?post=4335"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jenniina.fi\/finnish\/wp-json\/wp\/v2\/tags?post=4335"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}