diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 9847b27..04754d3 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -112,6 +112,17 @@ ul[class] { list-style: none; } +img.fade-in { + opacity: 0; + transition: opacity 0.4s ease-in; + + &.loaded { + opacity: 1; + } +} + + + header { display: flex; justify-content: space-between; diff --git a/app/helpers/site_helper.rb b/app/helpers/site_helper.rb index 61e6208..3f71019 100644 --- a/app/helpers/site_helper.rb +++ b/app/helpers/site_helper.rb @@ -1,7 +1,7 @@ module SiteHelper def frontend_javascript_importmap_tags - only_use = %w"application" + only_use = %w"application @hotwired/turbo-rails" importmap_json = JSON.parse(Rails.application.importmap.to_json(resolver: self))['imports'].select{ |k,v| only_use.include?(k)} safe_join [ javascript_inline_importmap_tag(JSON.pretty_generate({ "imports" => importmap_json})), @@ -35,8 +35,8 @@ module SiteHelper first_link = doc.at_css('a') first_link.remove if first_link - result[:title] = doc.at_css('h1').text - result[:description] = doc.at_css('div').text + result[:title] = doc.at_css('h1')&.text + result[:description] = doc.at_css('div')&.text result[:link] = first_link&.to_html doc.search('*').each do |node| diff --git a/app/javascript/application.js b/app/javascript/application.js index 29d1ef4..0459aee 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -1,3 +1,14 @@ +import "@hotwired/turbo-rails" + +var input_range +const max_value = 180 +const speed = 12 + +var curr_value +var raf_id +var random_salt = Math.random() + + function rand_in_range(from, to) { const rand = Math.floor(Math.random() * (to - from + 1)) + from @@ -36,91 +47,141 @@ function shuffle_cards() { localStorage.setItem('shown_cards', JSON.stringify(shown_cards)); } -document.querySelectorAll('.button_shuffle_cards').forEach((item, index) => { - item.addEventListener('click', (e) => { - shuffle_cards() - }) - if (index == 0) - shuffle_cards() +document.addEventListener("turbo:load", ()=> { + init() + }) +document.addEventListener("turbo:before-cache", () => { + if (input_range) { + input_range.value = 0; + input_range.closest('.reveal__container').style.setProperty('--opacity', 1); + updateRotation(input_range.value) + } -// Choose language confirmation button -document.querySelectorAll('#confirm_btn').forEach((confirm_btn) => { - confirm_btn.addEventListener('click', (e) => { - window.location.href = '/' + document.getElementById('language_select').value + let images = document.querySelectorAll("img.fade-in.loaded") + images.forEach(image => { + image.classList.remove("loaded") }) + + }) -// Copy link to clipboard -document.querySelectorAll('[data-share-title]').forEach((share_btn) => { - share_btn.addEventListener('click', (e) => { - - if (navigator.share) { - navigator.share({ - title: e.currentTarget.getAttribute('data-share-title'), - text: e.currentTarget.getAttribute('data-share-text'), - url: window.location.href - }).then(() => { - console.log('Thanks for sharing!'); - }) - .catch(console.error); - } else { - navigator.clipboard.writeText(window.location.href) - alert('URL copied to clipboard!') - } +function init() { - + random_salt = Math.random() + + document.querySelectorAll('.button_shuffle_cards').forEach((item, index) => { + item.addEventListener('click', (e) => { + shuffle_cards() + }) + if (index == 0) + shuffle_cards() }) -}) -document.querySelectorAll('.card__stack a').forEach((card_link) => { - card_link.addEventListener('click', (e) => { - e.preventDefault() + // Choose language confirmation button + document.querySelectorAll('#confirm_btn').forEach((confirm_btn) => { + confirm_btn.addEventListener('click', (e) => { + // window.location.href = '/' + document.getElementById('language_select').value + Turbo.visit('/' + document.getElementById('language_select').value, {advance: 'advance'}) + + }) }) -}) -// Open all external links in ny tab -for (const link of document.links) { - if (link.hostname.replace(/^www\./i, "") != window.location.hostname.replace(/^www\./i, "") && (link.protocol == 'https:' || link.protocol == 'http:')) - link.target = '_blank' -} + // Copy link to clipboard + document.querySelectorAll('[data-share-title]').forEach((share_btn) => { + share_btn.addEventListener('click', (e) => { + + if (navigator.share) { + navigator.share({ + title: e.currentTarget.getAttribute('data-share-title'), + text: e.currentTarget.getAttribute('data-share-text'), + url: window.location.href + }).then(() => { + console.log('Thanks for sharing!'); + }) + .catch(console.error); + } else { + navigator.clipboard.writeText(window.location.href) + alert('URL copied to clipboard!') + } + + + }) + }) + document.querySelectorAll('.card__stack a').forEach((card_link) => { + card_link.addEventListener('click', (e) => { + e.preventDefault() + }) + }) + + + // Open all external links in ny tab + for (const link of document.links) { + if (link.hostname.replace(/^www\./i, "") != window.location.hostname.replace(/^www\./i, "") && (link.protocol == 'https:' || link.protocol == 'http:')) + link.target = '_blank' + } + + input_range = document.getElementsByClassName('reveal')[0] -const input_range = document.getElementsByClassName('reveal')[0] -const max_value = 180 -const speed = 12 + if (input_range) { + input_range.min = 0; + input_range.max = max_value; + } -var curr_value -var raf_id + if (input_range) { + input_range.addEventListener('mousedown', unlockStartHandler, false) + input_range.addEventListener('touchstart', unlockStartHandler, false) + input_range.addEventListener('mouseup', unlockEndHandler, false) + input_range.addEventListener('touchend', unlockEndHandler, false) + + input_range.addEventListener("input", (e) => { + e.currentTarget.closest('.reveal__container').style.setProperty('--opacity', 1 - (e.currentTarget.value/e.currentTarget.max)) + + updateRotation(e.currentTarget.value) + }) + } + + let images = document.querySelectorAll("img.fade-in") + + images.forEach(image => { + image.addEventListener("load", () => { + image.classList.add("loaded") + }) -if (input_range) { - input_range.min = 0; - input_range.max = max_value; + // For cached images that may already be loaded + if (image.complete) { + window.requestAnimationFrame(() => { + image.classList.add("loaded") + }) + } + }) } + function unlockStartHandler() { window.cancelAnimationFrame(raf_id); curr_value = +this.value; } + function unlockEndHandler() { curr_value = +this.value; if (curr_value >= this.max) { const card = document.querySelector('.card__container.active') - window.location.href = card.parentNode.getAttribute('href') + //window.location.href = card.parentNode.getAttribute('href') + Turbo.visit(card.parentNode.getAttribute('href'), { action: 'advance'} ) - // reset - this.value = 0; - this.closest('.reveal__container').style.setProperty('--opacity', 1); } else { raf_id = window.requestAnimationFrame(animateHandler); } } + // handle range animation function animateHandler() { @@ -137,39 +198,21 @@ function animateHandler() { curr_value = curr_value - speed; } -if (input_range) { - input_range.addEventListener('mousedown', unlockStartHandler, false) - input_range.addEventListener('mousestart', unlockStartHandler, false) - input_range.addEventListener('mouseup', unlockEndHandler, false) - input_range.addEventListener('touchend', unlockEndHandler, false) - - input_range.addEventListener("input", (e) => { - e.currentTarget.closest('.reveal__container').style.setProperty('--opacity', 1 - (e.currentTarget.value/e.currentTarget.max)) - - updateRotation(e.currentTarget.value) - }) -} function updateRotation(angle) { - document.documentElement.style.setProperty('--flip-deg', angle + 'deg') - - document.documentElement.style.setProperty('--flip-scale', calculateScaleFactor(angle)) - - document.documentElement.style.setProperty('--flip-rotate', calculateRotateFactor(angle) + 'deg') - + document.documentElement.style.setProperty('--flip-deg', angle + 'deg') + document.documentElement.style.setProperty('--flip-scale', calculateScaleFactor(angle)) + document.documentElement.style.setProperty('--flip-rotate', calculateRotateFactor(angle) + 'deg') } -const random_salt = Math.random() function calculateScaleFactor(angle) { const radians = angle * (Math.PI / 180) return 1 + (0.1 * Math.sin(radians)) - // return 1 + (0.2 * (angle / 180)) } - function calculateRotateFactor(angle) { const radians = angle * (Math.PI / 180) return (4 * Math.sin(radians)) + (random_salt * 2) diff --git a/app/views/site/tmpl_article.html.erb b/app/views/site/tmpl_article.html.erb index fa6d54e..d5969d5 100644 --- a/app/views/site/tmpl_article.html.erb +++ b/app/views/site/tmpl_article.html.erb @@ -25,7 +25,7 @@ #{rails_storage_proxy_url(asset.file.variant(resize_to_fill: [1002,1369]))} 1002w ", sizes: "", - loading: 'lazy' if asset&.file&.image? %> + class: 'fade-in' if asset&.file&.image? %> <% end %>