first commit

This commit is contained in:
root committed 2026-10-03 15:41:23 +00:00
commit 792676e974
169 files changed
+6195

No files matched your search

+313
View File
@@ -0,0 +1,313 @@
<?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>
+262
View File
@@ -0,0 +1,262 @@
<?php
$maxSide = 6000;
$errorSizeMessage = Yii::t('app', 'Изображение слишком большое ({width}x{height}px). Максимальный размер — {maxSide}px по большей стороне.', [
'maxSide' => $maxSide,
]);
$errorMessage = Yii::t('app', 'Возникла ошибка');
$utmJson = json_encode($UTM ?? [], JSON_UNESCAPED_UNICODE);
$createApiUrl = ($apiHost ?? '') . '/api/images/public/create';
$redirectUrlBase = $redirect_url_base ?? '';
$redirectUrlUtm = $redirect_url_utm ?? '';
?>
<form action="<?= htmlspecialchars($createApiUrl, ENT_QUOTES, 'UTF-8') ?>" class="dropzone-form gradient-dashed-border" id="dropzone-form" method="POST" style="background: transparent; border: unset; display: <?= ($show ?? true) ? '' : 'none'; ?>; cursor: pointer;">
<div class="dropzone-form-inner">
<div class="dropzone-form-icon">
<img src="<?= $assetsUriPrefix; ?>/images/dropzone-icon.svg" alt="" height="64" width="64">
</div>
<div id="form-title" class="dropzone-form-text"><?= Yii::t('app', 'Загрузите изображение') ?> <br class="d-md-none"><?= Yii::t('app', 'для улучшения') ?></div>
<div class="dropzone-form-format">
<div class="dropzone-form-format-label"><?= Yii::t('app', 'Поддерживаемые форматы') ?>:</div>
<div class="dropzone-form-format-list">
<div class="dropzone-form-format-item">png</div>
<div class="dropzone-form-format-item">jpeg</div>
<div class="dropzone-form-format-item">jpg</div>
<div class="dropzone-form-format-item">webp</div>
<div class="dropzone-form-format-item">heic</div>
</div>
</div>
<div class="dropzone-form-action">
<button type="button" class="dropzone-form-button">
<span><?= Yii::t('app', 'Загрузить изображение') ?></span>
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.3333 14.6667C15.6345 14.6667 17.5 12.8012 17.5 10.5C17.5 8.43079 15.9917 6.71387 14.0145 6.38874C14.0113 6.38821 14.0086 6.38583 14.0078 6.38267C13.4802 4.43373 11.6992 3 9.58333 3C7.05203 3 5 5.05203 5 7.58333C5 7.75477 5.00941 7.924 5.02775 8.09055C5.0282 8.0947 5.0255 8.09856 5.02145 8.09957C3.57289 8.4621 2.5 9.77242 2.5 11.3333C2.5 13.1743 3.99238 14.6667 5.83333 14.6667H6.66667M10 8.83726V18M10 8.83726L7.5 11.3333M10 8.83726L12.5 11.3333" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
</div>
</div>
<input type="file" id="dropzone-file-input" name="file" accept="image/jpeg,image/png,image/jpg,image/webp,image/heic,.heic" style="display: none;">
</form>
<script>
const form = {
el: document.getElementById('dropzone-form'),
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;
}
};
const utmData = <?= $utmJson ?>;
const redirectUrlBase = <?= json_encode($redirectUrlBase, JSON_UNESCAPED_UNICODE) ?>;
const redirectUrlUtm = <?= json_encode($redirectUrlUtm, JSON_UNESCAPED_UNICODE) ?>;
const createApiUrl = <?= json_encode($createApiUrl, JSON_UNESCAPED_UNICODE) ?>;
const errorSizeTpl = <?= json_encode($errorSizeMessage, JSON_UNESCAPED_UNICODE) ?>;
const genericErrorMsg = <?= json_encode($errorMessage, JSON_UNESCAPED_UNICODE) ?>;
(function initNativeUploader() {
const formEl = document.getElementById('dropzone-form');
const fileInput = document.getElementById('dropzone-file-input');
if (!formEl || !fileInput) return;
let columnsSwapped = false;
formEl.addEventListener('click', (e) => {
if (e.target !== fileInput) {
fileInput.click();
}
});
['dragenter', 'dragover'].forEach(eventName => {
formEl.addEventListener(eventName, (e) => {
e.preventDefault();
e.stopPropagation();
formEl.classList.add('dragover');
});
});
['dragleave', 'dragend', 'drop'].forEach(eventName => {
formEl.addEventListener(eventName, (e) => {
e.preventDefault();
e.stopPropagation();
formEl.classList.remove('dragover');
});
});
formEl.addEventListener('drop', (e) => {
const files = e.dataTransfer?.files;
if (files && files.length > 0) {
handleSelectedFile(files[0]);
}
});
fileInput.addEventListener('change', () => {
if (fileInput.files && fileInput.files.length > 0) {
handleSelectedFile(fileInput.files[0]);
}
});
function swapColumns() {
if (columnsSwapped) return;
const col5 = document.querySelector('.col-md-5');
const col7 = document.querySelector('.col-md-7');
if (col5 && col7) {
const temp = document.createElement('div');
col5.replaceWith(temp);
col7.replaceWith(col5);
temp.replaceWith(col7);
columnsSwapped = true;
}
}
function handleSelectedFile(file) {
const maxSide = 6000;
const objectUrl = URL.createObjectURL(file);
const img = new Image();
img.onload = () => {
const width = img.naturalWidth;
const height = img.naturalHeight;
if (width > maxSide || height > maxSide) {
URL.revokeObjectURL(objectUrl);
fileInput.value = '';
const msg = errorSizeTpl
.replace('{width}', width)
.replace('{height}', height);
form.show();
if (typeof preview !== 'undefined' && preview) {
const col7 = document.querySelector('.col-md-7');
if (col7) col7.appendChild(preview.el);
preview.show();
preview.showError(msg);
}
if (typeof process !== 'undefined' && process) process.hide();
if (typeof examples !== 'undefined' && examples) examples.hide();
return;
}
form.hide();
swapColumns();
if (typeof preview !== 'undefined' && preview) {
const col7 = document.querySelector('.col-md-7');
if (col7) col7.appendChild(preview.el);
preview.hide();
preview.setSource(objectUrl);
preview.show();
}
if (typeof process !== 'undefined' && process) {
const col5 = document.querySelector('.col-md-5');
if (col5) col5.appendChild(process.el);
process.setFilename(file.name);
process.setSize(file.size);
process.setDimensions(width, height);
process.setStage(0);
process.setStatus(0);
process.show();
}
if (typeof examples !== 'undefined' && examples) {
examples.hide();
}
uploadFile(file);
};
img.onerror = () => {
URL.revokeObjectURL(objectUrl);
if (typeof preview !== 'undefined' && preview) {
preview.show();
preview.showError(genericErrorMsg);
}
};
img.src = objectUrl;
}
function uploadFile(file) {
const xhr = new XMLHttpRequest();
const formData = new FormData();
formData.append('file', file);
const actionUrl = createApiUrl || formEl.getAttribute('action') || '/api/images/public/create';
xhr.open('POST', actionUrl, true);
xhr.withCredentials = true;
xhr.setRequestHeader('Accept', 'application/json');
xhr.upload.onprogress = (e) => {
if (e.lengthComputable && typeof process !== 'undefined' && process) {
const progress = Math.round((e.loaded / e.total) * 100);
process.setStatus(progress);
}
};
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
let response;
try {
response = JSON.parse(xhr.responseText);
} catch (e) {
showUploadError(genericErrorMsg);
return;
}
const imageId = response?.id || response?.data?.id || response?.image?.id || response?.image_id;
if (!imageId) {
showUploadError(genericErrorMsg);
return;
}
if (typeof process !== 'undefined' && process) {
process.setStatus(100);
process.setStage(1);
}
// Build redirect target URL: $redirect_url_base . $imageId . ($redirect_url_utm ? '&' . $redirect_url_utm : '')
let targetUrl = redirectUrlBase + encodeURIComponent(imageId);
if (redirectUrlUtm) {
// const sep = targetUrl.includes('?') ? '&' : '?';
targetUrl += '&' + redirectUrlUtm;
}
window.location.href = targetUrl;
} else {
showUploadError(genericErrorMsg);
}
};
xhr.onerror = () => {
showUploadError(genericErrorMsg);
};
xhr.send(formData);
}
function showUploadError(msg) {
if (typeof process !== 'undefined' && process) process.hide();
form.show();
if (typeof preview !== 'undefined' && preview) preview.showError(msg);
fileInput.value = '';
}
})();
</script>
+99
View File
@@ -0,0 +1,99 @@
<style>
.image-loading {
display: flex;
overflow: hidden;
width: 100%;
border-radius: 24px;
height: 100%;
place-items: center;
justify-content: center;
}
.image-loading-progress::before {
background-image: var(--source);
filter: blur(15px) brightness(0.7);
background-size: cover;
background-position: center;
transform: scale(1.1);
}
.image-loading-progress #source {
/*height: 100%;*/
z-index: 3;
position: absolute;
filter: brightness(0.7);
object-fit: contain;
}
.image-loading-error {
display: none;
}
.image-loading-error.show {
display: block;
}
.image-loading-progress:has(.image-loading-error.show) .image-loading-icon {
opacity: 0;
}
.image-loading-progress:has(.image-loading-error.show) #source {
filter: brightness(0.4);
}
.image-loading-progress:has(.image-loading-error.show):before {
filter: blur(15px) brightness(0.4);
}
@media (max-width: 768px) {
.image-loading-progress {
height: 35dvh !important;
}
.image-loading-progress #source {
position: static;
}
}
</style>
<div id="preview" class="image-loading image-loading-progress" style="--source: url('<?= $source ?? '' ?>'); display: <?= ($show ?? true) ? '' : 'none'; ?>" >
<img id="source" src="<?= $source ?? '' ?>" style="height: 100%; width: 100%; object-fit: contain; flex-shrink: 0;">
<div class="image-loading-icon">
<img class="sandglass" src="<?= $assetsUriPrefix ?>/images/sandglass.svg" alt="">
<img class="spinner" src="<?= $assetsUriPrefix ?>/images/spinner.svg" alt="">
</div>
<div id="upscale-error-overlay" class="image-loading-error">
<div class="image-loading-error-icon">
<img src="<?= $assetsUriPrefix ?>/images/confused.svg" alt="">
</div>
<div class="image-loading-error-title"><?= Yii::t('app', 'Извините, что-то пошло не так') ?></div>
<div id="upscale-error-message" class="image-loading-error-text"><?= Yii::t('app', 'Максимальный допустимый размер файла 20 Мб') ?></div>
</div>
</div>
<script>
const preview = {
el: document.getElementById('preview'),
upscaleErrorOverlay: document.getElementById('upscale-error-overlay'),
upscaleErrorMessage: document.getElementById('upscale-error-message'),
sourceEl: document.getElementById('source'),
hide() {
this.el.style.display = 'none';
},
show() {
this.el.style.display = '';
this.upscaleErrorOverlay.classList.remove('show');
},
setSource(url) {
this.sourceEl.src = url;
this.el.style.setProperty('--source', "url('" + url + "')");
},
setDimensions(w, h) {
console.log('setSize', w, h);
},
setStage(stage) {
},
showError(message) {
this.upscaleErrorMessage.innerText = message || 'Ошибка обработки запроса.';
this.upscaleErrorOverlay.classList.add('show');
},
};
</script>
+140
View File
@@ -0,0 +1,140 @@
<?php
$labels = [
'stage0' => \Yii::t('app', 'Загружаем файл на сервер'),
'stage1' => \Yii::t('app', 'Улучшаем и увеличиваем'),
'stage2' => \Yii::t('app', 'Загружаем полученный результат'),
];
$labelsJson = json_encode($labels, JSON_UNESCAPED_UNICODE);
?>
<div id="process" class="dropzone-form dropzone-form-loading gradient-dashed-border" style="display: <?= ($show ?? true) ? '' : 'none'; ?>">
<div class="dropzone-form-inner">
<div id="process-title" class="dropzone-form-text"><?= \Yii::t('app', 'Загружаем файл на сервер') ?></div>
<div id="process-filename" class="dropzone-form-filename"><?= htmlspecialchars($filename ?? '', ENT_QUOTES, 'UTF-8') ?></div>
<div class="dropzone-form-loading-info">
<div class="dropzone-form-loading-info-col">
<div class="dropzone-form-loading-info-item">
<div class="dropzone-form-loading-info-icon">
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 4.5V6.5H4M6 20.5V18.5H4M18 4.5V6.5H20M18 20.5V18.5H20M7 7.5H17V17.5H7V7.5Z" stroke="currentColor" stroke-linecap="square"/>
</svg>
</div>
<div class="dropzone-form-loading-info-text">
<span id="origin_width"><?= htmlspecialchars($origin_width ?? '', ENT_QUOTES, 'UTF-8') ?></span> x <span id="origin_height"><?= htmlspecialchars($origin_height ?? '', ENT_QUOTES, 'UTF-8') ?></span>&nbsp;<span>px</span></div>
</div>
<div class="dropzone-form-loading-info-item">
<div class="dropzone-form-loading-info-icon">
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 17.5C18.7614 17.5 21 15.2614 21 12.5C21 10.017 19.19 7.95665 16.8174 7.56649C16.8136 7.56585 16.8104 7.56299 16.8094 7.5592C16.1762 5.22047 14.039 3.5 11.5 3.5C8.46243 3.5 6 5.96243 6 9C6 9.20572 6.01129 9.4088 6.0333 9.60866C6.03385 9.61364 6.0306 9.61827 6.02574 9.61949C4.28747 10.0545 3 11.6269 3 13.5C3 15.7091 4.79086 17.5 7 17.5H8M12 10.5047V21.5M12 10.5047L9 13.5M12 10.5047L15 13.5" stroke="currentColor" stroke-linecap="square"/>
</svg>
</div>
<div class="dropzone-form-loading-info-text">
<span id="origin_size"><?= htmlspecialchars($origin_size ?? '', ENT_QUOTES, 'UTF-8') ?></span></div>
</div>
</div>
<div class="dropzone-form-loading-info-col dropzone-form-loading-info-arrow">
<svg width="37" height="24" viewBox="0 0 37 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 17L12 12L7 7M13 17L18 12L13 7" stroke="currentColor" stroke-linecap="square"/>
<path d="M20 17L25 12L20 7M26 17L31 12L26 7" stroke="currentColor" stroke-linecap="square"/>
</svg>
</div>
<div class="dropzone-form-loading-info-col">
<div class="dropzone-form-loading-info-item">
<div class="dropzone-form-loading-info-icon">
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 4.5V6.5H4M6 20.5V18.5H4M18 4.5V6.5H20M18 20.5V18.5H20M7 7.5H17V17.5H7V7.5Z" stroke="currentColor" stroke-linecap="square"/>
</svg>
</div>
<div class="dropzone-form-loading-info-text">
<span id="upscale_width"><?= htmlspecialchars($upscale_width ?? '', ENT_QUOTES, 'UTF-8') ?></span> x <span id="upscale_height"><?= htmlspecialchars($upscale_height ?? '', ENT_QUOTES, 'UTF-8') ?></span>&nbsp;<span>px</span></div>
</div>
<div class="dropzone-form-loading-info-item">
<div class="dropzone-form-loading-info-icon">
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 17.5C18.7614 17.5 21 15.2614 21 12.5C21 10.017 19.19 7.95665 16.8174 7.56649C16.8136 7.56585 16.8104 7.56299 16.8094 7.5592C16.1762 5.22047 14.039 3.5 11.5 3.5C8.46243 3.5 6 5.96243 6 9C6 9.20572 6.01129 9.4088 6.0333 9.60866C6.03385 9.61364 6.0306 9.61827 6.02574 9.61949C4.28747 10.0545 3 11.6269 3 13.5C3 15.7091 4.79086 17.5 7 17.5H8M12 10.5047V21.5M12 10.5047L9 13.5M12 10.5047L15 13.5" stroke="currentColor" stroke-linecap="square"/>
</svg>
</div>
<div class="dropzone-form-loading-info-text">
≈ <span id="upscale_size"><?= htmlspecialchars($upscale_size ?? '', ENT_QUOTES, 'UTF-8') ?></span></div>
</div>
</div>
</div>
<div class="dropzone-form-loading-progress">
<div class="progress">
<div class="progress-label">0%</div>
<div class="progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
<div class="dropzone-form-note"><?= Yii::t('app', 'Увеличение 4х доступно после регистрации') ?></div>
</div>
</div>
<script>
const labels = <?= $labelsJson ?>;
const process = {
el: document.getElementById('process'),
barEl: document.querySelector('.progress-bar'),
labelEl: document.querySelector('.progress-label'),
titleEl: document.getElementById('process-title'),
filenameEl: document.getElementById('process-filename'),
originSizeEl: document.getElementById('origin_size'),
upscaleSizeEl: document.getElementById('upscale_size'),
originWidthEl: document.getElementById('origin_width'),
originHeightEl: document.getElementById('origin_height'),
upscaleWidthEl: document.getElementById('upscale_width'),
upscaleHeightEl: document.getElementById('upscale_height'),
status: {
current: 0,
stage: 0,
},
hide() {
if (this.el) this.el.style.display = 'none';
},
show() {
if (this.el) this.el.style.display = '';
},
setFilename(filename) {
if (this.filenameEl) this.filenameEl.innerText = filename;
},
setSize(size) {
if (this.originSizeEl) this.originSizeEl.innerText = this.formatB(size);
if (this.upscaleSizeEl) this.upscaleSizeEl.innerText = this.formatB(size * 3);
},
setDimensions(w, h) {
if (this.originWidthEl) this.originWidthEl.innerText = w;
if (this.originHeightEl) this.originHeightEl.innerText = h;
if (this.upscaleWidthEl) this.upscaleWidthEl.innerText = w * 2;
if (this.upscaleHeightEl) this.upscaleHeightEl.innerText = h * 2;
},
setStage(stage) {
if (!this.titleEl) return;
this.status.stage = stage;
this.titleEl.innerText = labels['stage' + stage] || '...';
},
setStatus(status) {
const val = Math.min(100, Math.max(0, status));
this.status.current = val;
if (this.barEl) this.barEl.style.width = val + '%';
if (this.labelEl) this.labelEl.innerText = val + '%';
},
formatB(bytes) {
if (bytes === 0 || isNaN(bytes)) return '0 B';
const units = ['B', 'Kb', 'Mb', 'Gb', 'Tb'];
const i = Math.floor(Math.log(bytes) / Math.log(1024));
const formattedValue = (bytes / Math.pow(1024, i)).toFixed(2);
return parseFloat(formattedValue) + ' ' + units[i];
}
};
</script>