🚛 feat: add Daimler Truck logos and Optimize Companies component (#394)

This commit is contained in:
Marco Beretta
2025-08-15 00:09:06 +02:00
committed by GitHub
parent a28ab06f77
commit 2c19e8968e
4 changed files with 172 additions and 79 deletions

View File

@@ -1,4 +1,4 @@
import React from 'react'
import React, { useMemo } from 'react'
import Image from 'next/image'
import styles from './companies.module.css'
@@ -27,23 +27,38 @@ const companies: Company[] = [
logoDark: '/images/logos/BostonUniversity_dark.png',
logoColor: '/images/logos/BostonUniversity_color.png',
},
{
name: 'Daimler Truck',
logo: '/images/logos/DaimlerTruck_light.svg',
logoDark: '/images/logos/DaimlerTruck_dark.svg',
},
]
export const Companies = () => {
const minLogos = 15
const duplicateCount = Math.ceil(minLogos / companies.length)
const duplicatedCompanies = Array(duplicateCount * 2)
.fill(companies)
.flat()
export const Companies: React.FC = React.memo(() => {
const minLogos = 8 // Reduced minimum for better performance
const displayCount = Math.max(minLogos, companies.length)
const logosToShow = useMemo(
() => Array.from({ length: displayCount }, (_, i) => companies[i % companies.length]),
[displayCount],
)
// Create duplicated array for seamless infinite scroll
const duplicatedLogos = useMemo(() => [...logosToShow, ...logosToShow], [logosToShow])
const baseSpeed = 2
const duration = Math.max(70, companies.length * baseSpeed)
const mobileDuration = Math.max(15, companies.length * 1.5)
const duration = useMemo(() => Math.max(40, logosToShow.length * baseSpeed), [logosToShow.length])
const mobileDuration = useMemo(() => Math.max(30, logosToShow.length * 1.5), [logosToShow.length])
const containerStyle = {
'--scroll-duration': `${duration}s`,
'--scroll-duration-mobile': `${mobileDuration}s`,
} as React.CSSProperties
const containerStyle = useMemo(
() =>
({
'--scroll-duration': `${duration}s`,
'--scroll-duration-mobile': `${mobileDuration}s`,
}) as React.CSSProperties,
[duration, mobileDuration],
)
return (
<section className="py-20">
@@ -58,76 +73,79 @@ export const Companies = () => {
</div>
<div className={styles.scrollContainer} style={containerStyle}>
<div className={styles.scrollContent}>
{duplicatedCompanies.map((company, index) => (
<div
key={`logo-${index}`}
className={`${styles.logoItem} ${company.logoColor ? 'group' : ''}`}
>
{company.logoColor ? (
<div className="relative">
{/* Light logo */}
<Image
src={company.logo}
alt={`${company.name} logo`}
className={`${styles.logo} dark:hidden group-hover:opacity-0 transition-opacity duration-300`}
width={120}
height={60}
unoptimized
priority={index < companies.length}
/>
{/* Dark logo */}
<Image
src={company.logoDark}
alt={`${company.name} logo`}
className={`${styles.logo} hidden dark:block group-hover:opacity-0 transition-opacity duration-300`}
width={120}
height={60}
unoptimized
priority={index < companies.length}
/>
{/* Color logo */}
<Image
src={company.logoColor}
alt={`${company.name} logo`}
className={`${styles.logo} absolute top-0 left-0 opacity-0 group-hover:opacity-100 transition-opacity duration-300`}
width={120}
height={60}
unoptimized
priority={index < companies.length}
/>
</div>
) : (
<>
{/* Light logo */}
<Image
src={company.logo}
alt={`${company.name} logo`}
className={`${styles.logo} ${company.logoDark ? 'dark:hidden' : ''}`}
width={120}
height={60}
unoptimized
priority={index < companies.length}
/>
{company.logoDark && (
{duplicatedLogos.map((company, index) => {
const isFirstFewLogos = index < Math.min(4, logosToShow.length)
const key = `${company.name}-${index}`
return (
<div key={key} className={`${styles.logoItem} ${company.logoColor ? 'group' : ''}`}>
{company.logoColor ? (
<div className="relative">
<Image
src={company.logoDark}
src={company.logo}
alt={`${company.name} logo`}
className={`${styles.logo} hidden dark:block`}
className={`${styles.logo} dark:hidden group-hover:opacity-0 transition-opacity duration-300`}
width={120}
height={60}
unoptimized
priority={index < companies.length}
priority={isFirstFewLogos}
loading={isFirstFewLogos ? 'eager' : 'lazy'}
/>
)}
</>
)}
</div>
))}
<Image
src={company.logoDark || company.logo}
alt={`${company.name} logo`}
className={`${styles.logo} hidden dark:block group-hover:opacity-0 transition-opacity duration-300`}
width={120}
height={60}
unoptimized
priority={isFirstFewLogos}
loading={isFirstFewLogos ? 'eager' : 'lazy'}
/>
<Image
src={company.logoColor}
alt={`${company.name} logo`}
className={`${styles.logo} absolute top-0 left-0 opacity-0 group-hover:opacity-100 transition-opacity duration-300`}
width={120}
height={60}
unoptimized
priority={isFirstFewLogos}
loading={isFirstFewLogos ? 'eager' : 'lazy'}
/>
</div>
) : (
<>
<Image
src={company.logo}
alt={`${company.name} logo`}
className={`${styles.logo} ${company.logoDark ? 'dark:hidden' : ''}`}
width={120}
height={60}
unoptimized
priority={isFirstFewLogos}
loading={isFirstFewLogos ? 'eager' : 'lazy'}
/>
{company.logoDark && (
<Image
src={company.logoDark}
alt={`${company.name} logo`}
className={`${styles.logo} hidden dark:block`}
width={120}
height={60}
unoptimized
priority={isFirstFewLogos}
loading={isFirstFewLogos ? 'eager' : 'lazy'}
/>
)}
</>
)}
</div>
)
})}
</div>
</div>
</div>
</section>
)
}
})
export default Companies

View File

@@ -4,7 +4,13 @@
position: relative;
width: 100%;
mask-image: linear-gradient(to right, transparent 0%, black 5%, black 95%, transparent 100%);
-webkit-mask-image: linear-gradient(to right, transparent 0%, black 5%, black 95%, transparent 100%);
-webkit-mask-image: linear-gradient(
to right,
transparent 0%,
black 5%,
black 95%,
transparent 100%
);
}
.scrollContent {
@@ -57,7 +63,7 @@
.logo {
filter: grayscale(100%) opacity(0.4) brightness(1.3);
}
.logo:hover {
filter: grayscale(0%) opacity(0.8) brightness(1);
transform: scale(1.05);
@@ -70,12 +76,12 @@
gap: 2.5rem;
animation: scroll var(--scroll-duration-mobile, 35s) linear infinite;
}
.logoItem {
min-width: 120px;
height: 80px;
}
.logo {
max-height: 60px;
max-width: 120px;
@@ -86,12 +92,12 @@
.scrollContent {
gap: 2rem;
}
.logoItem {
min-width: 100px;
height: 70px;
}
.logo {
max-height: 50px;
max-width: 100px;

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.6.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 250 18.86" style="enable-background:new 0 0 250 18.86;" xml:space="preserve"
fill="#FFFFFF">
<g>
<polygon points="156.11,0.34 156.11,2.77 160.85,2.77 160.85,18.51 163.66,18.51 163.66,2.77 168.47,2.77 168.47,0.34 "/>
<path d="M195.6,0.33v10.62c0,2.7,0.11,3.47,0.57,4.53c0.98,2.13,3.3,3.38,6.28,3.38c2.97,0,5.29-1.26,6.28-3.38
c0.46-1.04,0.57-1.83,0.57-4.53V0.33h-2.81v10.56c0,1.94-0.05,2.57-0.3,3.25c-0.49,1.42-1.86,2.26-3.74,2.26
c-1.72,0-3.06-0.71-3.6-1.94c-0.35-0.76-0.44-1.39-0.44-3.58V0.33H195.6z"/>
<path d="M229.81,5.68C229.43,2.04,227.18,0,223.6,0c-1.88,0-3.66,0.68-4.99,1.88c-1.83,1.69-2.78,4.31-2.78,7.59
c0,5.7,3.08,9.39,7.88,9.39c3.82,0,6.05-2.34,6.37-6.64l-2.55-0.18c-0.27,2.89-1.61,4.37-3.9,4.37c-3,0-4.86-2.67-4.86-6.96
c0-4.28,1.86-6.99,4.8-6.99c2.05,0,3.33,1.15,3.77,3.38h0.19L229.81,5.68z"/>
<polygon points="246.13,0.34 240.21,7.63 239.1,7.63 239.1,0.34 236.29,0.34 236.29,18.51 239.1,18.51 239.1,10.06 240,10.06
246.32,18.51 250,18.51 242.34,8.84 249.43,0.34 "/>
<path d="M179.85,2.77c2.43,0,3.02,0.08,3.79,0.46c0.9,0.44,1.42,1.34,1.42,2.48c0,1.33-0.65,2.34-1.77,2.75
c-0.74,0.27-1.34,0.33-3.3,0.33h-1.25V2.77L179.85,2.77z M175.93,18.51h2.81v-7.3h2.81l4.11,7.3h3.32l-4.47-7.76
c2.29-0.93,3.46-2.64,3.46-5.01c0-2.1-1.04-3.84-2.75-4.66c-1.23-0.57-2.37-0.74-5.39-0.74c-1.28,0-2.71-0.03-3.9,0.27V18.51z"/>
<rect x="43.38" y="0.34" width="2.83" height="18.17"/>
<polygon points="84.57,0.34 84.57,18.51 95.1,18.51 95.28,16.09 87.46,16.09 87.46,0.34 "/>
<polygon points="105.82,7.89 105.82,2.77 113.78,2.77 113.6,0.34 103.01,0.34 103.01,18.51 113.82,18.51 113.99,16.09
105.82,16.09 105.82,10.31 110.78,10.31 110.78,7.89 "/>
<path d="M30.22,11.84h-5.15l2.56-8.18L30.22,11.84z M26.29,0.34l-6.21,18.17h2.89l1.36-4.25h6.62l1.34,4.25h2.92L29.01,0.34H26.29z
"/>
<polygon points="56.99,0.35 55.92,18.51 58.5,18.51 59.4,4.17 64.76,18.51 66.21,18.51 71.6,4.17 72.5,18.51 75.08,18.51
74.01,0.35 70.37,0.35 65.49,13.2 60.66,0.35 "/>
<path d="M125.92,2.77c2.43,0,3.02,0.08,3.79,0.46c0.9,0.44,1.42,1.34,1.42,2.48c0,1.34-0.65,2.34-1.77,2.75
c-0.74,0.27-1.33,0.33-3.3,0.33h-1.25V2.77L125.92,2.77z M122,18.51h2.81v-7.3h2.81l4.11,7.3h3.32l-4.47-7.76
c2.29-0.93,3.46-2.64,3.46-5.01c0-2.1-1.03-3.84-2.75-4.66c-1.23-0.57-2.37-0.74-5.39-0.74c-1.28,0-2.71-0.03-3.9,0.27V18.51z"/>
<path d="M2.81,16.09V2.7H3.6c2.97,0,3.73,0.11,4.82,0.79c1.66,1.04,2.53,3.2,2.53,5.9c0,2.7-0.81,4.82-2.34,5.83
c-1.03,0.68-1.96,0.87-4.33,0.87H2.81z M0,0.61v17.9h3.3c3.79,0,5.03-0.21,6.57-1.06c2.52-1.41,4-4.44,4-8.03
c0-3.62-1.57-6.78-4.16-8.16C8.31,0.52,6.76,0.34,3.68,0.34C2.63,0.34,0.97,0.35,0,0.61z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.6.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 250 18.86" style="enable-background:new 0 0 250 18.86;" xml:space="preserve">
<g>
<polygon points="156.11,0.34 156.11,2.77 160.85,2.77 160.85,18.51 163.66,18.51 163.66,2.77 168.47,2.77 168.47,0.34 "/>
<path d="M195.6,0.33v10.62c0,2.7,0.11,3.47,0.57,4.53c0.98,2.13,3.3,3.38,6.28,3.38c2.97,0,5.29-1.26,6.28-3.38
c0.46-1.04,0.57-1.83,0.57-4.53V0.33h-2.81v10.56c0,1.94-0.05,2.57-0.3,3.25c-0.49,1.42-1.86,2.26-3.74,2.26
c-1.72,0-3.06-0.71-3.6-1.94c-0.35-0.76-0.44-1.39-0.44-3.58V0.33H195.6z"/>
<path d="M229.81,5.68C229.43,2.04,227.18,0,223.6,0c-1.88,0-3.66,0.68-4.99,1.88c-1.83,1.69-2.78,4.31-2.78,7.59
c0,5.7,3.08,9.39,7.88,9.39c3.82,0,6.05-2.34,6.37-6.64l-2.55-0.18c-0.27,2.89-1.61,4.37-3.9,4.37c-3,0-4.86-2.67-4.86-6.96
c0-4.28,1.86-6.99,4.8-6.99c2.05,0,3.33,1.15,3.77,3.38h0.19L229.81,5.68z"/>
<polygon points="246.13,0.34 240.21,7.63 239.1,7.63 239.1,0.34 236.29,0.34 236.29,18.51 239.1,18.51 239.1,10.06 240,10.06
246.32,18.51 250,18.51 242.34,8.84 249.43,0.34 "/>
<path d="M179.85,2.77c2.43,0,3.02,0.08,3.79,0.46c0.9,0.44,1.42,1.34,1.42,2.48c0,1.33-0.65,2.34-1.77,2.75
c-0.74,0.27-1.34,0.33-3.3,0.33h-1.25V2.77L179.85,2.77z M175.93,18.51h2.81v-7.3h2.81l4.11,7.3h3.32l-4.47-7.76
c2.29-0.93,3.46-2.64,3.46-5.01c0-2.1-1.04-3.84-2.75-4.66c-1.23-0.57-2.37-0.74-5.39-0.74c-1.28,0-2.71-0.03-3.9,0.27V18.51z"/>
<rect x="43.38" y="0.34" width="2.83" height="18.17"/>
<polygon points="84.57,0.34 84.57,18.51 95.1,18.51 95.28,16.09 87.46,16.09 87.46,0.34 "/>
<polygon points="105.82,7.89 105.82,2.77 113.78,2.77 113.6,0.34 103.01,0.34 103.01,18.51 113.82,18.51 113.99,16.09
105.82,16.09 105.82,10.31 110.78,10.31 110.78,7.89 "/>
<path d="M30.22,11.84h-5.15l2.56-8.18L30.22,11.84z M26.29,0.34l-6.21,18.17h2.89l1.36-4.25h6.62l1.34,4.25h2.92L29.01,0.34H26.29z
"/>
<polygon points="56.99,0.35 55.92,18.51 58.5,18.51 59.4,4.17 64.76,18.51 66.21,18.51 71.6,4.17 72.5,18.51 75.08,18.51
74.01,0.35 70.37,0.35 65.49,13.2 60.66,0.35 "/>
<path d="M125.92,2.77c2.43,0,3.02,0.08,3.79,0.46c0.9,0.44,1.42,1.34,1.42,2.48c0,1.34-0.65,2.34-1.77,2.75
c-0.74,0.27-1.33,0.33-3.3,0.33h-1.25V2.77L125.92,2.77z M122,18.51h2.81v-7.3h2.81l4.11,7.3h3.32l-4.47-7.76
c2.29-0.93,3.46-2.64,3.46-5.01c0-2.1-1.03-3.84-2.75-4.66c-1.23-0.57-2.37-0.74-5.39-0.74c-1.28,0-2.71-0.03-3.9,0.27V18.51z"/>
<path d="M2.81,16.09V2.7H3.6c2.97,0,3.73,0.11,4.82,0.79c1.66,1.04,2.53,3.2,2.53,5.9c0,2.7-0.81,4.82-2.34,5.83
c-1.03,0.68-1.96,0.87-4.33,0.87H2.81z M0,0.61v17.9h3.3c3.79,0,5.03-0.21,6.57-1.06c2.52-1.41,4-4.44,4-8.03
c0-3.62-1.57-6.78-4.16-8.16C8.31,0.52,6.76,0.34,3.68,0.34C2.63,0.34,0.97,0.35,0,0.61z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB