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
|
||||
label: Umami Website ID
|
||||
type: string
|
||||
- name: factsUrl
|
||||
label: Facts JSON URL
|
||||
description: URL to fetch facts data from
|
||||
type: string
|
||||
|
||||
- name: socialmedia
|
||||
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"
|
||||
umamiUrl = "https://data.mchvnh.ch/script.js"
|
||||
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'
|
||||
faq = 'Häufige Fragen'
|
||||
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'
|
||||
faq = 'Frequently Asked Questions'
|
||||
faq-short = 'FAQ'
|
||||
close = 'Close'
|
||||
close = 'Close'
|
||||
facts = 'Facts'
|
||||
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
{{ partial "projects.html" . }}
|
||||
|
||||
{{ partial "facts.html" . }}
|
||||
|
||||
{{ partial "faq.html" . }}
|
||||
</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="#about" alt='{{ T "about" }}'>{{ T "about" }}</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="https://blog.michivonah.ch" alt='{{ T "blog" }}'>{{ T "blog" }}</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);
|
||||
}
|
||||
|
||||
// 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){
|
||||
const trigger = document.querySelector(triggerSelector);
|
||||
const target = ((targetElement) ? document.querySelector(targetElement) : trigger);
|
||||
|
||||
if(trigger){
|
||||
const observer = new IntersectionObserver(entries => {
|
||||
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);
|
||||
}
|
||||
return createIntersectionObserver(trigger, (entry) => {
|
||||
entry.isIntersecting ? target.classList.add(animationClass) : target.classList.remove(animationClass);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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');
|
||||
countOnScroll('.fact-counter', '--factCounter', 'data-count');
|
||||
|
||||
// calculate age
|
||||
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{
|
||||
padding-block: 40px;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue