refactor: my-marquee abstract component
This commit is contained in:
parent
b90f590e66
commit
76f9dc6499
4 changed files with 239 additions and 141 deletions
BIN
assets/sounds/cowbell.mp3
Normal file
BIN
assets/sounds/cowbell.mp3
Normal file
Binary file not shown.
|
@ -2,110 +2,72 @@ function getButtons() {
|
|||
const buttons = document.createElement('template');
|
||||
buttons.id = 'button-collection-template';
|
||||
buttons.innerHTML = `
|
||||
<style>
|
||||
.button-collection-marquee-wrapper {
|
||||
mask-image: linear-gradient(to right, transparent 0%, black 10%, black 90%, transparent 100%);
|
||||
}
|
||||
|
||||
.button-collection-marquee {
|
||||
overflow: hidden;
|
||||
overflow-x: auto;
|
||||
position: relative;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.button-collection-marquee-content {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
width: max-content;
|
||||
animation: scroll-left 60s linear infinite;
|
||||
}
|
||||
|
||||
.button-collection-marquee-content:hover {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
/* Animation */
|
||||
@keyframes scroll-left {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-25%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="button-collection-marquee-wrapper">
|
||||
<div class="button-collection-marquee">
|
||||
<div class="button-collection-marquee-content">
|
||||
<!-- Socials -->
|
||||
<button-8831 href="mailto:tibo@depeuter.dev"
|
||||
src="https://cyber.dabamos.de/88x31/email.gif"
|
||||
hint="Mail Me"></button-8831>
|
||||
<button-8831 href="./key.asc"
|
||||
src="https://www.88x31.nl/gifs/pgp-now.gif"
|
||||
hint="My GPG key"></button-8831>
|
||||
<!-- Operating Systems -->
|
||||
<button-8831 href="https://www.gnu.org/gnu/linux.html"
|
||||
src="https://www.88x31.nl/gifs/gnu-linux.gif"
|
||||
hint="Made on GNU/Linux"></button-8831>
|
||||
<!-- Editors -->
|
||||
<!-- Services -->
|
||||
<button-8831 href="https://www.proxmox.com/"
|
||||
src="https://www.88x31.nl/gifs/proxmox.gif"
|
||||
hint="Proxmox VE"></button-8831>
|
||||
<button-8831 href="https://cloud.depeuter.dev/"
|
||||
src="https://www.88x31.nl/gifs/nextcloud.gif"
|
||||
hint="Nextcloud"></button-8831>
|
||||
<button-8831 href="https://ublockorigin.com/"
|
||||
src="https://www.88x31.nl/gifs/ublock-now.png"
|
||||
hint="uBlock Origin Now!"></button-8831>
|
||||
<!-- Movements -->
|
||||
<button-8831 href="https://home.cern/news/opinion/computing/open-internet-and-free-web"
|
||||
src="https://www.88x31.nl/gifs/fftake.gif"
|
||||
hint="Take back the web (Firefox)"></button-8831>
|
||||
<button-8831 href="https://www.ifixit.com/"
|
||||
src="https://www.88x31.nl/gifs/right-to-repair.png"
|
||||
hint="I Support Right To Repair"></button-8831>
|
||||
<!-- Compatibility -->
|
||||
<button-8831 href="https://www.mozilla.org/firefox/"
|
||||
src="https://www.88x31.nl/gifs/firefox4.gif"
|
||||
hint="Tested on Firefox"></button-8831>
|
||||
<!-- Web -->
|
||||
<button-8831 href="https://www.depeuter.dev"
|
||||
src="https://www.88x31.nl/gifs/graphicdesign.gif"
|
||||
hint="Graphic Design Is My Passion"></button-8831>
|
||||
<button-8831 href="https://developer.mozilla.org/en-US/docs/Web/CSS"
|
||||
src="https://www.88x31.nl/gifs/css.gif"
|
||||
hint="CSS is awesome"></button-8831>
|
||||
<!-- Media -->
|
||||
<button-8831 href="https://last.fm/user/fortemfiducia"
|
||||
src="https://www.88x31.nl/gifs/3dot5mmfc-button.gif"
|
||||
hint="My Last.fm account"></button-8831>
|
||||
<button-8831 href="https://open.spotify.com/user/deyoloboy?si=3bca31169a434d4a"
|
||||
src="https://www.88x31.nl/gifs/spotify.gif"
|
||||
hint="My Spotify account"></button-8831>
|
||||
<button-8831 href="https://www.blankbanshee.com/"
|
||||
src="https://www.88x31.nl/gifs/banshee.gif"
|
||||
hint="Blank Banshee"></button-8831>
|
||||
<button-8831 href="https://fmskyline.bandcamp.com/"
|
||||
src="https://www.88x31.nl/gifs/fm.gif"
|
||||
hint="FMSkyline"></button-8831>
|
||||
<button-8831 href="https://luxuryelite.bandcamp.com/"
|
||||
src="https://capstasher.neocities.org/88x31Buttons/lu.png"
|
||||
hint="Luxury Elite"></button-8831>
|
||||
<button-8831 href="https://nmesh.bandcamp.com/"
|
||||
src="https://www.88x31.nl/gifs/nm.png"
|
||||
hint="nmesh"></button-8831>
|
||||
<!-- Fun -->
|
||||
<button-8831 href="https://www.depeuter.dev"
|
||||
src="https://www.88x31.nl/gifs/thedigitalme.gif"
|
||||
hint="The Digital Me"></button-8831>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
<my-marquee speed="50" direction="left">
|
||||
<!-- Socials -->
|
||||
<button-8831 href="mailto:tibo@depeuter.dev"
|
||||
src="https://cyber.dabamos.de/88x31/email.gif"
|
||||
hint="Mail Me"></button-8831>
|
||||
<button-8831 href="./key.asc"
|
||||
src="https://www.88x31.nl/gifs/pgp-now.gif"
|
||||
hint="My GPG key"></button-8831>
|
||||
<!-- Operating Systems -->
|
||||
<button-8831 href="https://www.gnu.org/gnu/linux.html"
|
||||
src="https://www.88x31.nl/gifs/gnu-linux.gif"
|
||||
hint="Made on GNU/Linux"></button-8831>
|
||||
<!-- Editors -->
|
||||
<!-- Services -->
|
||||
<button-8831 href="https://www.proxmox.com/"
|
||||
src="https://www.88x31.nl/gifs/proxmox.gif"
|
||||
hint="Proxmox VE"></button-8831>
|
||||
<button-8831 href="https://cloud.depeuter.dev/"
|
||||
src="https://www.88x31.nl/gifs/nextcloud.gif"
|
||||
hint="Nextcloud"></button-8831>
|
||||
<button-8831 href="https://ublockorigin.com/"
|
||||
src="https://www.88x31.nl/gifs/ublock-now.png"
|
||||
hint="uBlock Origin Now!"></button-8831>
|
||||
<!-- Movements -->
|
||||
<button-8831 href="https://home.cern/news/opinion/computing/open-internet-and-free-web"
|
||||
src="https://www.88x31.nl/gifs/fftake.gif"
|
||||
hint="Take back the web (Firefox)"></button-8831>
|
||||
<button-8831 href="https://www.ifixit.com/"
|
||||
src="https://www.88x31.nl/gifs/right-to-repair.png"
|
||||
hint="I Support Right To Repair"></button-8831>
|
||||
<!-- Compatibility -->
|
||||
<button-8831 href="https://www.mozilla.org/firefox/"
|
||||
src="https://www.88x31.nl/gifs/firefox4.gif"
|
||||
hint="Tested on Firefox"></button-8831>
|
||||
<!-- Web -->
|
||||
<button-8831 href="https://www.depeuter.dev"
|
||||
src="https://www.88x31.nl/gifs/graphicdesign.gif"
|
||||
hint="Graphic Design Is My Passion"></button-8831>
|
||||
<button-8831 href="https://developer.mozilla.org/en-US/docs/Web/CSS"
|
||||
src="https://www.88x31.nl/gifs/css.gif"
|
||||
hint="CSS is awesome"></button-8831>
|
||||
<!-- Media -->
|
||||
<button-8831 href="https://last.fm/user/fortemfiducia"
|
||||
src="https://www.88x31.nl/gifs/3dot5mmfc-button.gif"
|
||||
hint="My Last.fm account"></button-8831>
|
||||
<button-8831 href="https://open.spotify.com/user/deyoloboy?si=3bca31169a434d4a"
|
||||
src="https://www.88x31.nl/gifs/spotify.gif"
|
||||
hint="My Spotify account"></button-8831>
|
||||
<button-8831 href="https://www.blankbanshee.com/"
|
||||
src="https://www.88x31.nl/gifs/banshee.gif"
|
||||
hint="Blank Banshee"></button-8831>
|
||||
<button-8831 href="https://fmskyline.bandcamp.com/"
|
||||
src="https://www.88x31.nl/gifs/fm.gif"
|
||||
hint="FMSkyline"></button-8831>
|
||||
<button-8831 href="https://luxuryelite.bandcamp.com/"
|
||||
src="https://capstasher.neocities.org/88x31Buttons/lu.png"
|
||||
hint="Luxury Elite"></button-8831>
|
||||
<button-8831 href="https://nmesh.bandcamp.com/"
|
||||
src="https://www.88x31.nl/gifs/nm.png"
|
||||
hint="nmesh"></button-8831>
|
||||
<!-- Fun -->
|
||||
<button-8831 href="https://www.depeuter.dev"
|
||||
src="https://www.88x31.nl/gifs/thedigitalme.gif"
|
||||
hint="The Digital Me"></button-8831>
|
||||
</my-marquee>
|
||||
`;
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
@ -114,46 +76,11 @@ class ButtonCollection extends HTMLElement {
|
|||
constructor() {
|
||||
super();
|
||||
|
||||
import ('./button-8831.js');
|
||||
|
||||
const templateContent = getButtons().content;
|
||||
|
||||
const wrapper = templateContent.cloneNode(true);
|
||||
const marquee = wrapper.querySelector('.button-collection-marquee');
|
||||
const content = wrapper.querySelector('.button-collection-marquee-content');
|
||||
|
||||
// Duplicate the buttons to create an infinite scroll effect
|
||||
content.innerHTML = content.innerHTML.repeat(4);
|
||||
|
||||
const shadowRoot = this.attachShadow({mode: 'open'});
|
||||
const shadowRoot = this.attachShadow({ mode: 'open' });
|
||||
shadowRoot.appendChild(wrapper);
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
requestAnimationFrame(() => {
|
||||
// Half the width of the content
|
||||
marquee.scrollLeft = content.scrollWidth / 2 - window.innerWidth / 2;
|
||||
console.log(`Initial scroll position set to: ${marquee.scrollLeft}`);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
marquee.addEventListener('scroll', () => {
|
||||
const scrollStep = content.scrollWidth / 4; // Half the width of the content
|
||||
|
||||
const currentScroll = marquee.scrollLeft;
|
||||
|
||||
// Handle scrolling to the right
|
||||
if (currentScroll >= content.scrollWidth * 0.65) {
|
||||
console.log(`Jumping back by ${scrollStep}`);
|
||||
marquee.scrollLeft -= scrollStep; // Jump back by half the width
|
||||
}
|
||||
|
||||
// Handle scrolling to the left
|
||||
if (currentScroll <= content.scrollWidth * 0.35) {
|
||||
console.log(`Jumping forward by ${scrollStep}`);
|
||||
marquee.scrollLeft += scrollStep; // Jump forward by half the width
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
166
components/my-marquee.js
Normal file
166
components/my-marquee.js
Normal file
|
@ -0,0 +1,166 @@
|
|||
function getTemplate() {
|
||||
const template = document.createElement('template');
|
||||
template.id = 'my-marquee-template';
|
||||
template.innerHTML = `
|
||||
<style>
|
||||
.marquee-wrapper {
|
||||
mask-image: linear-gradient(to right, transparent 0%, black 10%, black 90%, transparent 100%);
|
||||
}
|
||||
|
||||
.marquee {
|
||||
overflow: hidden;
|
||||
overflow-x: auto;
|
||||
position: relative;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.marquee-content {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
width: max-content;
|
||||
animation: scroll-left 0s linear infinite;
|
||||
}
|
||||
|
||||
.marquee-content:hover {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
/* Animation */
|
||||
@keyframes scroll-left {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-25%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scroll-right {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(25%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="marquee-wrapper">
|
||||
<div class="marquee">
|
||||
<div class="marquee-content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
class MyMarquee extends HTMLElement {
|
||||
static observedAttributes = ['speed', 'direction'];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
const wrapper = getTemplate().content.cloneNode(true);
|
||||
const marquee = wrapper.querySelector('.marquee');
|
||||
const content = wrapper.querySelector('.marquee-content');
|
||||
|
||||
const shadowRoot = this.attachShadow({ mode: 'open' });
|
||||
shadowRoot.appendChild(wrapper);
|
||||
|
||||
this.marquee = marquee;
|
||||
this.content = content;
|
||||
this.repetitions = 1;
|
||||
this.speed = 500; // Default speed
|
||||
this.direction = 'left'; // Default direction
|
||||
|
||||
window.addEventListener('load', this.onLoad.bind(this));
|
||||
this.marquee.addEventListener('scroll', this.handleScroll.bind(this), { passive: true });
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
if (name === 'speed') {
|
||||
this.speed = parseInt(newValue, 10) || this.speed; // Default to 100 if not set
|
||||
this.updateAnimation();
|
||||
} else if (name === 'direction') {
|
||||
this.direction = newValue || this.direction; // Default to 'left' if not set
|
||||
this.updateAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
onLoad() {
|
||||
const slot = this.content.querySelector('slot');
|
||||
|
||||
const slottedElements = slot.assignedNodes({ flatten: true }).filter(node => node.nodeType === Node.ELEMENT_NODE);
|
||||
this.content.innerHTML = slottedElements.map(el => el.outerHTML).join('');
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const singleWidth = this.content.scrollWidth;
|
||||
|
||||
// Calculate the needed repetitions
|
||||
let repetitions = Math.ceil(window.innerWidth / singleWidth) + 1;
|
||||
this.repetitions = repetitions * 4; // Double the repetitions for a seamless effect
|
||||
|
||||
if (debug) {
|
||||
console.log(`Single content width: ${singleWidth}, Repetitions: ${this.repetitions}`);
|
||||
}
|
||||
|
||||
this.content.innerHTML = this.content.innerHTML.repeat(this.repetitions);
|
||||
this.repeatedWidth = singleWidth * this.repetitions;
|
||||
|
||||
this.updateAnimation();
|
||||
|
||||
// Set initial scroll position
|
||||
this.marquee.scrollLeft = (this.repeatedWidth - window.innerWidth) / 2;
|
||||
});
|
||||
}
|
||||
|
||||
updateAnimation() {
|
||||
// Set the animation duration
|
||||
const duration = (this.repeatedWidth / this.speed) * 1000; // Convert to milliseconds
|
||||
this.content.style.animationDuration = `${duration}ms`;
|
||||
|
||||
// Set the animation direction
|
||||
console.assert(['left', 'right'].includes(this.direction))
|
||||
this.content.style.animationName = `scroll-${this.direction}`;
|
||||
if (debug) {
|
||||
console.log(`Animation updated: Duration ${duration}ms, Direction ${this.direction}`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
handleScroll() {
|
||||
const scrollStep = this.content.scrollWidth * 2 / this.repetitions;
|
||||
|
||||
const currentScroll = this.marquee.scrollLeft;
|
||||
|
||||
// Handle scrolling to the right
|
||||
if (currentScroll >= this.content.scrollWidth * 0.7) {
|
||||
if (debug) {
|
||||
console.log(`Jumping back by ${scrollStep}`);
|
||||
ping();
|
||||
}
|
||||
this.marquee.scrollLeft -= scrollStep;
|
||||
}
|
||||
|
||||
// Handle scrolling to the left
|
||||
if (currentScroll <= this.content.scrollWidth * 0.3) {
|
||||
if (debug) {
|
||||
console.log(`Jumping forward by ${scrollStep}`);
|
||||
ping();
|
||||
}
|
||||
this.marquee.scrollLeft += scrollStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ping() {
|
||||
const ping = new Audio('../assets/sounds/cowbell.mp3');
|
||||
ping.play().catch(error => {
|
||||
console.error('Error playing sound:', error);
|
||||
});
|
||||
}
|
||||
|
||||
customElements.define('my-marquee', MyMarquee);
|
|
@ -1 +1,6 @@
|
|||
import ('./components/button-collection.js');
|
||||
import ('./components/button-8831.js');
|
||||
import ('./components/my-marquee.js');
|
||||
|
||||
import ('./components/button-collection.js');
|
||||
|
||||
const debug = false;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue