313 lines
12 KiB
PHP
313 lines
12 KiB
PHP
<?php
|
|
|
|
$demoBasePath = rtrim($assetsUriPrefix, '/') . "/images/demo/{$indpic}";
|
|
$demoLocalPath = __DIR__ . "/../images/demo/{$indpic}";
|
|
|
|
$getAssetUrl = function (string $relativePath) use ($demoBasePath, $demoLocalPath): string {
|
|
$fullPath = $demoLocalPath . '/' . ltrim($relativePath, '/');
|
|
$version = file_exists($fullPath) ? filemtime($fullPath) : '1';
|
|
return $demoBasePath . '/' . ltrim($relativePath, '/') . '?v=' . $version;
|
|
};
|
|
|
|
$labelBefore = Yii::t('app', 'До');
|
|
$labelAfter = Yii::t('app', 'После');
|
|
|
|
$sourceMob = $getAssetUrl('1/source_mob.avif');
|
|
$sourceOrig = $getAssetUrl('1/source.avif');
|
|
|
|
$resultMob = $getAssetUrl('1/result_mob.avif');
|
|
$resultOrig = $getAssetUrl('1/result.avif');
|
|
|
|
?>
|
|
|
|
<div id="examples">
|
|
|
|
<div class="image-compare" id="image-compare">
|
|
<img src="<?= $sourceMob ?>"
|
|
srcset="<?= $sourceMob ?> 768w, <?= $sourceOrig ?> 1696w"
|
|
sizes="(max-width: 767.98px) 100vw, 728px"
|
|
id="image-compare-source"
|
|
fetchpriority="high"
|
|
loading="eager"
|
|
width="728"
|
|
height="395"
|
|
alt="<?= htmlspecialchars($labelBefore) ?>">
|
|
|
|
<img src="<?= $resultMob ?>"
|
|
srcset="<?= $resultMob ?> 768w, <?= $resultOrig ?> 1696w"
|
|
sizes="(max-width: 767.98px) 100vw, 728px"
|
|
id="image-compare-result"
|
|
fetchpriority="high"
|
|
loading="eager"
|
|
width="728"
|
|
height="395"
|
|
alt="<?= htmlspecialchars($labelAfter) ?>">
|
|
</div>
|
|
|
|
<div class="image-examples">
|
|
<div class="image-examples-label"><?= Yii::t('app', 'Примеры изображений') ?></div>
|
|
<div class="image-examples-list">
|
|
<?php for ($i = 1; $i <= 5; $i++):
|
|
$thumbMob = $getAssetUrl("{$i}/thumb_mob.avif");
|
|
$thumbOrig = $getAssetUrl("{$i}/thumb.avif");
|
|
?>
|
|
<div class="image-examples-item <?= $i === 1 ? 'active' : '' ?>"
|
|
data-index="<?= $i ?>"
|
|
data-source="<?= $getAssetUrl("{$i}/source.avif") ?>"
|
|
data-result="<?= $getAssetUrl("{$i}/result.avif") ?>"
|
|
data-source-mob="<?= $getAssetUrl("{$i}/source_mob.avif") ?>"
|
|
data-result-mob="<?= $getAssetUrl("{$i}/result_mob.avif") ?>">
|
|
<div class="image-examples-image">
|
|
<?php if ($i === 1): ?>
|
|
<picture>
|
|
<source media="(max-width: 767.98px)" srcset="<?= $thumbMob ?>">
|
|
<img src="<?= $thumbOrig ?>" width="53" height="53" alt="Example 1">
|
|
</picture>
|
|
<?php else: ?>
|
|
<picture class="deferred-picture">
|
|
<source media="(max-width: 767.98px)" data-srcset="<?= $thumbMob ?>">
|
|
<img data-src="<?= $thumbOrig ?>" width="53" height="53" alt="Example <?= $i ?>" class="deferred-thumb">
|
|
</picture>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
<?php endfor; ?>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
const examples = {
|
|
el: document.getElementById('examples'),
|
|
titleEl: document.getElementById('form-title'),
|
|
|
|
hide() {
|
|
window.scrollTo(0, 0);
|
|
window.scrollTo({ top: 0, behavior: 'instant' });
|
|
if (this.el) this.el.style.display = 'none';
|
|
},
|
|
show() {
|
|
if (this.el) this.el.style.display = '';
|
|
},
|
|
setTitle(html) {
|
|
if (this.titleEl) this.titleEl.innerHTML = html;
|
|
}
|
|
};
|
|
|
|
function setupNativeCompare(element, labelBefore, labelAfter) {
|
|
if (!element || element.classList.contains('native-icv') || element.classList.contains('icv')) return null;
|
|
|
|
const sourceImg = element.querySelector('#image-compare-source');
|
|
const resultImg = element.querySelector('#image-compare-result');
|
|
if (!sourceImg || !resultImg) return null;
|
|
|
|
element.classList.add('native-icv');
|
|
|
|
const beforeWrap = document.createElement('div');
|
|
beforeWrap.className = 'icv__before-wrap';
|
|
|
|
resultImg.className = 'icv__after-img';
|
|
|
|
const handle = document.createElement('div');
|
|
handle.className = 'icv__handle';
|
|
const circle = document.createElement('div');
|
|
circle.className = 'icv__handle-circle';
|
|
handle.appendChild(circle);
|
|
|
|
const lblBefore = document.createElement('div');
|
|
lblBefore.className = 'icv__label icv__label-before';
|
|
lblBefore.textContent = labelBefore;
|
|
|
|
const lblAfter = document.createElement('div');
|
|
lblAfter.className = 'icv__label icv__label-after';
|
|
lblAfter.textContent = labelAfter;
|
|
|
|
beforeWrap.appendChild(sourceImg);
|
|
|
|
element.appendChild(resultImg);
|
|
element.appendChild(beforeWrap);
|
|
element.appendChild(handle);
|
|
element.appendChild(lblBefore);
|
|
element.appendChild(lblAfter);
|
|
|
|
function updateSlider(percent) {
|
|
percent = Math.max(0, Math.min(100, percent));
|
|
beforeWrap.style.width = percent + '%';
|
|
handle.style.left = percent + '%';
|
|
sourceImg.style.width = element.clientWidth + 'px';
|
|
}
|
|
|
|
window.addEventListener('resize', () => {
|
|
sourceImg.style.width = element.clientWidth + 'px';
|
|
});
|
|
|
|
let isDragging = false;
|
|
function getPos(e) {
|
|
const rect = element.getBoundingClientRect();
|
|
const clientX = e.touches ? e.touches[0].clientX : e.clientX;
|
|
return ((clientX - rect.left) / rect.width) * 100;
|
|
}
|
|
|
|
function onStart(e) {
|
|
isDragging = true;
|
|
updateSlider(getPos(e));
|
|
}
|
|
|
|
function onMove(e) {
|
|
if (!isDragging) return;
|
|
updateSlider(getPos(e));
|
|
}
|
|
|
|
function onEnd() {
|
|
isDragging = false;
|
|
}
|
|
|
|
element.addEventListener('mousedown', onStart);
|
|
element.addEventListener('touchstart', onStart, { passive: true });
|
|
|
|
window.addEventListener('mousemove', onMove);
|
|
window.addEventListener('touchmove', onMove, { passive: true });
|
|
|
|
window.addEventListener('mouseup', onEnd);
|
|
window.addEventListener('touchend', onEnd);
|
|
|
|
updateSlider(50);
|
|
|
|
return {
|
|
setImages(sourceUrl, resultUrl) {
|
|
sourceImg.removeAttribute('srcset');
|
|
resultImg.removeAttribute('srcset');
|
|
sourceImg.src = sourceUrl;
|
|
resultImg.src = resultUrl;
|
|
}
|
|
};
|
|
}
|
|
|
|
function initCompareViewer() {
|
|
const element = document.getElementById('image-compare');
|
|
const beforeTxt = '<?= addslashes($labelBefore) ?>';
|
|
const afterTxt = '<?= addslashes($labelAfter) ?>';
|
|
|
|
if (element && typeof ImageCompare !== 'undefined') {
|
|
const options = {
|
|
controlColor: "#FFFFFF",
|
|
controlShadow: true,
|
|
addCircle: true,
|
|
addCircleBlur: true,
|
|
showLabels: true,
|
|
labelOptions: { before: beforeTxt, after: afterTxt, onHover: false },
|
|
smoothing: true,
|
|
smoothingAmount: 100,
|
|
hoverStart: false,
|
|
verticalMode: false,
|
|
startingPoint: 50,
|
|
fluidMode: false
|
|
};
|
|
window.icvInstance = new ImageCompare(element, options).mount();
|
|
} else if (element) {
|
|
window.nativeCompareInstance = setupNativeCompare(element, beforeTxt, afterTxt);
|
|
}
|
|
|
|
const exampleItems = document.querySelectorAll('.image-examples-item');
|
|
exampleItems.forEach(item => {
|
|
item.onclick = function() {
|
|
const isMobile = window.innerWidth < 768;
|
|
// На мобильном берем source_mob, на ПК — полный оригинал
|
|
const source = (isMobile && this.getAttribute('data-source-mob'))
|
|
? this.getAttribute('data-source-mob')
|
|
: this.getAttribute('data-source');
|
|
const result = (isMobile && this.getAttribute('data-result-mob'))
|
|
? this.getAttribute('data-result-mob')
|
|
: this.getAttribute('data-result');
|
|
|
|
if (window.icvInstance) {
|
|
window.icvInstance.images.start.element.removeAttribute('srcset');
|
|
window.icvInstance.images.end.element.removeAttribute('srcset');
|
|
window.icvInstance.images.start.element.src = source;
|
|
window.icvInstance.images.end.element.src = result;
|
|
} else if (window.nativeCompareInstance) {
|
|
window.nativeCompareInstance.setImages(source, result);
|
|
} else {
|
|
const sourceEl = document.getElementById('image-compare-source');
|
|
const resultEl = document.getElementById('image-compare-result');
|
|
if (sourceEl) {
|
|
sourceEl.removeAttribute('srcset');
|
|
sourceEl.src = source;
|
|
}
|
|
if (resultEl) {
|
|
resultEl.removeAttribute('srcset');
|
|
resultEl.src = result;
|
|
}
|
|
}
|
|
|
|
exampleItems.forEach(el => el.classList.remove('active'));
|
|
this.classList.add('active');
|
|
};
|
|
});
|
|
}
|
|
|
|
function initStrictImagePipeline() {
|
|
initCompareViewer();
|
|
|
|
const preloadUrl = (url) => {
|
|
return new Promise(resolve => {
|
|
const img = new Image();
|
|
img.onload = img.onerror = resolve;
|
|
img.src = url;
|
|
});
|
|
};
|
|
|
|
const deferredPics = document.querySelectorAll('.deferred-picture');
|
|
const thumbPromises = [];
|
|
|
|
deferredPics.forEach(pic => {
|
|
const source = pic.querySelector('source');
|
|
const img = pic.querySelector('img');
|
|
|
|
if (source && source.getAttribute('data-srcset')) {
|
|
source.srcset = source.getAttribute('data-srcset');
|
|
}
|
|
if (img && img.getAttribute('data-src')) {
|
|
const p = new Promise(resolve => {
|
|
img.onload = img.onerror = resolve;
|
|
img.src = img.getAttribute('data-src');
|
|
});
|
|
thumbPromises.push(p);
|
|
}
|
|
});
|
|
|
|
Promise.all(thumbPromises).then(() => {
|
|
const queue = [2, 3, 4, 5];
|
|
|
|
async function loadSequentially() {
|
|
const isMobile = window.innerWidth < 768;
|
|
for (const num of queue) {
|
|
const item = document.querySelector(`.image-examples-item[data-index="${num}"]`);
|
|
if (!item) continue;
|
|
|
|
const s = (isMobile && item.getAttribute('data-source-mob'))
|
|
? item.getAttribute('data-source-mob')
|
|
: item.getAttribute('data-source');
|
|
const r = (isMobile && item.getAttribute('data-result-mob'))
|
|
? item.getAttribute('data-result-mob')
|
|
: item.getAttribute('data-result');
|
|
|
|
await Promise.all([preloadUrl(s), preloadUrl(r)]);
|
|
}
|
|
}
|
|
|
|
if ('requestIdleCallback' in window) {
|
|
requestIdleCallback(() => loadSequentially());
|
|
} else {
|
|
setTimeout(loadSequentially, 150);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', initStrictImagePipeline);
|
|
} else {
|
|
initStrictImagePipeline();
|
|
}
|
|
</script>
|