Newer
Older
<picture>
<!-- Serve WebP images -->
<source
type="image/webp"
srcset="
@/assets/images/responsive_images/hero_no-faces_5-320.webp 320w,
@/assets/images/responsive_images/hero_no-faces_5-640.webp 640w,
@/assets/images/responsive_images/hero_no-faces_5-1280.webp 1280w,
@/assets/images/responsive_images/hero_no-faces_5-1920.webp 1920w"
sizes="(max-width: 600px) 320px,
(max-width: 1200px) 640px,
(min-width: 1201px) 1280px,
1920px"
/>
<!-- Fallback to JPG images for browsers that do not support WebP -->
<img
ref="heroImage"
id="title-image"
:class="{ mobile: mobileView }"
srcset="
@/assets/images/responsive_images/hero_no-faces_5-320.jpg 320w,
@/assets/images/responsive_images/hero_no-faces_5-640.jpg 640w,
@/assets/images/responsive_images/hero_no-faces_5-1280.jpg 1280w,
@/assets/images/responsive_images/hero_no-faces_5-1920.jpg 1920w"
sizes="(max-width: 600px) 320px,
(max-width: 1200px) 640px,
(min-width: 1201px) 1280px,
1920px"
src="@/assets/images/responsive_images/hero_no-faces_5-1280.jpg"
alt="Two contrasting images of water sources with the left image shows a close-up of a hand holding a clear glass being filled with water from a kitchen faucet. The right image shows a rustic landscape with a blue well tank labeled WELL-X-TROL with a wired fence and dry grass in the background."
<img
ref="bwHeroImage"
id="bw-title-image"
srcset="
@/assets/images/responsive_images/hero_no-faces_5-320.jpg 320w,
@/assets/images/responsive_images/hero_no-faces_5-640.jpg 640w,
@/assets/images/responsive_images/hero_no-faces_5-1280.jpg 1280w,
@/assets/images/responsive_images/hero_no-faces_5-1920.jpg 1920w"
sizes="(max-width: 600px) 320px,
(max-width: 1200px) 640px,
(min-width: 1201px) 1280px,
1920px"
src="@/assets/images/responsive_images/hero_no-faces_5-1280.jpg"
alt="Two contrasting grayscale images of water sources with the left image shows a close-up of a hand holding a glass being filled with water from a kitchen faucet. The right image shows a rustic landscape with a well tank labeled WELL-X-TROL with a wired fence and dry grass in the background."
<picture>
<!-- Serve WebP images -->
<source
type="image/webp"
srcset="
@/assets/images/responsive_images/hero_no-faces_5-320.webp 320w,
@/assets/images/responsive_images/hero_no-faces_5-640.webp 640w,
@/assets/images/responsive_images/hero_no-faces_5-1280.webp 1280w,
@/assets/images/responsive_images/hero_no-faces_5-1920.webp 1920w"
sizes="(max-width: 600px) 320px,
(max-width: 1200px) 640px,
(min-width: 1201px) 1280px,
1920px"
/>
<!-- Fallback to JPG images for browsers that do not support WebP -->
<img
ref="bwHeroImage"
id="bw-title-image"
rel="preload"
:class="{ mobile: mobileView }"
srcset="
@/assets/images/responsive_images/hero_no-faces_5-320.jpg 320w,
@/assets/images/responsive_images/hero_no-faces_5-640.jpg 640w,
@/assets/images/responsive_images/hero_no-faces_5-1280.jpg 1280w,
@/assets/images/responsive_images/hero_no-faces_5-1920.jpg 1920w"
sizes="(max-width: 600px) 320px,
(max-width: 1200px) 640px,
(min-width: 1201px) 1280px,
1920px"
src="@/assets/images/responsive_images/hero_no-faces_5-1280.jpg"
<!-- Using SVG masks with a circle shape to reveal color image where circles are on top of bw -->
<svg ref="overlay" id="overlay" xmlns="http://www.w3.org/2000/svg">
<defs>
<mask id="circleMask">
<rect width="100%" height="100%" fill="white" />
<!-- Circles will be added here via D3.js -->
<rect width="100%" height="100%" fill="transparent" mask="url(#circleMask)" />
<h1>{{ t('text.pageTitle') }}</h1>
<h3>{{ t('text.pageSubtitle') }}</h3>
<div class="text-container" id="lang-button">
<LanguageButton />
</div>
import { ref, onMounted, nextTick } from 'vue';
import LanguageButton from '@/components/LanguageButton.vue';
const overlay = ref(null);
const updateSvgDimensions = () => {
if (heroImage.value && bwHeroImage.value && overlay.value) {
const { width, height } = heroImage.value.getBoundingClientRect();
overlay.value.setAttribute('width', width);
overlay.value.setAttribute('height', height);
bwHeroImage.value.style.width = `${width}px`;
bwHeroImage.value.style.height = `${height}px`;
bwHeroImage.value.style.top = '0';
bwHeroImage.value.style.left = '0';
const svg = d3.select(overlay.value);
const mask = svg.select('#circleMask');
const svgWidth = overlay.value.clientWidth;
const svgHeight = overlay.value.clientHeight;
if (svgWidth === 0 || svgHeight === 0) {
console.error('SVG dimensions are not set correctly.');
return;
}
mask.selectAll('circle').remove(); // Clear existing circles
const numberOfCircles = 40; // Number of circles to add
const circlesData = Array.from({ length: numberOfCircles }).map(() => ({
cx: Math.random() * svgWidth,
cy: Math.random() * svgHeight,
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
}));
const circles = mask.selectAll('circle')
.data(circlesData)
.enter()
.append('circle')
.attr('cx', d => d.cx)
.attr('cy', d => d.cy)
.attr('r', d => d.r)
.attr('fill', 'black');
function animateCircles() {
circles.transition()
.duration(1000 + Math.random() * 1000)
.ease(d3.easeSin)
.attr('cy', d => d.cy + (Math.random() - 0.5) * 100) // Larger random range for more movement
.attr('cx', d => d.cx + (Math.random() - 0.5) * 350)
.on('end', function() {
d3.select(this)
.transition()
.duration(2000 + Math.random() * 3000)
.ease(d3.easeCubic)
.attr('cy', d => d.cy + (Math.random() - 0.5) * 200)
.attr('cx', d => d.cx + (Math.random() - 0.5) * 300)
.on('end', animateCircles); // Recursive call for continuous animation
});
nextTick(() => {
updateSvgDimensions();
addRandomCircles();
});
const debounceResize = debounce(() => {
}, 200);
window.addEventListener('resize', debounceResize);
if (heroImage.value.complete) {
onImageLoad();
} else {
heroImage.value.addEventListener('load', onImageLoad);
}
function debounce(func, wait) {
let timeout;
return function (...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
<style lang="scss" scoped>
#grid-container {
display: grid;
#image-container {
position: relative;
width: 100%;
}
#title-image,
#bw-title-image {
width: 100%;
#bw-title-image {
filter: grayscale(100%);
position: absolute;
top: 0;
left: 0;
pointer-events: none; /* Ensure the grayscale image doesn't interfere with other interactions */
mask: url(#circleMask); /* Apply the SVG mask */
#overlay {
position: absolute;
top: 0;
left: 0;
pointer-events: none; /* Ensure the SVG overlay doesn't interfere with other interactions */
}
background: var(--color-background-header-footer);
color: var(--color-text);
color: var(--color-text);
background: var(--color-background-header-footer);
h1 {
margin: -20px 20px;
color: white;
text-align: left; /* Optional: Align text to the left */
}
text-align: left; /* Optional: Align text to the left */
svg {
position: absolute;
top: 0;
left: 0;
width: 100%;
#lang-button {
display: flex;
justify-content: flex-end;
width: 100%;
margin-bottom: 0;
padding: 10px;
box-sizing: border-box;
}