pharma-see / components /image-optimizer.js
steake's picture
improve prompt engineering for images
ca7232a verified
/**
* Image Optimizer Web Component
* Automatically optimizes image loading with lazy loading, placeholders, and responsive images
*/
class ImageOptimizer extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
const src = this.getAttribute('src') || '';
const alt = this.getAttribute('alt') || '';
const width = this.getAttribute('width') || '100%';
const height = this.getAttribute('height') || 'auto';
const lazy = this.hasAttribute('lazy');
const placeholder = this.getAttribute('placeholder') || 'https://static.photos/abstract/100x100';
const category = this.getAttribute('category') || 'general';
// Generate optimized image URL based on static.photos API
const optimizeURL = (url) => {
// If it's already a static.photos URL, keep it
if (url.includes('static.photos')) return url;
// For other URLs, we could add resize parameters if needed
// For now, just return the original
return url;
};
// Create responsive image sets for different screen sizes
const generateSrcSet = (baseUrl) => {
if (!baseUrl.includes('static.photos')) return '';
// Extract dimensions if present
const dimMatch = baseUrl.match(/\/(\d+)x(\d+)\//);
if (!dimMatch) return '';
const [_, width, height] = dimMatch;
const base = baseUrl.replace(`/${width}x${height}/`, '');
// Generate srcset for common breakpoints
const sizes = [
{ width: 320, height: Math.round(320 * height / width) },
{ width: 640, height: Math.round(640 * height / width) },
{ width: 768, height: Math.round(768 * height / width) },
{ width: 1024, height: Math.round(1024 * height / width) },
{ width: 1280, height: Math.round(1280 * height / width) }
];
return sizes.map(size =>
`${base.replace(/\/\d+x\d+\//, `/${size.width}x${size.height}/`)} ${size.width}w`
).join(', ');
};
const optimizedSrc = optimizeURL(src);
const srcset = generateSrcSet(optimizedSrc);
this.shadowRoot.innerHTML = `
<style>
:host {
display: block;
position: relative;
width: ${width};
height: ${height};
overflow: hidden;
}
.image-container {
position: relative;
width: 100%;
height: 100%;
}
.optimized-image {
width: 100%;
height: 100%;
object-fit: cover;
transition: opacity 0.3s ease;
opacity: 0;
}
.optimized-image.loaded {
opacity: 1;
}
.placeholder {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
border-radius: inherit;
}
@keyframes loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
.error-fallback {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #f8f9fa;
display: flex;
align-items: center;
justify-content: center;
color: #6c757d;
font-size: 14px;
border-radius: inherit;
}
.error-fallback i {
font-size: 24px;
margin-bottom: 8px;
}
</style>
<div class="image-container">
<div class="placeholder"></div>
<img
class="optimized-image"
src="${optimizedSrc}"
${srcset ? `srcset="${srcset}"` : ''}
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
alt="${alt}"
${lazy ? 'loading="lazy"' : ''}
onload="this.classList.add('loaded')"
onerror="this.style.display='none'; this.parentNode.querySelector('.error-fallback').style.display='flex';"
>
<div class="error-fallback" style="display: none;">
<div style="text-align: center;">
<i class="fas fa-image"></i>
<div>${alt || 'Image'}</div>
</div>
</div>
</div>
`;
}
static get observedAttributes() {
return ['src', 'alt', 'width', 'height'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
this.connectedCallback();
}
}
}
customElements.define('optimized-image', ImageOptimizer);