mirror of
https://github.com/michivonah/website-v3.git
synced 2025-12-22 13:26:29 +01:00
create facts section & by the way reengineer the IntersectionObserver function
This commit is contained in:
parent
7e6957ce7e
commit
a7df408049
11 changed files with 175 additions and 18 deletions
|
|
@ -78,6 +78,10 @@ content:
|
||||||
- name: websiteId
|
- name: websiteId
|
||||||
label: Umami Website ID
|
label: Umami Website ID
|
||||||
type: string
|
type: string
|
||||||
|
- name: factsUrl
|
||||||
|
label: Facts JSON URL
|
||||||
|
description: URL to fetch facts data from
|
||||||
|
type: string
|
||||||
|
|
||||||
- name: socialmedia
|
- name: socialmedia
|
||||||
label: Social Media
|
label: Social Media
|
||||||
|
|
|
||||||
6
content/de/facts.md
Normal file
6
content/de/facts.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: "Fakten"
|
||||||
|
slug: "facts"
|
||||||
|
---
|
||||||
|
|
||||||
|
Einige Fakten über mich...
|
||||||
6
content/en/facts.md
Normal file
6
content/en/facts.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: "Facts"
|
||||||
|
slug: "facts"
|
||||||
|
---
|
||||||
|
|
||||||
|
Some facts about me...
|
||||||
|
|
@ -99,3 +99,4 @@ defaultContentLanguage = 'de'
|
||||||
twitterUsername = "@michivonah"
|
twitterUsername = "@michivonah"
|
||||||
umamiUrl = "https://data.mchvnh.ch/script.js"
|
umamiUrl = "https://data.mchvnh.ch/script.js"
|
||||||
websiteId = "9b188ed8-77b0-4238-aef5-c1b3d48106e4"
|
websiteId = "9b188ed8-77b0-4238-aef5-c1b3d48106e4"
|
||||||
|
factsUrl = "https://cdn.michivonah.ch/facts.json"
|
||||||
|
|
|
||||||
|
|
@ -15,4 +15,5 @@ no-projects-found = 'Keine Projekte gefunden'
|
||||||
no-more-projects = 'Keine weiteren Projekte verfügbar'
|
no-more-projects = 'Keine weiteren Projekte verfügbar'
|
||||||
faq = 'Häufige Fragen'
|
faq = 'Häufige Fragen'
|
||||||
faq-short = 'FAQ'
|
faq-short = 'FAQ'
|
||||||
close = 'Schliessen'
|
close = 'Schliessen'
|
||||||
|
facts = 'Fakten'
|
||||||
|
|
@ -15,4 +15,5 @@ no-projects-found = 'No projects found'
|
||||||
no-more-projects = 'No more projects available'
|
no-more-projects = 'No more projects available'
|
||||||
faq = 'Frequently Asked Questions'
|
faq = 'Frequently Asked Questions'
|
||||||
faq-short = 'FAQ'
|
faq-short = 'FAQ'
|
||||||
close = 'Close'
|
close = 'Close'
|
||||||
|
facts = 'Facts'
|
||||||
|
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
{{ partial "projects.html" . }}
|
{{ partial "projects.html" . }}
|
||||||
|
|
||||||
|
{{ partial "facts.html" . }}
|
||||||
|
|
||||||
{{ partial "faq.html" . }}
|
{{ partial "faq.html" . }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
34
layouts/partials/facts.html
Normal file
34
layouts/partials/facts.html
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<div id="facts" class="facts">
|
||||||
|
<div class="facts-intro fade-up">
|
||||||
|
{{ with .Site.GetPage "facts" }}
|
||||||
|
<h1>{{ .Title }}</h1>
|
||||||
|
<p>{{ .Content }}</p>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
<div class="fact-container">
|
||||||
|
{{ $url := .Site.Params.factsUrl }}
|
||||||
|
{{ with try (resources.GetRemote $url) }}
|
||||||
|
{{ with .Err }}
|
||||||
|
{{ errorf "%s" . }}
|
||||||
|
{{ else with .Value }}
|
||||||
|
{{ $facts := . | transform.Unmarshal }}
|
||||||
|
{{ if $facts }}
|
||||||
|
{{ range $fact := $facts }}
|
||||||
|
{{ $count := 0 }}
|
||||||
|
{{ if isset $fact "total" }}
|
||||||
|
{{ $count = $fact.total }}
|
||||||
|
{{ else if isset $fact "items" }}
|
||||||
|
{{ $count = len $fact.items }}
|
||||||
|
{{ end }}
|
||||||
|
<div class="fact fade-up">
|
||||||
|
<p class="fact-counter" data-count="{{ $count }}"></p>
|
||||||
|
<p>{{ index $fact.titles $.Lang }}</p>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
{{ else }}
|
||||||
|
{{ errorf "Unable to get remote resource %q" $url }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
<a href="#" alt='{{ T "home" }}'>{{ T "home" }}</a>
|
<a href="#" alt='{{ T "home" }}'>{{ T "home" }}</a>
|
||||||
<a href="#about" alt='{{ T "about" }}'>{{ T "about" }}</a>
|
<a href="#about" alt='{{ T "about" }}'>{{ T "about" }}</a>
|
||||||
<a href="#projects" alt='{{ T "projects" }}'>{{ T "projects" }}</a>
|
<a href="#projects" alt='{{ T "projects" }}'>{{ T "projects" }}</a>
|
||||||
|
<a href="#facts" alt='{{ T "facts" }}'>{{ T "facts" }}</a>
|
||||||
<a href="#faq" alt='{{ T "faq-short" }}'>{{ T "faq-short" }}</a>
|
<a href="#faq" alt='{{ T "faq-short" }}'>{{ T "faq-short" }}</a>
|
||||||
<a href="https://blog.michivonah.ch" alt='{{ T "blog" }}'>{{ T "blog" }}</a>
|
<a href="https://blog.michivonah.ch" alt='{{ T "blog" }}'>{{ T "blog" }}</a>
|
||||||
<a href="#contact" alt='{{ T "contact" }}'>{{ T "contact" }}</a>
|
<a href="#contact" alt='{{ T "contact" }}'>{{ T "contact" }}</a>
|
||||||
|
|
|
||||||
|
|
@ -100,29 +100,65 @@ function updateNavStyle(){
|
||||||
document.querySelector("nav").classList.toggle("small", window.scrollY > 20);
|
document.querySelector("nav").classList.toggle("small", window.scrollY > 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
// intersection observer for animations
|
/**
|
||||||
// credits: https://coolcssanimation.com/how-to-trigger-a-css-animation-on-scroll/
|
* Template for easily creating a IntersectionObserver
|
||||||
|
* @param {*} triggerElement Element which triggers the IntersectionObserver
|
||||||
|
* @param {*} callback Function which gets interesction object back
|
||||||
|
* @param {*} rootMargin Parameter rootMargin of the IntersectionObserver
|
||||||
|
* @param {*} threshold Parameter threshold of the IntersectionObserver
|
||||||
|
* @returns IntersectionObserver
|
||||||
|
*/
|
||||||
|
function createIntersectionObserver(triggerElement, callback, rootMargin = '0px 0px 5% 0px', threshold = 0.1){
|
||||||
|
if(triggerElement){
|
||||||
|
const observer = new IntersectionObserver(entries => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
callback(entry);
|
||||||
|
});
|
||||||
|
}, { rootMargin, threshold });
|
||||||
|
return observer.observe(triggerElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function which applies an IntersectionObserver
|
||||||
|
* for running animations on scroll/visibility of a container.
|
||||||
|
*
|
||||||
|
* Original inspiration: https://coolcssanimation.com/how-to-trigger-a-css-animation-on-scroll/
|
||||||
|
* @param {*} triggerSelector Query selector of the element, which should trigger the animation
|
||||||
|
* @param {*} animationClass Class to append when animation is triggered
|
||||||
|
* @param {*} targetElement Element to apply the animation (css class) to. Will applied to triggerSelector if not defined.
|
||||||
|
* @returns IntersectionObserver
|
||||||
|
*/
|
||||||
function animationOnScroll(triggerSelector, animationClass, targetElement){
|
function animationOnScroll(triggerSelector, animationClass, targetElement){
|
||||||
const trigger = document.querySelector(triggerSelector);
|
const trigger = document.querySelector(triggerSelector);
|
||||||
const target = ((targetElement) ? document.querySelector(targetElement) : trigger);
|
const target = ((targetElement) ? document.querySelector(targetElement) : trigger);
|
||||||
|
|
||||||
if(trigger){
|
return createIntersectionObserver(trigger, (entry) => {
|
||||||
const observer = new IntersectionObserver(entries => {
|
entry.isIntersecting ? target.classList.add(animationClass) : target.classList.remove(animationClass);
|
||||||
entries.forEach(entry => {
|
});
|
||||||
if (entry.isIntersecting) {
|
|
||||||
target.classList.add(animationClass);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
target.classList.remove(animationClass);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, { rootMargin: '0px 0px 5% 0px', threshold: 0.1 });
|
|
||||||
|
|
||||||
return observer.observe(trigger);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates IntersectionObserver for triggering counter animation
|
||||||
|
* @param {*} elementSelector Selector of the element(s) to apply the counter effect to
|
||||||
|
* @param {*} cssPropertyName CSS Property with counter's value
|
||||||
|
* @param {*} valueAttribute HTML data attribute with the counter's final value
|
||||||
|
*/
|
||||||
|
function countOnScroll(elementSelector, cssPropertyName, valueAttribute){
|
||||||
|
const items = document.querySelectorAll(elementSelector);
|
||||||
|
|
||||||
|
items.forEach(element => {
|
||||||
|
const value = parseInt(element.getAttribute(valueAttribute));
|
||||||
|
|
||||||
|
createIntersectionObserver(element, (entry) => {
|
||||||
|
element.style.setProperty(cssPropertyName, entry.isIntersecting ? value : 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply on scroll effects
|
||||||
animationOnScroll('.contact-title-wrapper', 'typewriter-animation', '.contact-title');
|
animationOnScroll('.contact-title-wrapper', 'typewriter-animation', '.contact-title');
|
||||||
|
countOnScroll('.fact-counter', '--factCounter', 'data-count');
|
||||||
|
|
||||||
// calculate age
|
// calculate age
|
||||||
function calculateAge(selector){
|
function calculateAge(selector){
|
||||||
|
|
|
||||||
|
|
@ -777,6 +777,71 @@ nav.small .nav-links a:last-child:focus{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FACTS */
|
||||||
|
.facts{
|
||||||
|
margin-top: 30px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
text-align: center;
|
||||||
|
scroll-margin-top: calc(var(--navSmallHeight) + 10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fact-container{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fact{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
width: 100%;
|
||||||
|
padding-block: 40px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fact p{
|
||||||
|
margin: 5px 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width:690px){
|
||||||
|
.fact-container{
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fact{
|
||||||
|
padding-block: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@property --factCounter{
|
||||||
|
syntax: '<integer>';
|
||||||
|
initial-value: 0;
|
||||||
|
inherits: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fact-counter{
|
||||||
|
font-size: 3.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--primary);
|
||||||
|
transition: --factCounter 1s, var(--baseTransition);
|
||||||
|
counter-reset: factCounter var(--factCounter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (pointer: fine){
|
||||||
|
.fact-counter:hover,
|
||||||
|
.fact-counter:focus{
|
||||||
|
transform: scale(1.1);
|
||||||
|
transition: var(--baseTransition);
|
||||||
|
filter: drop-shadow(0 0 0.6rem var(--primary));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fact-counter::after{
|
||||||
|
content: counter(factCounter);
|
||||||
|
}
|
||||||
|
|
||||||
/* FAQ */
|
/* FAQ */
|
||||||
.faq{
|
.faq{
|
||||||
padding-block: 40px;
|
padding-block: 40px;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue