fix: единая загрузка примеров и стабильный первый кадр (api#301)
deploy-preland / deploy zoomstage.online (push) Successful in 33s
deploy-preland / deploy zoomstage.online (push) Successful in 33s
This commit is contained in:
1 parent
75ccf14889
commit
0c9a406d68
10 files changed
+347
-257
No files matched your search
+31
-220
@@ -1,101 +1,39 @@
|
||||
<?php
|
||||
|
||||
$demoBasePath = rtrim($assetsUriPrefix, '/') . "/{$indpic}";
|
||||
$demoLocalPath = __DIR__ . "/../{$indpic}";
|
||||
|
||||
$getAssetUrl = function (int $number, string $device, string $role) use ($demoBasePath, $demoLocalPath): string {
|
||||
$relativePath = sprintf('%02d', $number) . "_{$device}_{$role}.avif";
|
||||
$fullPath = $demoLocalPath . '/' . ltrim($relativePath, '/');
|
||||
$relativePath = sprintf('%02d', $number) . "_{$device}_{$role}.avif";
|
||||
$fullPath = $demoLocalPath . '/' . $relativePath;
|
||||
$version = file_exists($fullPath) ? filemtime($fullPath) : '1';
|
||||
return $demoBasePath . '/' . ltrim($relativePath, '/') . '?v=' . $version;
|
||||
return $demoBasePath . '/' . $relativePath . '?v=' . $version;
|
||||
};
|
||||
|
||||
$labelBefore = Yii::t('app', 'До');
|
||||
$labelAfter = Yii::t('app', 'После');
|
||||
|
||||
$sourceMob = $getAssetUrl(1, 'mob', '1-source');
|
||||
$sourceOrig = $getAssetUrl(1, 'desk', '1-source');
|
||||
|
||||
$resultMob = $getAssetUrl(1, 'mob', '2-result');
|
||||
$resultOrig = $getAssetUrl(1, 'desk', '2-result');
|
||||
|
||||
$labelAfter = Yii::t('app', 'После');
|
||||
?>
|
||||
|
||||
<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, 'mob', '3-thumb');
|
||||
$thumbOrig = $getAssetUrl($i, 'desk', '3-thumb');
|
||||
?>
|
||||
<div class="image-examples-item <?= $i === 1 ? 'active' : '' ?>"
|
||||
data-index="<?= $i ?>"
|
||||
data-source="<?= $getAssetUrl($i, 'desk', '1-source') ?>"
|
||||
data-result="<?= $getAssetUrl($i, 'desk', '2-result') ?>"
|
||||
data-source-mob="<?= $getAssetUrl($i, 'mob', '1-source') ?>"
|
||||
data-result-mob="<?= $getAssetUrl($i, 'mob', '2-result') ?>">
|
||||
<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 id="examples" data-examples-state="loading" aria-busy="true">
|
||||
<div data-examples-visual style="visibility:hidden" aria-hidden="true" inert>
|
||||
<div class="image-compare" id="image-compare">
|
||||
<img id="image-compare-source" width="728" height="395" alt="<?= htmlspecialchars($labelBefore) ?>">
|
||||
<img id="image-compare-result" 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++): ?>
|
||||
<div class="image-examples-item <?= $i===1 ? 'active' : '' ?>" data-example="<?= $i ?>" data-index="<?= $i ?>"
|
||||
data-source="<?= $getAssetUrl($i, 'desk', '1-source') ?>" data-result="<?= $getAssetUrl($i, 'desk', '2-result') ?>"
|
||||
data-source-mob="<?= $getAssetUrl($i, 'mob', '1-source') ?>" data-result-mob="<?= $getAssetUrl($i, 'mob', '2-result') ?>"
|
||||
data-thumb="<?= $getAssetUrl($i, 'desk', '3-thumb') ?>" data-thumb-mob="<?= $getAssetUrl($i, 'mob', '3-thumb') ?>" aria-disabled="true" tabindex="-1">
|
||||
<div class="image-examples-image"><picture><img width="100" height="100" alt="Example <?= $i ?>"></picture></div>
|
||||
</div>
|
||||
<?php endfor; ?>
|
||||
<?php endfor; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn button-outline examples-retry" data-examples-retry hidden><?= Yii::t('app', 'Повторить') ?></button>
|
||||
</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;
|
||||
}
|
||||
};
|
||||
|
||||
<?= require __DIR__ . '/example-loader.php' ?>
|
||||
function setupNativeCompare(element, labelBefore, labelAfter) {
|
||||
if (!element || element.classList.contains('native-icv') || element.classList.contains('icv')) return null;
|
||||
|
||||
@@ -132,15 +70,18 @@ $resultOrig = $getAssetUrl(1, 'desk', '2-result');
|
||||
element.appendChild(lblBefore);
|
||||
element.appendChild(lblAfter);
|
||||
|
||||
let sliderPercent = 50;
|
||||
element.addEventListener('examples:images', () => updateSlider(sliderPercent));
|
||||
function updateSlider(percent) {
|
||||
sliderPercent = percent;
|
||||
percent = Math.max(0, Math.min(100, percent));
|
||||
beforeWrap.style.width = percent + '%';
|
||||
handle.style.left = percent + '%';
|
||||
sourceImg.style.width = element.clientWidth + 'px';
|
||||
element.querySelector('#image-compare-source').style.width = element.clientWidth + 'px';
|
||||
}
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
sourceImg.style.width = element.clientWidth + 'px';
|
||||
element.querySelector('#image-compare-source').style.width = element.clientWidth + 'px';
|
||||
});
|
||||
|
||||
let isDragging = false;
|
||||
@@ -175,140 +116,10 @@ $resultOrig = $getAssetUrl(1, 'desk', '2-result');
|
||||
|
||||
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();
|
||||
}
|
||||
window.ZoomzillaExamples.attach(element => {
|
||||
setupNativeCompare(element, <?= json_encode($labelBefore, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>, <?= json_encode($labelAfter, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>);
|
||||
});
|
||||
</script>
|
||||
Reference in new issue
Block a user